diff --git a/CSharpDriver.sln b/CSharpDriver.sln index 35bfba877a6..e3d1b1323cf 100644 --- a/CSharpDriver.sln +++ b/CSharpDriver.sln @@ -1,11 +1,17 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30011.22 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31919.166 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D2012971-32BB-4C5F-BFC4-30A9994AB152}" + ProjectSection(SolutionItems) = preProject + src\Directory.Build.props = src\Directory.Build.props + EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{E472BDF5-61F1-461D-872B-9F53BB3ACA80}" + ProjectSection(SolutionItems) = preProject + tests\BuildProps\Tests.Build.props = tests\BuildProps\Tests.Build.props + EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MongoDB.Bson", "src\MongoDB.Bson\MongoDB.Bson.csproj", "{9FCB42A5-3BC6-492B-8EA0-53EF32E9F8CD}" EndProject @@ -48,11 +54,15 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution MongoDBTest.ruleset = MongoDBTest.ruleset EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SkippableTests", "tests\SkippableTests\SkippableTests.csproj", "{D198833A-6AC3-4327-8B02-5095455192D0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MongoDB.Driver.TestConsoleApplication", "tests\MongoDB.Driver.TestConsoleApplication\MongoDB.Driver.TestConsoleApplication.csproj", "{2E5780D2-29A5-483C-9CA2-844F45A66D0C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AstrolabeWorkloadExecutor", "tests\AstrolabeWorkloadExecutor\AstrolabeWorkloadExecutor.csproj", "{B90F025F-89D3-436A-AD78-6AA304A6E240}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MongoDB.Driver.SmokeTests.Sdk", "tests\SmokeTests\MongoDB.Driver.SmokeTests.Sdk\MongoDB.Driver.SmokeTests.Sdk.csproj", "{B711A69F-A337-452C-95E1-A6B15C727CBA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MongoDB.Driver.TestConsoleApplication", "tests\MongoDB.Driver.TestConsoleApplication\MongoDB.Driver.TestConsoleApplication.csproj", "{2E5780D2-29A5-483C-9CA2-844F45A66D0C}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SmokeTests", "SmokeTests", "{F64BF86A-1EF1-4596-84A6-6B4AB766CD77}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AstrolabeWorkloadExecutor", "tests\AstrolabeWorkloadExecutor\AstrolabeWorkloadExecutor.csproj", "{B90F025F-89D3-436A-AD78-6AA304A6E240}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MongoDB.TestHelpers", "tests\MongoDB.TestHelpers\MongoDB.TestHelpers.csproj", "{DF888021-744F-4A8B-9324-831DEFC48AB8}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -124,10 +134,6 @@ Global {7A015030-6329-4FAD-B6E3-CED5ED53019C}.Debug|Any CPU.Build.0 = Debug|Any CPU {7A015030-6329-4FAD-B6E3-CED5ED53019C}.Release|Any CPU.ActiveCfg = Release|Any CPU {7A015030-6329-4FAD-B6E3-CED5ED53019C}.Release|Any CPU.Build.0 = Release|Any CPU - {D198833A-6AC3-4327-8B02-5095455192D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D198833A-6AC3-4327-8B02-5095455192D0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D198833A-6AC3-4327-8B02-5095455192D0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D198833A-6AC3-4327-8B02-5095455192D0}.Release|Any CPU.Build.0 = Release|Any CPU {2E5780D2-29A5-483C-9CA2-844F45A66D0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2E5780D2-29A5-483C-9CA2-844F45A66D0C}.Debug|Any CPU.Build.0 = Debug|Any CPU {2E5780D2-29A5-483C-9CA2-844F45A66D0C}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -136,6 +142,14 @@ Global {B90F025F-89D3-436A-AD78-6AA304A6E240}.Debug|Any CPU.Build.0 = Debug|Any CPU {B90F025F-89D3-436A-AD78-6AA304A6E240}.Release|Any CPU.ActiveCfg = Release|Any CPU {B90F025F-89D3-436A-AD78-6AA304A6E240}.Release|Any CPU.Build.0 = Release|Any CPU + {B711A69F-A337-452C-95E1-A6B15C727CBA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B711A69F-A337-452C-95E1-A6B15C727CBA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B711A69F-A337-452C-95E1-A6B15C727CBA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B711A69F-A337-452C-95E1-A6B15C727CBA}.Release|Any CPU.Build.0 = Release|Any CPU + {DF888021-744F-4A8B-9324-831DEFC48AB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DF888021-744F-4A8B-9324-831DEFC48AB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DF888021-744F-4A8B-9324-831DEFC48AB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DF888021-744F-4A8B-9324-831DEFC48AB8}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -157,9 +171,11 @@ Global {C50D554C-2771-4CC1-9B2C-BB17FB27F935} = {E472BDF5-61F1-461D-872B-9F53BB3ACA80} {DAB8DFFD-0020-43B3-9C08-7723F5D68E90} = {E472BDF5-61F1-461D-872B-9F53BB3ACA80} {7A015030-6329-4FAD-B6E3-CED5ED53019C} = {E472BDF5-61F1-461D-872B-9F53BB3ACA80} - {D198833A-6AC3-4327-8B02-5095455192D0} = {E472BDF5-61F1-461D-872B-9F53BB3ACA80} {2E5780D2-29A5-483C-9CA2-844F45A66D0C} = {E472BDF5-61F1-461D-872B-9F53BB3ACA80} {B90F025F-89D3-436A-AD78-6AA304A6E240} = {E472BDF5-61F1-461D-872B-9F53BB3ACA80} + {B711A69F-A337-452C-95E1-A6B15C727CBA} = {F64BF86A-1EF1-4596-84A6-6B4AB766CD77} + {F64BF86A-1EF1-4596-84A6-6B4AB766CD77} = {E472BDF5-61F1-461D-872B-9F53BB3ACA80} + {DF888021-744F-4A8B-9324-831DEFC48AB8} = {E472BDF5-61F1-461D-872B-9F53BB3ACA80} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {24BEC44B-92B0-43AA-9B15-163459D0C098} diff --git a/Docs/landing/data/releases.toml b/Docs/landing/data/releases.toml index be04efff8e9..4e084a14f22 100644 --- a/Docs/landing/data/releases.toml +++ b/Docs/landing/data/releases.toml @@ -1,8 +1,13 @@ -current = "2.17.0" +current = "2.18.0" [[versions]] - version = "2.17.0" + version = "2.18.0" status = "current" + docs = "./2.18/" + api = "./2.18/apidocs" + +[[versions]] + version = "2.17.1" docs = "./2.17/" api = "./2.17/apidocs" @@ -101,32 +106,32 @@ current = "2.17.0" package = "MongoDB.Driver" description = "The driver." dependencies = ".NET Core Driver,.NET BSON Library" - versions = "2.15.0,2.14.1,2.13.2,2.12.4,2.11.6,2.10.4,2.9.3,2.8.1,2.7.3,2.6.1,2.5.1,2.4.4,2.3.0,2.2.4,2.1.1,2.0.2" + versions = "2.18.0,2.17.1,2.16.1,2.15.1,2.14.1,2.13.3,2.12.5,2.11.6,2.10.4,2.9.3,2.8.1,2.7.3,2.6.1,2.5.1,2.4.4,2.3.0,2.2.4,2.1.1,2.0.2" [[drivers]] name = ".NET GridFS" package = "MongoDB.Driver.GridFS" description = "The GridFS library." dependencies = ".NET Driver,.NET Core Driver,.NET BSON Library" - versions = "2.15.0,2.14.1,2.13.2,2.12.4,2.11.6,2.10.4,2.9.3,2.8.1,2.7.3,2.6.1,2.5.1,2.4.4,2.3.0,2.2.4,2.1.1" + versions = "2.18.0,2.17.1,2.16.1,2.15.1,2.14.1,2.13.3,2.12.5,2.11.6,2.10.4,2.9.3,2.8.1,2.7.3,2.6.1,2.5.1,2.4.4,2.3.0,2.2.4,2.1.1" [[drivers]] name = ".NET Core Driver" package = "MongoDB.Driver.Core" description = "The core driver." dependencies = ".NET BSON Library" - versions = "2.15.0,2.14.1,2.13.2,2.12.4,2.11.6,2.10.4,2.9.3,2.8.1,2.7.3,2.6.1,2.5.1,2.4.4,2.3.0,2.2.4,2.1.1,2.0.2" + versions = "2.18.0,2.17.1,2.16.1,2.15.1,2.14.1,2.13.3,2.12.5,2.11.6,2.10.4,2.9.3,2.8.1,2.7.3,2.6.1,2.5.1,2.4.4,2.3.0,2.2.4,2.1.1,2.0.2" [[drivers]] name = ".NET BSON Library" package = "MongoDB.Bson" description = "The BSON library." dependencies = "" - versions = "2.15.0,2.14.1,2.13.2,2.12.4,2.11.6,2.10.4,2.9.3,2.8.1,2.7.3,2.6.1,2.5.1,2.4.4,2.3.0,2.2.4,2.1.1,2.0.2" + versions = "2.18.0,2.17.1,2.16.1,2.15.1,2.14.1,2.13.3,2.12.5,2.11.6,2.10.4,2.9.3,2.8.1,2.7.3,2.6.1,2.5.1,2.4.4,2.3.0,2.2.4,2.1.1,2.0.2" [[drivers]] name = ".NET Legacy Driver" package = "mongocsharpdriver" description = "The legacy driver." dependencies = ".NET Driver,.NET Core Driver,.NET BSON Library" - versions = "2.15.0,2.14.1,2.13.2,2.12.4,2.11.6,2.10.4,2.9.3,2.8.1,2.7.3,2.6.1,2.5.1,2.4.4,2.3.0,2.2.4,2.1.1,2.0.2,1.11" + versions = "2.18.0,2.17.1,2.16.1,2.15.1,2.14.1,2.13.3,2.12.5,2.11.6,2.10.4,2.9.3,2.8.1,2.7.3,2.6.1,2.5.1,2.4.4,2.3.0,2.2.4,2.1.1,2.0.2,1.11" diff --git a/Docs/landing/static/versions.json b/Docs/landing/static/versions.json index 994c7c0fdfd..ff94c86a683 100644 --- a/Docs/landing/static/versions.json +++ b/Docs/landing/static/versions.json @@ -1,4 +1,5 @@ [ + {"version": "2.18"}, {"version": "2.17"}, {"version": "2.16"}, {"version": "2.15"}, diff --git a/Docs/reference/config.toml b/Docs/reference/config.toml index cdd54d2e522..6e4e06c78c5 100644 --- a/Docs/reference/config.toml +++ b/Docs/reference/config.toml @@ -1,4 +1,4 @@ -baseurl = "/mongo-csharp-driver/2.17" +baseurl = "/mongo-csharp-driver/2.18" languageCode = "en-us" title = "MongoDB .NET Driver" theme = "mongodb" diff --git a/Docs/reference/content/index.md b/Docs/reference/content/index.md index bfd81d6cfa6..a21d71b973e 100644 --- a/Docs/reference/content/index.md +++ b/Docs/reference/content/index.md @@ -9,7 +9,7 @@ type = "index" The [Getting Started]({{< relref "getting_started\index.md" >}}) guide contains information about system requirements, installation, and a simple tutorial to get up and running quickly. -## What's new in 2.17.0 +## What's new in 2.18.0 The [What's New]({{< relref "what_is_new.md" >}}) section contains the major new features of the driver. diff --git a/Docs/reference/content/reference/driver/crud/linq3.md b/Docs/reference/content/reference/driver/crud/linq3.md index bb4a1d2a44e..37b554fa31d 100644 --- a/Docs/reference/content/reference/driver/crud/linq3.md +++ b/Docs/reference/content/reference/driver/crud/linq3.md @@ -11,19 +11,21 @@ title = "LINQ3" ## LINQ3 -We have implemented a new LINQ provider, which is known as LINQ3. The current LINQ provider is known as LINQ2 (and LINQ1 is the now-obsolete LINQ provider in the v1.x releases of the driver). +We have implemented a new LINQ provider, which is known as LINQ3. The previous LINQ provider is known as LINQ2 (and LINQ1 is the now-obsolete LINQ provider in the v1.x releases of the driver). -While we fully transition to the new LINQ provider the two LINQ providers will exist side by side. LINQ2 will continue to be the default LINQ provider for the time being. +While we fully transition to the new LINQ provider the two LINQ providers will exist side by side. Starting with 2.19.0, LINQ3 is the default LINQ provider. -LINQ3 is production-ready. It fixes many LINQ2 bugs and offers support for a variety of new aggregation pipeline features present in newer server versions. We encourage all users to switch to LINQ3 and report any issues encountered. The [MongoDB Analyzer](https://www.mongodb.com/docs/mongodb-analyzer/current/) will provide tooltips indicating whether a particular query is supported in LINQ2, LINQ3, or both. +LINQ3 is production-ready. It fixes many LINQ2 bugs and offers support for a variety of new aggregation pipeline features present in newer server versions. The [MongoDB Analyzer](https://www.mongodb.com/docs/mongodb-analyzer/current/) will provide tooltips indicating whether a particular query is supported in LINQ2, LINQ3, or both. -You can opt into the new LINQ3 provider by configuring your `MongoClient` to use the new LINQ provider as follows: +If you encounter a problem with LINQ3, you can switch back the LINQ2 provider by configuring your `MongoClient` to use the previous LINQ provider as follows: ```csharp var connectionString = "mongodb://localhost"; var clientSettings = MongoClientSettings.FromConnectionString(connectionString); -clientSettings.LinqProvider = LinqProvider.V3; +clientSettings.LinqProvider = LinqProvider.V2; var client = new MongoClient(clientSettings); ``` The LINQ provider is only configurable at the `MongoClient` level. All LINQ queries run with a particular `MongoClient` use the same LINQ provider. + +If you do encounter a query that works in LINQ2 but fails in LINQ3, please file a [CSHARP ticket](https://jira.mongodb.org/browse/CSHARP) with a self-contained reproduction of the problem so that we can investigate and fix the issue. diff --git a/Docs/reference/content/reference/driver/index.md b/Docs/reference/content/reference/driver/index.md index 27a59e88c65..1581a8245c7 100644 --- a/Docs/reference/content/reference/driver/index.md +++ b/Docs/reference/content/reference/driver/index.md @@ -19,4 +19,5 @@ The MongoDB .NET Driver is mostly just a wrapper around [MongoDB.Driver.Core]({{ - [Administration]({{< relref "reference\driver\admin.md" >}}) - [Definitions and Builders]({{< relref "reference\driver\definitions.md" >}}) - [CRUD Operations]({{< relref "reference\driver\crud\index.md" >}}) -- [Error Handling]({{< relref "reference\driver\error_handling.md" >}}) \ No newline at end of file +- [Error Handling]({{< relref "reference\driver\error_handling.md" >}}) +- [Logging]({{< relref "reference\driver\logging.md" >}}) \ No newline at end of file diff --git a/Docs/reference/content/reference/driver/logging.md b/Docs/reference/content/reference/driver/logging.md new file mode 100644 index 00000000000..1d6c7384a88 --- /dev/null +++ b/Docs/reference/content/reference/driver/logging.md @@ -0,0 +1,75 @@ ++++ +date = "2022-11-22T15:36:56Z" +draft = false +title = "Logging" +[menu.main] + parent = "Driver" + identifier = "Logging" + weight = 10 + pre = "" ++++ + +## Logging + +Starting in version 2.18, the .NET/C# driver uses the standard [.NET logging API](https://learn.microsoft.com/en-us/dotnet/core/extensions/logging?tabs=command-line). The [MongoDB logging specification](https://github.com/mongodb/specifications/blob/master/source/logging/logging.rst) defines the components, structure, and verbosity of the logs. On this page, you can learn how to set up and configure logging for your application. + +You can configure logging using the [`LoggingSettings`]({{< apiref "T_MongoDB_Driver_Core_Configuration_LoggingSettings" >}}). ```LoggingSettings``` contains the following properties: + +|Property|Description| +|--------|-----------| +|LoggerFactory|The [ILoggerFactory](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging.iloggerfactory?view=dotnet-plat-ext-6.0) object that will create an [ILogger](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging.ilogger?view=dotnet-plat-ext-6.0).

**Data Type:** ILoggerFactory
**Default:** null | +|MaxDocumentSize|Maximum number of characters for extended JSON documents in logged messages

For example, when the driver logs the [CommandStarted](https://github.com/mongodb/specifications/blob/master/source/command-logging-and-monitoring/command-logging-and-monitoring.rst#command-started-message) event, it truncates the Command field to the specified character limit.

**Data Type:** int
**Default:** 1000| + + +The following code example creates a MongoClient that logs debug messages to the console. To do so, the code performs the following steps: + +- Creates a LoggerFactory, which specifies the logging destination and level +- Creates a LoggingSettings object, passing the LoggerFactory object as a parameter to the constructor +- Creates a MongoClient object, passing the LoggingSettings object as a parameter to the constructor + + +```csharp + using var loggerFactory = LoggerFactory.Create(b => + { + b.AddSimpleConsole(); + b.SetMinimumLevel(LogLevel.Debug); + }); + + var settings = MongoClientSettings.FromConnectionString(...); + settings.LoggingSettings = new LoggingSettings(loggerFactory); + var client = new MongoClient(settings); +``` +.NET/C# driver log category naming: + +|Property|Description| +|--------|-----------| +|MongoDB.Command|command| +|MongoDB.SDAM|topology| +|MongoDB.ServerSelection|serverSelection| +|MongoDB.Connection|connection| +|MongoDB.Internal.*|Prefix for all .NET/C# Driver internal components not described by spec| + + +How to configure log categories verbosity example: + +```csharp +var categoriesConfiguration = new Dictionary +{ + // Output all logs from all categories with Error and higher level + { "LogLevel:Default", "Error" }, + // Output SDAM logs with Debug and higher level + { "LogLevel:MongoDB.SDAM", "Debug" } +}; +var config = new ConfigurationBuilder() + .AddInMemoryCollection(categoriesConfiguration) + .Build(); +using var loggerFactory = LoggerFactory.Create(b => +{ + b.AddConfiguration(config); + b.AddSimpleConsole(); +}); + +var settings = MongoClientSettings.FromConnectionString("mongodb://localhost:27017"); +settings.LoggingSettings = new LoggingSettings(loggerFactory); +var client = new MongoClient(settings); +``` diff --git a/Docs/reference/content/reference/driver_core/index.md b/Docs/reference/content/reference/driver_core/index.md index eb1535c617f..942ed5251b6 100644 --- a/Docs/reference/content/reference/driver_core/index.md +++ b/Docs/reference/content/reference/driver_core/index.md @@ -23,7 +23,13 @@ Connection pooling is provided for every server that is discovered. There are a ### Server Monitoring -Each server that is discovered is monitored. By default, this monitoring happens every 10 seconds and consists of a `{ hello: 1 }` (or legacy hello) call followed by a `{ buildInfo: 1 }` call. When servers go down, the frequency of these calls will be increased to 500 milliseconds. See the [Server Discovery and Monitoring Specification](https://github.com/mongodb/specifications/blob/master/source/server-discovery-and-monitoring/server-discovery-and-monitoring-summary.rst) for more information. +Each server that is discovered is monitored. + +MongoDB 4.4 or later: The `MongoClient` establishes an exhaust cursor to each discovered cluster node using `{ hello: 1 }` (or legacy hello). Cluster nodes push topology updates to `MongoClient`. + +MongoDB 4.2 or ealier: The `MongoClient` polls each discovered cluster node every [heartbeatFrequencyMS](https://www.mongodb.com/docs/manual/reference/connection-string/#mongodb-urioption-urioption.heartbeatFrequencyMS) (default 10 seconds, MongoDB Atlas 5 seconds) using `{ hello: 1 }` (or legacy hello). + +When servers go down, the frequency of these calls will be increased to 500 milliseconds. See the [Server Discovery and Monitoring Specification](https://github.com/mongodb/specifications/blob/master/source/server-discovery-and-monitoring/server-discovery-and-monitoring-summary.rst) for more information. ### Server Selection diff --git a/Docs/reference/content/what_is_new.md b/Docs/reference/content/what_is_new.md index 3bd4f87ec02..8923e4056bc 100644 --- a/Docs/reference/content/what_is_new.md +++ b/Docs/reference/content/what_is_new.md @@ -8,6 +8,16 @@ title = "What's New" pre = "" +++ +## What's New in 2.18.0 + +The main new features in 2.18.0 include: + +* Snappy compression now uses a managed implementation +* ZStandard compression now uses a managed implementation +* Cache AWS credentials when possible +* New cross driver standard logging support +* Support for $documents aggregation pipeline stage + ## What's New in 2.17.0 The main new features in 2.17.0 include: diff --git a/Docs/reference/data/mongodb.toml b/Docs/reference/data/mongodb.toml index 294417bd4a5..81c464384e8 100644 --- a/Docs/reference/data/mongodb.toml +++ b/Docs/reference/data/mongodb.toml @@ -1,5 +1,5 @@ githubRepo = "mongo-csharp-driver" githubBranch = "master" -currentVersion = "2.17" +currentVersion = "2.18" highlightTheme = "idea.css" apiUrl = "apidocs/html" diff --git a/Docs/reference/themes/mongodb/static/css/overrides.css b/Docs/reference/themes/mongodb/static/css/overrides.css index bd7f42e083e..abbec57aa0d 100644 --- a/Docs/reference/themes/mongodb/static/css/overrides.css +++ b/Docs/reference/themes/mongodb/static/css/overrides.css @@ -115,3 +115,26 @@ a code { .jsEnabled #search { visibility: visible; } + +@media print { + a[href]:after { + content: none !important; + } + + .edit-link, + .sidebar, + .toggle-nav, + #header-db .nav-items { + display: none !important; + } + + #header-db { + position: inherit !important; + } + + .content .main-column { + margin-top: 0 !important; + margin-left: 0 !important; + width: 100% !important; + } +} diff --git a/GitVersion.yml b/GitVersion.yml index 38136914e22..803f209c029 100644 --- a/GitVersion.yml +++ b/GitVersion.yml @@ -1 +1 @@ -next-version: 2.17.0 +next-version: 2.18.1 diff --git a/README.md b/README.md index acb1c633066..49be990ff30 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,7 @@ Please see our [guidelines](CONTRIBUTING.md) for contributing to the driver. * 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 @@ -105,12 +106,15 @@ Please see our [guidelines](CONTRIBUTING.md) for contributing to the driver. * 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 @@ -123,6 +127,7 @@ Please see our [guidelines](CONTRIBUTING.md) for contributing to the driver. * 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 @@ -133,6 +138,7 @@ Please see our [guidelines](CONTRIBUTING.md) for contributing to the driver. * 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 @@ -142,19 +148,19 @@ Please see our [guidelines](CONTRIBUTING.md) for contributing to the driver. * Ed Rooth edward.rooth@wallstreetjapan.com * Katie Sadoff https://github.com/ksadoff * 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 -* Vincent Kam https://github.com/vincentkam -* Craig Wilson https://github.com/craiggwilson -* Ming Yau Lee https://github.com/mingyaulee 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). diff --git a/Release Notes/Release Notes v2.17.1.md b/Release Notes/Release Notes v2.17.1.md new file mode 100644 index 00000000000..5a075043840 --- /dev/null +++ b/Release Notes/Release Notes v2.17.1.md @@ -0,0 +1,27 @@ +# .NET Driver Version 2.17.1 Release Notes + +This is a patch release that fixes a potential data corruption bug in `RewrapManyDataKey` when rotating encrypted data encryption keys backed by GCP or Azure key services. + +The following conditions will trigger this bug: + +- A GCP-backed or Azure-backed data encryption key being rewrapped requires fetching an access token for decryption of the data encryption key. + +The result of this bug is that the key material for all data encryption keys being rewrapped is replaced by new randomly generated material, destroying the original key material. + +To mitigate potential data corruption, upgrade to this version or higher before using `RewrapManyDataKey` to rotate Azure-backed or GCP-backed data encryption keys. A backup of the key vault collection should **always** be taken before key rotation. + +An online version of these release notes is available at: + +https://github.com/mongodb/mongo-csharp-driver/blob/master/Release%20Notes/Release%20Notes%20v2.17.1.md + +The list of JIRA tickets resolved in this release is available at: + +https://jira.mongodb.org/issues/?jql=project%20%3D%20CSHARP%20AND%20fixVersion%20%3D%202.17.1%20ORDER%20BY%20key%20ASC + +Documentation on the .NET driver can be found at: + +http://mongodb.github.io/mongo-csharp-driver/ + +## Upgrading + +There are no known backwards breaking changes in this release. diff --git a/Release Notes/Release Notes v2.18.0.md b/Release Notes/Release Notes v2.18.0.md new file mode 100644 index 00000000000..88b96dceb9f --- /dev/null +++ b/Release Notes/Release Notes v2.18.0.md @@ -0,0 +1,23 @@ +# .NET Driver Version 2.18.0 Release Notes + +This is the general availability release for the 2.18.0 version of the driver. + +The main new features in 2.18.0 include: + +* Snappy compression now uses a managed implementation +* ZStandard compression now uses a managed implementation +* Cache AWS credentials when possible +* New cross driver standard logging support +* Support for $documents aggregation pipeline stage + +An online version of these release notes is available at: + +https://github.com/mongodb/mongo-csharp-driver/blob/master/Release%20Notes/Release%20Notes%20v2.18.0.md + +The full list of JIRA issues resolved in this release is available at: + +https://jira.mongodb.org/issues/?jql=project%20%3D%20CSHARP%20AND%20fixVersion%20%3D%202.18.0%20ORDER%20BY%20key%20ASC + +Documentation on the .NET driver can be found at: + +https://mongodb.github.io/mongo-csharp-driver/ diff --git a/Release Notes/Release Notes v2.18.1.md b/Release Notes/Release Notes v2.18.1.md new file mode 100644 index 00000000000..2ba8bf27a25 --- /dev/null +++ b/Release Notes/Release Notes v2.18.1.md @@ -0,0 +1,19 @@ +# .NET Driver Version 2.18.1 Release Notes + +This is a patch release that addresses some issues reported since 2.18.0 was released. + +The main new features in 2.18.1 include: + +* TODO + +An online version of these release notes is available at: + +https://github.com/mongodb/mongo-csharp-driver/blob/master/Release%20Notes/Release%20Notes%20v2.18.1.md + +The full list of JIRA issues resolved in this release is available at: + +https://jira.mongodb.org/issues/?jql=project%20%3D%20CSHARP%20AND%20fixVersion%20%3D%202.18.1%20ORDER%20BY%20key%20ASC + +Documentation on the .NET driver can be found at: + +https://mongodb.github.io/mongo-csharp-driver/ diff --git a/Release Notes/Release Notes v2.19.0.md b/Release Notes/Release Notes v2.19.0.md new file mode 100644 index 00000000000..107be22d3c1 --- /dev/null +++ b/Release Notes/Release Notes v2.19.0.md @@ -0,0 +1,45 @@ +# .NET Driver Version 2.19.0 Release Notes + +This is the general availability release for the 2.19.0 version of the driver. + +The main new features in 2.19.0 include: + +* Atlas Search builders +* Default LinqProvider changed to LINQ3 +* Support for Range Indexes preview +* ObjectSerializer allowed types configuration +* Bucket and BucketAuto stages support in LINQ3 +* Support Azure VM-assigned Managed Identity for Automatic KMS Credentials +* Native support for AWS IAM Roles + +### ObjectSerializer allowed types configuration + +The `ObjectSerializer` has been changed to only allow deserialization of types that are considered safe. +What types are considered safe is determined by a new configurable `AllowedTypes` function (of type `Func`). +The default `AllowedTypes` function is `ObjectSerializer.DefaultAllowedTypes` which returns true for a number of well-known framework types that we have deemed safe. +A typical example might be to allow all the default allowed types as well as your own types. This could be accomplished as follows: + +``` +var objectSerializer = new ObjectSerializer(type => ObjectSerializer.DefaultAllowedTypes(type) || type.FullName.StartsWith("MyNamespace")); +BsonSerializer.RegisterSerializer(objectSerializer); +``` + +More information about the `ObjectSerializer` is available in [our FAQ](https://www.mongodb.com/docs/drivers/csharp/v2.19/faq). + +### Default LinqProvider changed to LINQ3 +Default LinqProvider has been changed to LINQ3. +LinqProvider can be changed back to LINQ2 in the following way: + +``` +var connectionString = "mongodb://localhost"; +var clientSettings = MongoClientSettings.FromConnectionString(connectionString); +clientSettings.LinqProvider = LinqProvider.V2; +var client = new MongoClient(clientSettings); +``` +If you encounter a bug in LINQ3 provider, please report it in [CSHARP JIRA project](https://jira.mongodb.org/projects/CSHARP/issues). + +An online version of these release notes is available [here](https://github.com/mongodb/mongo-csharp-driver/blob/master/Release%20Notes/Release%20Notes%20v2.19.0.md). + +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.19.0%20ORDER%20BY%20key%20ASC). + +Documentation on the .NET driver can be found [here](https://www.mongodb.com/docs/drivers/csharp/v2.19/). diff --git a/THIRD-PARTY-NOTICES b/THIRD-PARTY-NOTICES index fb8b422e3d2..38ef8013836 100644 --- a/THIRD-PARTY-NOTICES +++ b/THIRD-PARTY-NOTICES @@ -2,9 +2,9 @@ The MongoDB .NET Driver uses third-party libraries or other resources that may be distributed under licenses different than the MongoDB .NET Driver software. In the event that we accidentally failed to list a required notice, -please bring it to our attention through any of the ways detailed here: +please bring it to our attention by opening a JIRA ticket in our issue tracker: - mongodb-dev@googlegroups.com + https://jira.mongodb.org/browse/CSHARP The attached notices are provided for information only. @@ -85,79 +85,4 @@ https://github.com/mongodb/mongo-csharp-driver. 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. - -3. The following files: Snappy32NativeMethods.cs, Snappy64NativeMethods.cs, SnappyCodec.cs, SnappyStatus.cs, SnappyNativeTests.cs - - Original work: - Copyright (c) 2016 - David Rouyer rouyer.david@gmail.com Copyright (c) 2011 - 2014 Robert Važan, Google Inc. - - Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - - * Neither the name of Robert Važan nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - Modified work: - Copyright 2020–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. - -4. The following files: NativeBufferInfo.cs, Zstandard64NativeMethods.cs, ZstandardNativeWrapper.cs, ZstandardStream.cs - - Original work: - Copyright (c) 2016-present, Facebook, Inc. All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - * Neither the name Facebook nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR - ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - Modified work: - Copyright 2020–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. \ No newline at end of file diff --git a/build.cake b/build.cake index de3418c7c7d..eb277fe4fe0 100644 --- a/build.cake +++ b/build.cake @@ -1,13 +1,15 @@ -#addin nuget:?package=Cake.FileHelpers&version=4.0.1 -#addin nuget:?package=Cake.Git&version=1.0.1 -#addin nuget:?package=Cake.Incubator&version=6.0.0 -#tool dotnet:?package=GitVersion.Tool&version=5.6.9 -#tool nuget:?package=JunitXml.TestLogger&version=3.0.98 +#addin nuget:?package=Cake.FileHelpers&version=5.0.0 +#addin nuget:?package=Cake.Git&version=2.0.0 +#addin nuget:?package=Cake.Incubator&version=7.0.0 +#tool dotnet:?package=GitVersion.Tool&version=5.10.3 +#tool nuget:?package=JunitXml.TestLogger&version=3.0.114 using System; -using System.Text.RegularExpressions; using System.Linq; -using Cake.Common.Tools.DotNetCore.DotNetCoreVerbosity; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; +using Cake.Common.Tools.DotNet.DotNetVerbosity; +using Architecture = System.Runtime.InteropServices.Architecture; using Path = Cake.Core.IO.Path; const string defaultTarget = "Default"; @@ -49,7 +51,6 @@ Task("Default") Task("Release") .IsDependentOn("Build") - .IsDependentOn("Test") .IsDependentOn("Docs") .IsDependentOn("Package"); @@ -57,18 +58,18 @@ Task("Restore") .Does(() => { // disable parallel restore to work around apparent bugs in restore - var restoreSettings = new DotNetCoreRestoreSettings + var restoreSettings = new DotNetRestoreSettings { DisableParallel = true }; - DotNetCoreRestore(solutionFullPath, restoreSettings); + DotNetRestore(solutionFullPath, restoreSettings); }); Task("Build") .IsDependentOn("Restore") .Does((buildConfig) => { - var settings = new DotNetCoreBuildSettings + var settings = new DotNetBuildSettings { NoRestore = true, Configuration = configuration, @@ -82,11 +83,11 @@ Task("Build") if (buildConfig.IsReleaseMode) { Console.WriteLine("Build continuousIntegration is enabled"); - settings.MSBuildSettings = new DotNetCoreMSBuildSettings(); + settings.MSBuildSettings = new DotNetMSBuildSettings(); // configure deterministic build for better compatibility with debug symbols (used in Package/Build tasks). Affects: *.nupkg settings.MSBuildSettings.SetContinuousIntegrationBuild(continuousIntegrationBuild: true); } - DotNetCoreBuild(solutionFullPath, settings); + DotNetBuild(solutionFullPath, settings); }); Task("BuildArtifacts") @@ -113,7 +114,9 @@ Task("BuildArtifacts") // add additional files needed by Sandcastle if (targetFramework == "net472" && project == "MongoDB.Driver.Core") { + fileNames.Add("AWSSDK.Core.dll"); fileNames.Add("DnsClient.dll"); + fileNames.Add("Microsoft.Extensions.Logging.Abstractions.dll"); fileNames.Add("MongoDB.Libmongocrypt.dll"); fileNames.Add("SharpCompress.dll"); } @@ -161,71 +164,37 @@ Task("Test") Console.WriteLine($"MONGO_X509_CLIENT_CERTIFICATE_PASSWORD={mongoX509ClientCertificatePassword}"); } - var settings = new DotNetCoreTestSettings - { - NoBuild = true, - NoRestore = true, - Configuration = configuration, - Loggers = CreateLoggers(), - ArgumentCustomization = args => args.Append("-- RunConfiguration.TargetPlatform=x64"), - Framework = buildConfig.Framework - }; - - DotNetCoreTest( - testProject.FullPath, - settings - ); + RunTests(buildConfig, testProject); }) .DeferOnError(); Task("TestNet472").IsDependentOn("Test"); Task("TestNetStandard20").IsDependentOn("Test"); Task("TestNetStandard21").IsDependentOn("Test"); +Task("TestNet60").IsDependentOn("Test"); Task("TestAwsAuthentication") .IsDependentOn("Build") .DoesForEach( - GetFiles("./**/MongoDB.Driver.Tests.csproj"), - testProject => - { - DotNetCoreTest( - testProject.FullPath, - new DotNetCoreTestSettings { - NoBuild = true, - NoRestore = true, - Configuration = configuration, - ArgumentCustomization = args => args.Append("-- RunConfiguration.TargetPlatform=x64"), - Filter = "Category=\"AwsMechanism\"" - } - ); - }); + items: GetFiles("./**/MongoDB.Driver.Tests.csproj"), + action: (BuildConfig buildConfig, Path testProject) => + RunTests(buildConfig, testProject, filter: "Category=\"AwsMechanism\"")); Task("TestPlainAuthentication") .IsDependentOn("Build") .DoesForEach( - GetFiles("./**/MongoDB.Driver.Tests.csproj"), - testProject => - { - DotNetCoreTest( - testProject.FullPath, - new DotNetCoreTestSettings { - NoBuild = true, - NoRestore = true, - Configuration = configuration, - ArgumentCustomization = args => args.Append("-- RunConfiguration.TargetPlatform=x64"), - Filter = "Category=\"PlainMechanism\"" - } - ); - }); + items: GetFiles("./**/MongoDB.Driver.Tests.csproj"), + action: (BuildConfig buildConfig, Path testProject) => + RunTests(buildConfig, testProject, filter: "Category=\"PlainMechanism\"")); // currently we are not running this Task on Evergreen (only locally occassionally) Task("TestAllGuidRepresentations") .IsDependentOn("Build") .DoesForEach( - GetFiles("./**/*.Tests.csproj") + items: GetFiles("./**/*.Tests.csproj") // .Where(name => name.ToString().Contains("Bson.Tests")) // uncomment to only test Bson .Where(name => !name.ToString().Contains("Atlas")), - testProject => + action: (BuildConfig buildConfig, Path testProject) => { var modes = new string[][] { @@ -244,208 +213,119 @@ Task("TestAllGuidRepresentations") Console.WriteLine($"TEST_WITH_DEFAULT_GUID_REPRESENTATION_MODE={testWithGuidRepresentationMode}"); Console.WriteLine($"TEST_WITH_DEFAULT_GUID_REPRESENTATION={testWithGuidRepresentation}"); - DotNetCoreTest( - testProject.FullPath, - new DotNetCoreTestSettings { - NoBuild = true, - NoRestore = true, - Configuration = configuration, - ArgumentCustomization = args => args.Append("-- RunConfiguration.TargetPlatform=x64"), - EnvironmentVariables = new Dictionary + RunTests( + buildConfig, + testProject, + settings => + { + settings.EnvironmentVariables = new Dictionary { { "TEST_WITH_DEFAULT_GUID_REPRESENTATION_MODE", testWithGuidRepresentationMode }, { "TEST_WITH_DEFAULT_GUID_REPRESENTATION", testWithGuidRepresentation } - } - } - ); + }; + }); } }); Task("TestAtlasConnectivity") .IsDependentOn("Build") .DoesForEach( - GetFiles("./**/AtlasConnectivity.Tests.csproj"), - testProject => -{ - DotNetCoreTest( - testProject.FullPath, - new DotNetCoreTestSettings { - NoBuild = true, - NoRestore = true, - Configuration = configuration, - ArgumentCustomization = args => args.Append("-- RunConfiguration.TargetPlatform=x64") - } - ); -}); + items: GetFiles("./**/AtlasConnectivity.Tests.csproj"), + action: (BuildConfig buildConfig, Path testProject) => RunTests(buildConfig, testProject)); Task("TestAtlasDataLake") .IsDependentOn("Build") .DoesForEach( - GetFiles("./**/MongoDB.Driver.Tests.csproj"), - testProject => - { - DotNetCoreTest( - testProject.FullPath, - new DotNetCoreTestSettings { - NoBuild = true, - NoRestore = true, - Configuration = configuration, - ArgumentCustomization = args => args.Append("-- RunConfiguration.TargetPlatform=x64"), - Filter = "Category=\"AtlasDataLake\"" - } - ); - }); + items: GetFiles("./**/MongoDB.Driver.Tests.csproj"), + action: (BuildConfig buildConfig, Path testProject) => + RunTests(buildConfig, testProject, filter: "Category=\"AtlasDataLake\"")); -Task("TestOcsp") +Task("TestAtlasSearch") .IsDependentOn("Build") .DoesForEach( - GetFiles("./**/MongoDB.Driver.Tests.csproj"), - testProject => -{ - DotNetCoreTest( - testProject.FullPath, - new DotNetCoreTestSettings { - NoBuild = true, - NoRestore = true, - Configuration = configuration, - - ArgumentCustomization = args => args - .Append("--filter FullyQualifiedName~OcspIntegrationTests") - .Append("-- RunConfiguration.TargetPlatform=x64") - } - ); -}); + items: GetFiles("./**/MongoDB.Driver.Tests.csproj"), + action: (BuildConfig buildConfig, Path testProject) => + RunTests(buildConfig, testProject, filter: "Category=\"AtlasSearch\"")); -Task("TestGssapi") +Task("TestOcsp") .IsDependentOn("Build") .DoesForEach( items: GetFiles("./**/MongoDB.Driver.Tests.csproj"), action: (BuildConfig buildConfig, Path testProject) => - { - var settings = new DotNetCoreTestSettings - { - NoBuild = true, - NoRestore = true, - Configuration = configuration, - ArgumentCustomization = args => args.Append("-- RunConfiguration.TargetPlatform=x64"), - Filter = "Category=\"GssapiMechanism\"", - Framework = buildConfig.Framework - }; + RunTests(buildConfig, testProject, filter: "Category=\"OCSP\"")); - DotNetCoreTest( - testProject.FullPath, - settings - ); - }); +Task("TestGssapi") + .IsDependentOn("Build") + .DoesForEach( + items: GetFiles("./**/MongoDB.Driver.Tests.csproj"), + action: (BuildConfig buildConfig, Path testProject) => + RunTests(buildConfig, testProject, filter: "Category=\"GssapiMechanism\"")); Task("TestGssapiNet472").IsDependentOn("TestGssapi"); Task("TestGssapiNetStandard20").IsDependentOn("TestGssapi"); Task("TestGssapiNetStandard21").IsDependentOn("TestGssapi"); +Task("TestGssapiNet60").IsDependentOn("TestGssapi"); Task("TestServerless") .IsDependentOn("Build") .DoesForEach( items: GetFiles("./**/MongoDB.Driver.Tests.csproj"), action: (BuildConfig buildConfig, Path testProject) => - { - var settings = new DotNetCoreTestSettings - { - NoBuild = true, - NoRestore = true, - Configuration = configuration, - ArgumentCustomization = args => args.Append("-- RunConfiguration.TargetPlatform=x64"), - Filter = "Category=\"Serverless\"", - Framework = buildConfig.Framework - }; - - DotNetCoreTest( - testProject.FullPath, - settings - ); - }); + RunTests(buildConfig, testProject, filter: "Category=\"Serverless\"")); Task("TestServerlessNet472").IsDependentOn("TestServerless"); Task("TestServerlessNetStandard20").IsDependentOn("TestServerless"); Task("TestServerlessNetStandard21").IsDependentOn("TestServerless"); +Task("TestServerlessNet60").IsDependentOn("TestServerless"); Task("TestLoadBalanced") .IsDependentOn("Build") .DoesForEach( items: GetFiles("./**/*.Tests.csproj"), action: (BuildConfig buildConfig, Path testProject) => - { - var settings = new DotNetCoreTestSettings - { - NoBuild = true, - NoRestore = true, - Configuration = configuration, - ArgumentCustomization = args => args.Append("-- RunConfiguration.TargetPlatform=x64"), - Filter = "Category=\"SupportLoadBalancing\"", - Framework = buildConfig.Framework - }; - - DotNetCoreTest( - testProject.FullPath, - settings - ); - }); + RunTests(buildConfig, testProject, filter: "Category=\"SupportLoadBalancing\"")); Task("TestLoadBalancedNetStandard20").IsDependentOn("TestLoadBalanced"); Task("TestLoadBalancedNetStandard21").IsDependentOn("TestLoadBalanced"); +Task("TestLoadBalancedNet60").IsDependentOn("TestLoadBalanced"); Task("TestCsfleWithMockedKms") .IsDependentOn("Build") .DoesForEach( items: GetFiles("./**/*.Tests.csproj"), action: (BuildConfig buildConfig, Path testProject) => - { - var settings = new DotNetCoreTestSettings - { - NoBuild = true, - NoRestore = true, - Configuration = configuration, - Loggers = CreateLoggers(), - ArgumentCustomization = args => args.Append("-- RunConfiguration.TargetPlatform=x64"), - Filter = "Category=\"CSFLE\"", - Framework = buildConfig.Framework - }; - - DotNetCoreTest( - testProject.FullPath, - settings - ); - }); + RunTests(buildConfig, testProject, filter: "Category=\"CSFLE\"")); Task("TestCsfleWithMockedKmsNet472").IsDependentOn("TestCsfleWithMockedKms"); Task("TestCsfleWithMockedKmsNetStandard20").IsDependentOn("TestCsfleWithMockedKms"); Task("TestCsfleWithMockedKmsNetStandard21").IsDependentOn("TestCsfleWithMockedKms"); +Task("TestCsfleWithMockedKmsNet60").IsDependentOn("TestCsfleWithMockedKms"); Task("TestCsfleWithMongocryptd") .IsDependentOn("Build") .DoesForEach( items: GetFiles("./**/*.Tests.csproj"), action: (BuildConfig buildConfig, Path testProject) => - { - var settings = new DotNetCoreTestSettings - { - NoBuild = true, - NoRestore = true, - Configuration = configuration, - Loggers = CreateLoggers(), - ArgumentCustomization = args => args.Append("-- RunConfiguration.TargetPlatform=x64"), - Filter = "Category=\"CSFLE\"", - Framework = buildConfig.Framework - }; - - DotNetCoreTest( - testProject.FullPath, - settings - ); - }); + RunTests(buildConfig, testProject, filter: "Category=\"CSFLE\"")); Task("TestCsfleWithMongocryptdNet472").IsDependentOn("TestCsfleWithMongocryptd"); Task("TestCsfleWithMongocryptdNetStandard20").IsDependentOn("TestCsfleWithMongocryptd"); Task("TestCsfleWithMongocryptdNetStandard21").IsDependentOn("TestCsfleWithMongocryptd"); +Task("TestCsfleWithMongocryptdNet60").IsDependentOn("TestCsfleWithMongocryptd"); + +Task("TestCsfleWithAzureKms") + .IsDependentOn("Build") + .DoesForEach( + items: GetFiles("./**/*.Tests.csproj"), + action: (BuildConfig buildConfig, Path testProject) => + RunTests(buildConfig, testProject, filter: "Category=\"CsfleAZUREKMS\"")); + +Task("TestCsfleWithGcpKms") + .IsDependentOn("Build") + .DoesForEach( + items: GetFiles("./**/*.Tests.csproj"), + action: (BuildConfig buildConfig, Path testProject) => + RunTests(buildConfig, testProject, filter: "Category=\"CsfleGCPKMS\"")); Task("Docs") .IsDependentOn("ApiDocs") @@ -525,7 +405,7 @@ Task("Package") Task("PackageNugetPackages") .IsDependentOn("Build") - .Does(() => + .Does((buildConfig) => { EnsureDirectoryExists(artifactsPackagesDirectory); CleanDirectory(artifactsPackagesDirectory); @@ -542,18 +422,18 @@ Task("PackageNugetPackages") foreach (var project in projects) { var projectPath = $"{srcDirectory}\\{project}\\{project}.csproj"; - var settings = new DotNetCorePackSettings + var settings = new DotNetPackSettings { Configuration = configuration, OutputDirectory = artifactsPackagesDirectory, NoBuild = true, // SetContinuousIntegrationBuild is enabled for nupkg on the Build step IncludeSymbols = true, - MSBuildSettings = new DotNetCoreMSBuildSettings() + MSBuildSettings = new DotNetMSBuildSettings() // configure deterministic build for better compatibility with debug symbols (used in Package/Build tasks). Affects: *.snupkg - .SetContinuousIntegrationBuild(continuousIntegrationBuild: true) - .WithProperty("PackageVersion", gitVersion.LegacySemVer) + .SetContinuousIntegrationBuild(continuousIntegrationBuild: true) + .WithProperty("PackageVersion", buildConfig.PackageVersion) }; - DotNetCorePack(projectPath, settings); + DotNetPack(projectPath, settings); } }); @@ -596,37 +476,61 @@ Task("DumpGitVersion") { Information(gitVersion.Dump()); }); - + Task("TestsPackagingProjectReference") .IsDependentOn("Build") .DoesForEach( - GetFiles("./**/*.Tests.csproj"), - testProject => + items: GetFiles("./**/*.Tests.csproj"), + action: (BuildConfig buildConfig, Path testProject) => + RunTests(buildConfig, testProject, filter: "Category=\"Packaging\"")); + +Task("SmokeTests") + .IsDependentOn("PackageNugetPackages") + .DoesForEach( + GetFiles("./**/SmokeTests/**/*.SmokeTests*.csproj"), + action: (BuildConfig buildConfig, Path testProject) => { - var settings = new DotNetCoreTestSettings + var environmentVariables = new Dictionary { - NoBuild = true, - NoRestore = true, - Configuration = configuration, - ArgumentCustomization = args => args.Append("-- RunConfiguration.TargetPlatform=x64"), - Filter = "Category=\"Packaging\"" + { "SmokeTestsPackageSha", gitVersion.Sha } }; - DotNetCoreTest( + var toolSettings = new DotNetToolSettings { EnvironmentVariables = environmentVariables }; + + Information($"Updating MongoDB package: {buildConfig.PackageVersion} sha: {gitVersion.Sha}"); + + DotNetTool( testProject.FullPath, - settings - ); + "add package MongoDB.Driver", + $"--version [{buildConfig.PackageVersion}]", + toolSettings); + + RunTests( + buildConfig, + testProject, + settings => + { + settings.NoBuild = false; + settings.NoRestore = false; + settings.EnvironmentVariables = environmentVariables; + }); }); +Task("SmokeTestsNet472").IsDependentOn("SmokeTests"); +Task("SmokeTestsNetCoreApp21").IsDependentOn("SmokeTests"); +Task("SmokeTestsNetCoreApp31").IsDependentOn("SmokeTests"); +Task("SmokeTestsNet50").IsDependentOn("SmokeTests"); +Task("SmokeTestsNet60").IsDependentOn("SmokeTests"); + Task("TestsPackaging") .IsDependentOn("TestsPackagingProjectReference") .IsDependentOn("Package") .DoesForEach( - () => - { - var monikers = new[] { "net472", "netcoreapp21", "netcoreapp30", "net50" }; + () => + { + var monikers = new[] { "net472", "netcoreapp21", "netcoreapp30", "net50", "net60" }; var csprojTypes = new[] { "SDK" }; - var processorArchitectures = new[] { "x64" }; + var processorArchitectures = new[] { "x64", "arm64" }; var projectTypes = new[] { "xunit", "console" }; return @@ -636,7 +540,7 @@ Task("TestsPackaging") from projectType in projectTypes select new { Moniker = moniker, CsprojType = csprojType, ProcessorArchitecture = processorArchitecture, ProjectType = projectType }; }, - (testDetails) => + (testDetails) => { var moniker = testDetails.Moniker; var csprojFormat = testDetails.CsprojType; @@ -651,7 +555,7 @@ Task("TestsPackaging") EnsureDirectoryExists(monikerTestFolder); CleanDirectory(monikerTestFolder); - var csprojFileName = $"{monikerTestFolder.GetDirectoryName()}.csproj"; + var csprojFileName = $"{monikerTestFolder.GetDirectoryName()}.csproj"; var csprojFullPath = monikerTestFolder.CombineWithFilePath(csprojFileName); switch (projectType) @@ -665,12 +569,12 @@ Task("TestsPackaging") } Information("Creating test project..."); - DotNetCoreTool(csprojFullPath, "new xunit", $"--target-framework-override {moniker} --language C# "); + DotNetTool(csprojFullPath, "new xunit", $"--target-framework-override {moniker} --language C# "); Information("Created test project"); // the below two packages are added just to allow using the same code as in xunit Information($"Adding FluentAssertions..."); - DotNetCoreTool( + DotNetTool( csprojFullPath, "add package FluentAssertions", $"--framework {moniker} --version 4.12.0" @@ -680,7 +584,7 @@ Task("TestsPackaging") var mongoDriverPackageVersion = ConfigureAndGetTestedDriverVersion(monikerTestFolder, localNugetSourceName); Information($"Adding test package..."); - DotNetCoreTool( + DotNetTool( csprojFullPath, $"add package {mongoDbDriverPackageName}", $"--framework {moniker} --version {mongoDriverPackageVersion}" @@ -693,10 +597,10 @@ Task("TestsPackaging") var files = GetFiles($"{packagingTestsDirectory}/*.cs").ToList(); CopyFiles(files, monikerTestFolder); // copy tests content - Information("Running tests..."); - DotNetCoreTest( + Information("Running tests..."); + DotNetTest( csprojFullPath.ToString(), - new DotNetCoreTestSettings + new DotNetTestSettings { Framework = moniker, Configuration = configuration, @@ -706,7 +610,7 @@ Task("TestsPackaging") .Append($"-- RunConfiguration.TargetPlatform={processorArchitecture}") } ); - } + } break; case "console": { @@ -716,14 +620,14 @@ Task("TestsPackaging") // The described solution works but it's tricky to implement it via scripts return; } - + Information("Creating console project..."); - DotNetCoreTool(csprojFullPath, "new console", $"--target-framework-override {moniker} --language C# --langVersion 9"); + DotNetTool(csprojFullPath, "new console", $"--target-framework-override {moniker} --language C# --langVersion 9"); Information("Created test project"); - + // the below two packages are added just to allow using the same code as in xunit Information($"Adding FluentAssertions..."); - DotNetCoreTool( + DotNetTool( csprojFullPath, "add package FluentAssertions", $"--framework {moniker} --version 4.12.0" @@ -731,7 +635,7 @@ Task("TestsPackaging") Information($"Added FluentAssertions"); Information($"Adding xunit..."); - DotNetCoreTool( + DotNetTool( csprojFullPath, "add package xunit", $"--framework {moniker} --version 2.4.0" @@ -741,7 +645,7 @@ Task("TestsPackaging") var mongoDriverPackageVersion = ConfigureAndGetTestedDriverVersion(monikerTestFolder, localNugetSourceName); Information($"Adding tested package..."); - DotNetCoreTool( + DotNetTool( csprojFullPath, $"add package {mongoDbDriverPackageName}", $"--framework {moniker} --version {mongoDriverPackageVersion}" @@ -754,13 +658,13 @@ Task("TestsPackaging") var files = GetFiles($"{packagingTestsDirectory}/*.cs").ToList(); CopyFiles(files, monikerTestFolder); // copy tests content - Information("Running console app..."); - DotNetCoreRun( + Information("Running console app..."); + DotNetRun( csprojFullPath.ToString(), - new DotNetCoreRunSettings + new DotNetRunSettings { - EnvironmentVariables = new Dictionary() - { + EnvironmentVariables = new Dictionary() + { { "DefineConstants", "CONSOLE_TEST" }, { "PlatformTarget", processorArchitecture } }, @@ -768,7 +672,7 @@ Task("TestsPackaging") Configuration = configuration } ); - } + } break; default: throw new NotSupportedException($"Packaging tests for {projectType} is not supported."); } @@ -776,7 +680,7 @@ Task("TestsPackaging") string ConfigureAndGetTestedDriverVersion(DirectoryPath directoryPath, string localNugetSourceName) { CreateNugetConfig(directoryPath, localNugetSourceName); - + var packagesList = NuGetList( new NuGetListSettings { AllVersions = true, @@ -796,13 +700,13 @@ Task("TestsPackaging") var mongoDriverPackageVersion = packagesList.Single(p => p.Name == mongoDbDriverPackageName).Version; Information($"Package version {mongoDriverPackageVersion}"); return mongoDriverPackageVersion; - - void CreateNugetConfig(DirectoryPath directoryPath, string localNugetSourceName) + + void CreateNugetConfig(DirectoryPath directoryPath, string localNugetSourceName) { var nugetConfigPath = directoryPath.CombineWithFilePath("nuget.config"); if (FileExists(nugetConfigPath)) DeleteFile(nugetConfigPath); - - DotNetCoreTool(nugetConfigPath, "new nugetconfig"); // create a default nuget.config + + DotNetTool(nugetConfigPath, "new nugetconfig"); // create a default nuget.config // // @@ -815,19 +719,36 @@ Task("TestsPackaging") .DeferOnError(); Setup( - setupContext => + setupContext => { + var targetPlatform = RuntimeInformation.OSArchitecture switch + { + Architecture.Arm64 => "arm64", + Architecture.X64 => "x64", + var unknownArchitecture => throw new Exception($"Unknown CPU architecture: {unknownArchitecture}.") + }; + var lowerTarget = target.ToLowerInvariant(); - var framework = lowerTarget switch + // Apple M1 (arm64) must run on .NET 6 as the hosting process is arm64 and cannot load the previous netcoreapp2.1/3.1 runtimes. + // While Rosetta 2 can cross-compile x64->arm64 to run x64 code, it requires a completely separate install of the .NET runtimes + // in a different directory with a x64 dotnet host process. This would further complicate our testing for little additional gain. + var framework = targetPlatform == "arm64" ? "net6.0" : lowerTarget switch { - string s when s.StartsWith("test") && s.EndsWith("net472") => "net472", - string s when s.StartsWith("test") && s.EndsWith("netstandard20") => "netcoreapp2.1", - string s when s.StartsWith("test") && s.EndsWith("netstandard21") => "netcoreapp3.1", + string s when s.EndsWith("net472") => "net472", + string s when s.EndsWith("netstandard20") || s.EndsWith("netcoreapp21") => "netcoreapp2.1", + string s when s.EndsWith("netstandard21") || s.EndsWith("netcoreapp31") => "netcoreapp3.1", + string s when s.EndsWith("net472") => "net472", + string s when s.EndsWith("net50") => "net5.0", + string s when s.EndsWith("net60") => "net6.0", _ => null }; + var isReleaseMode = lowerTarget.StartsWith("package") || lowerTarget == "release"; - Console.WriteLine($"Framework: {framework ?? "null (not set)"}, IsReleaseMode: {isReleaseMode}"); - return new BuildConfig(isReleaseMode, framework); + var packageVersion = lowerTarget.StartsWith("smoketests") ? gitVersion.FullSemVer.Replace('+', '-') : gitVersion.LegacySemVer; + + Console.WriteLine($"Framework: {framework ?? "null (not set)"}, TargetPlatform: {targetPlatform}, IsReleaseMode: {isReleaseMode}, PackageVersion: {packageVersion}"); + var loggers = CreateLoggers(); + return new BuildConfig(isReleaseMode, framework, targetPlatform, packageVersion, loggers); }); RunTarget(target); @@ -836,11 +757,17 @@ public class BuildConfig { public bool IsReleaseMode { get; } public string Framework { get; } + public string PackageVersion { get; } + public string TargetPlatform { get; } + public string[] Loggers { get; } - public BuildConfig(bool isReleaseMode, string framework) + public BuildConfig(bool isReleaseMode, string framework, string targetPlatform, string packageVersion, string[] loggers) { IsReleaseMode = isReleaseMode; Framework = framework; + TargetPlatform = targetPlatform; + PackageVersion = packageVersion; + Loggers = loggers; } } @@ -850,5 +777,27 @@ string[] CreateLoggers() // Evergreen CI server requires JUnit output format to display test results var junitLogger = $"junit;LogFilePath={testResultsFile};FailureBodyFormat=Verbose"; var consoleLogger = "console;verbosity=detailed"; - return new []{ junitLogger, consoleLogger }; + return new []{ junitLogger, consoleLogger }; +} + +void RunTests(BuildConfig buildConfig, Path path, string filter = null) +{ + RunTests(buildConfig, path, settings => settings.Filter = filter); +} + +void RunTests(BuildConfig buildConfig, Path path, Action settingsAction) +{ + var settings = new DotNetTestSettings + { + NoBuild = true, + NoRestore = true, + Configuration = configuration, + Loggers = buildConfig.Loggers, + ArgumentCustomization = args => args.Append($"-- RunConfiguration.TargetPlatform={buildConfig.TargetPlatform}"), + Framework = buildConfig.Framework + }; + + settingsAction?.Invoke(settings); + + DotNetTest(path.FullPath, settings); } \ No newline at end of file diff --git a/build.config b/build.config index 252a1ec71da..719f0942b7d 100644 --- a/build.config +++ b/build.config @@ -1,3 +1,3 @@ #!/usr/bin/env bash -CAKE_VERSION=1.3.0 -DOTNET_VERSION=5.0.401 +CAKE_VERSION=2.2.0 +DOTNET_VERSION=6.0.400 diff --git a/build.ps1 b/build.ps1 index 7db6396c081..197bd747a45 100644 --- a/build.ps1 +++ b/build.ps1 @@ -101,6 +101,7 @@ if($FoundDotNetCliVersion -ne $DotNetVersion) { (New-Object System.Net.WebClient).DownloadFile($DotNetUnixInstallerUri, $ScriptPath); & bash $ScriptPath --install-dir "$InstallPath" --channel 2.1 --no-path & bash $ScriptPath --install-dir "$InstallPath" --channel 3.1 --no-path + & bash $ScriptPath --install-dir "$InstallPath" --channel 5.0 --no-path & bash $ScriptPath --version "$DotNetVersion" --install-dir "$InstallPath" --channel "$DotNetChannel" --no-path Remove-PathVariable "$InstallPath" @@ -111,6 +112,7 @@ if($FoundDotNetCliVersion -ne $DotNetVersion) { (New-Object System.Net.WebClient).DownloadFile($DotNetInstallerUri, $ScriptPath); & $ScriptPath -Channel 2.1 -InstallDir $InstallPath; & $ScriptPath -Channel 3.1 -InstallDir $InstallPath; + & $ScriptPath -Channel 5.0 -InstallDir $InstallPath; & $ScriptPath -Channel $DotNetChannel -Version $DotNetVersion -InstallDir $InstallPath; Remove-PathVariable "$InstallPath" diff --git a/build.sh b/build.sh index e3aafc21fe8..1d0e31a676d 100755 --- a/build.sh +++ b/build.sh @@ -33,15 +33,20 @@ if [ "$DOTNET_VERSION" != "$DOTNET_INSTALLED_VERSION" ]; then mkdir "$SCRIPT_DIR/.dotnet" fi curl -Lsfo "$SCRIPT_DIR/.dotnet/dotnet-install.sh" https://dot.net/v1/dotnet-install.sh - # N.B. We explicitly install .NET Core 2.1 and 3.1 because .NET 5.0 SDK can build those TFMs + # N.B. We explicitly install .NET Core 2.1 and 3.1 because .NET 6.0 SDK can build those TFMs # but will silently upgrade to a more recent runtime to execute tests if the desired runtime # isn't available. For example, `dotnet run --framework netcoreapp3.0` will silently run - # on .NET 5.0 if .NET Core 3.0 and 3.1 aren't installed. + # on .NET 6.0 if .NET Core 3.0 and 3.1 aren't installed. # This solution is admittedly hacky as .NET Core 2.1 and 3.1 won't be installed if # $DOTNET_VERSION matches $DOTNET_INSTALLED_VERSION, but it minimizes the changes required # to install required dependencies on Evergreen. - bash "$SCRIPT_DIR/.dotnet/dotnet-install.sh" --channel 2.1 --install-dir .dotnet --no-path - bash "$SCRIPT_DIR/.dotnet/dotnet-install.sh" --channel 3.1 --install-dir .dotnet --no-path + # Since ARM64 support was first added in .NET 6.0, the following commands will install: + # | CPU | 2.1 | 3.1 | Latest | + # | x64 | x64 | x64 | x64 | + # | arm64 | x64 | x64 | arm64 | + bash "$SCRIPT_DIR/.dotnet/dotnet-install.sh" --channel 2.1 --architecture x64 --install-dir .dotnet --no-path + bash "$SCRIPT_DIR/.dotnet/dotnet-install.sh" --channel 3.1 --architecture x64 --install-dir .dotnet --no-path + bash "$SCRIPT_DIR/.dotnet/dotnet-install.sh" --channel 5.0 --architecture x64 --install-dir .dotnet --no-path bash "$SCRIPT_DIR/.dotnet/dotnet-install.sh" --version $DOTNET_VERSION --install-dir .dotnet --no-path export PATH="$SCRIPT_DIR/.dotnet":$PATH export DOTNET_ROOT="$SCRIPT_DIR/.dotnet" diff --git a/evergreen/evergreen.yml b/evergreen/evergreen.yml index e826a52b4f5..85223c09a38 100644 --- a/evergreen/evergreen.yml +++ b/evergreen/evergreen.yml @@ -13,7 +13,7 @@ stepback: true command_type: system # Protect ourself against rogue test case, or curl gone wild, that runs forever -# 60 minutes: 20 minutes is a normal test run + up to 10 minutes for test setup + 15 minutes for longer macOS tests + 15 minutes for longer macOS 1015 tests +# 60 minutes: 20 minutes is a normal test run + up to 10 minutes for test setup + 15 minutes for longer macOS tests + 15 minutes for longer macOS 1100 tests exec_timeout_secs: 3600 # What to do when evergreen hits the timeout (`post:` tasks are run automatically) @@ -22,6 +22,7 @@ timeout: params: script: | ls -la + df -h functions: @@ -304,8 +305,6 @@ functions: . ./evergreen/set-virtualenv.sh . ./evergreen/set-temp-fle-aws-creds.sh ${PREPARE_SHELL} - OS=${OS} \ - . ./evergreen/fetch-crypt_shared-library.sh OS=${OS} \ evergreen/add-ca-certs.sh AUTH=${AUTH} \ @@ -317,6 +316,7 @@ functions: CLIENT_PEM=${DRIVERS_TOOLS}/.evergreen/x509gen/client.pem \ REQUIRE_API_VERSION=${REQUIRE_API_VERSION} \ FRAMEWORK=${FRAMEWORK} \ + CRYPT_SHARED_LIB_PATH=${CRYPT_SHARED_LIB_PATH} \ evergreen/run-tests.sh echo "Skipping certificate removal..." OS=${OS} \ @@ -331,10 +331,10 @@ functions: set +x ${PREPARE_CSFLE} export KMS_MOCK_SERVERS_ENABLED=true + export GCE_METADATA_HOST="localhost:5000" + export AZURE_IMDS_MOCK_ENDPOINT="localhost:8080" ${PREPARE_SHELL} set +o xtrace - OS=${OS} \ - . ./evergreen/fetch-crypt_shared-library.sh OS=${OS} \ evergreen/add-ca-certs.sh AUTH=${AUTH} \ @@ -345,6 +345,7 @@ functions: CLIENT_PEM=${DRIVERS_TOOLS}/.evergreen/x509gen/client.pem \ FRAMEWORK=${FRAMEWORK} \ TARGET="TestCsfleWithMockedKms" \ + CRYPT_SHARED_LIB_PATH=${CRYPT_SHARED_LIB_PATH} \ evergreen/run-tests.sh OS=${OS} \ evergreen/cleanup-test-resources.sh @@ -372,6 +373,7 @@ functions: REQUIRE_API_VERSION=${REQUIRE_API_VERSION} \ TARGET="TestCsfleWithMongocryptd" \ FRAMEWORK=${FRAMEWORK} \ + CRYPT_SHARED_LIB_PATH="" \ evergreen/run-tests.sh echo "Skipping certificate removal..." OS=${OS} \ @@ -430,7 +432,12 @@ functions: "iam_auth_assume_role_name" : "${iam_auth_assume_role_name}", "iam_auth_ec2_instance_account" : "${iam_auth_ec2_instance_account}", "iam_auth_ec2_instance_secret_access_key" : "${iam_auth_ec2_instance_secret_access_key}", - "iam_auth_ec2_instance_profile" : "${iam_auth_ec2_instance_profile}" + "iam_auth_ec2_instance_profile" : "${iam_auth_ec2_instance_profile}", + "iam_auth_assume_web_role_name": "${iam_auth_assume_web_role_name}", + "iam_web_identity_issuer": "${iam_web_identity_issuer}", + "iam_web_identity_rsa_key": "${iam_web_identity_rsa_key}", + "iam_web_identity_jwks_uri": "${iam_web_identity_jwks_uri}", + "iam_web_identity_token_file": "${iam_web_identity_token_file}" } EOF @@ -439,10 +446,12 @@ functions: type: test params: silent: true + shell: "bash" working_dir: mongo-csharp-driver script: | ${PREPARE_SHELL} cd ${DRIVERS_TOOLS}/.evergreen/auth_aws + . ./activate-authawsvenv.sh mongo aws_e2e_regular_aws.js - command: shell.exec type: test @@ -455,21 +464,20 @@ functions: PASS=$(urlencode "${iam_auth_ecs_secret_access_key}") MONGODB_URI="mongodb://$USER:$PASS@localhost" EOF - PROJECT_DIRECTORY=${PROJECT_DIRECTORY} evergreen/run-mongodb-aws-test.sh + PROJECT_DIRECTORY=${PROJECT_DIRECTORY} OS=${OS} evergreen/run-mongodb-aws-test.sh run-aws-auth-test-with-assume-role-credentials: - command: shell.exec type: test params: silent: true + shell: "bash" working_dir: mongo-csharp-driver script: | ${PREPARE_SHELL} # The aws_e2e_assume_role script requires python3 with boto3. - virtualenv -p C:/python/Python38/python.exe mongovenv - . mongovenv/Scripts/activate - pip install boto3 cd ${DRIVERS_TOOLS}/.evergreen/auth_aws + . ./activate-authawsvenv.sh mongo aws_e2e_assume_role.js - command: shell.exec type: test @@ -488,32 +496,140 @@ functions: SESSION_TOKEN=$(urlencode $SESSION_TOKEN) MONGODB_URI="mongodb://$USER:$PASS@localhost" EOF - PROJECT_DIRECTORY=${PROJECT_DIRECTORY} DRIVERS_TOOLS=${DRIVERS_TOOLS} evergreen/run-mongodb-aws-test.sh + PROJECT_DIRECTORY=${PROJECT_DIRECTORY} DRIVERS_TOOLS=${DRIVERS_TOOLS} OS=${OS} evergreen/run-mongodb-aws-test.sh run-aws-auth-test-with-aws-EC2-credentials: - command: shell.exec type: test params: silent: true + shell: "bash" working_dir: mongo-csharp-driver script: | ${PREPARE_SHELL} - # The aws_e2e_assume_role script requires python3 with boto3. - virtualenv -p C:/python/Python38/python.exe mongovenv - . mongovenv/Scripts/activate - pip install boto3 + if [ "${skip_EC2_auth_test}" = "true" ]; then + echo "This platform does not support the EC2 auth test, skipping..." + exit 0 + fi cd ${DRIVERS_TOOLS}/.evergreen/auth_aws + . ./activate-authawsvenv.sh mongo aws_e2e_ec2.js - command: shell.exec type: test params: working_dir: mongo-csharp-driver script: | + if [ "${skip_EC2_auth_test}" = "true" ]; then + echo "This platform does not support the EC2 auth test, skipping..." + exit 0 + fi # DO NOT ECHO WITH XTRACE (which PREPARE_SHELL does) cat <<'EOF' > "${PROJECT_DIRECTORY}/prepare_mongodb_aws.sh" MONGODB_URI="mongodb://localhost" EOF - PROJECT_DIRECTORY=${PROJECT_DIRECTORY} evergreen/run-mongodb-aws-test.sh + export AWS_EC2_ENABLED=true + PROJECT_DIRECTORY=${PROJECT_DIRECTORY} ASSERT_NO_URI_CREDS=true OS=$OS evergreen/run-mongodb-aws-test.sh + + run-aws-auth-test-with-aws-ECS-credentials: + - command: shell.exec + type: test + params: + silent: true + shell: "bash" + working_dir: mongo-csharp-driver + script: | + ${PREPARE_SHELL} + if [ "${skip_ECS_auth_test}" = "true" ]; then + echo "This platform does not support the ECS auth test, skipping..." + exit 0 + fi + cd ${DRIVERS_TOOLS}/.evergreen/auth_aws + . ./activate-authawsvenv.sh + echo "Project Directory: $PROJECT_DIRECTORY" + # SRC_DIRECTORY is workaround since EG_TOOLS expects "src" folder as a root + SRC_DIRECTORY=$(dirname $PROJECT_DIRECTORY)/src + echo "Src Directory: $SRC_DIRECTORY" + cp -r $PROJECT_DIRECTORY $SRC_DIRECTORY + # Workaround. EG_TOOLS scripts for ECS assume that a folder with EG scripts in the driver is named ".evergreen" + mkdir $SRC_DIRECTORY/.evergreen + cp -r $SRC_DIRECTORY/evergreen/run-mongodb-aws-ecs-test.sh $SRC_DIRECTORY/.evergreen/run-mongodb-aws-ecs-test.sh + cat < setup.js + const mongo_binaries = "$MONGODB_BINARIES"; + const project_dir = "$SRC_DIRECTORY" + EOF + mongo --nodb setup.js aws_e2e_ecs.js + cd - + + run-aws-auth-test-with-aws-web-identity-credentials: + - command: shell.exec + type: test + params: + shell: "bash" + working_dir: mongo-csharp-driver + script: | + ${PREPARE_SHELL} + if [ "${skip_web_identity_auth_test}" = "true" ]; then + echo "This platform does not support the web identity auth test, skipping..." + exit 0 + fi + cd ${DRIVERS_TOOLS}/.evergreen/auth_aws + . ./activate-authawsvenv.sh + mongo aws_e2e_web_identity.js + - command: shell.exec + type: test + params: + working_dir: mongo-csharp-driver + silent: true + script: | + if [ "${skip_web_identity_auth_test}" = "true" ]; then + echo "This platform does not support the web identity auth test, skipping..." + exit 0 + fi + # DO NOT ECHO WITH XTRACE (which PREPARE_SHELL does) + cat <<'EOF' > "${PROJECT_DIRECTORY}/prepare_mongodb_aws.sh" + export AWS_ROLE_ARN="${iam_auth_assume_web_role_name}" + export AWS_WEB_IDENTITY_TOKEN_FILE="${iam_web_identity_token_file}" + export MONGODB_URI="mongodb://localhost" + EOF + - command: shell.exec + type: test + params: + working_dir: mongo-csharp-driver + script: | + ${PREPARE_SHELL} + if [ "${skip_web_identity_auth_test}" = "true" ]; then + echo "This platform does not support the web identity auth test, skipping..." + exit 0 + fi + PROJECT_DIRECTORY=${PROJECT_DIRECTORY} OS=$OS ASSERT_NO_URI_CREDS=true evergreen/run-mongodb-aws-test.sh + - command: shell.exec + type: test + params: + working_dir: mongo-csharp-driver + silent: true + script: | + if [ "${skip_EC2_auth_test}" = "true" ]; then + echo "This platform does not support the web identity auth test, skipping..." + exit 0 + fi + # DO NOT ECHO WITH XTRACE (which PREPARE_SHELL does) + cat <<'EOF' > "${PROJECT_DIRECTORY}/prepare_mongodb_aws.sh" + export AWS_ROLE_ARN="${iam_auth_assume_web_role_name}" + export AWS_WEB_IDENTITY_TOKEN_FILE="${iam_web_identity_token_file}" + export AWS_ROLE_SESSION_NAME="test" + export MONGODB_URI="mongodb://localhost" + EOF + - command: shell.exec + type: test + params: + working_dir: mongo-csharp-driver + script: | + ${PREPARE_SHELL} + if [ "${skip_web_identity_auth_test}" = "true" ]; then + echo "This platform does not support the web identity auth test, skipping..." + exit 0 + fi + PROJECT_DIRECTORY=${PROJECT_DIRECTORY} OS=$OS ASSERT_NO_URI_CREDS=true evergreen/run-mongodb-aws-test.sh run-aws-auth-test-with-aws-credentials-as-environment-variables: - command: shell.exec @@ -534,7 +650,7 @@ functions: working_dir: mongo-csharp-driver script: | ${PREPARE_SHELL} - evergreen/run-mongodb-aws-test.sh + OS=${OS} ASSERT_NO_URI_CREDS=true evergreen/run-mongodb-aws-test.sh run-aws-auth-test-with-aws-credentials-and-session-token-as-environment-variables: - command: shell.exec @@ -557,7 +673,7 @@ functions: working_dir: mongo-csharp-driver script: | ${PREPARE_SHELL} - evergreen/run-mongodb-aws-test.sh + OS=${OS} ASSERT_NO_URI_CREDS=true evergreen/run-mongodb-aws-test.sh run-atlas-data-lake-test: - command: shell.exec @@ -568,6 +684,15 @@ functions: ${PREPARE_SHELL} evergreen/run-atlas-data-lake-test.sh + run-atlas-search-test: + - command: shell.exec + type: test + params: + working_dir: mongo-csharp-driver + script: | + ${PREPARE_SHELL} + ATLAS_SEARCH="${ATLAS_SEARCH}" evergreen/run-atlas-search-test.sh + run-ocsp-test: - command: shell.exec type: test @@ -597,10 +722,12 @@ functions: - command: shell.exec params: background: true + shell: "bash" script: | set -o xtrace cd ${DRIVERS_TOOLS}/.evergreen/ocsp - nohup ./venv/Scripts/python ocsp_mock.py \ + . ./activate-ocspvenv.sh + nohup python ocsp_mock.py \ --ca_file ${OCSP_ALGORITHM}/ca.pem \ --ocsp_responder_cert ${OCSP_ALGORITHM}/ca.crt \ --ocsp_responder_key ${OCSP_ALGORITHM}/ca.key \ @@ -616,10 +743,12 @@ functions: - command: shell.exec params: background: true + shell: "bash" script: | set -o xtrace cd ${DRIVERS_TOOLS}/.evergreen/ocsp - nohup ./venv/Scripts/python ocsp_mock.py \ + . ./activate-ocspvenv.sh + nohup python ocsp_mock.py \ --ca_file ${OCSP_ALGORITHM}/ca.pem \ --ocsp_responder_cert ${OCSP_ALGORITHM}/ocsp-responder.crt \ --ocsp_responder_key ${OCSP_ALGORITHM}/ocsp-responder.key \ @@ -635,10 +764,12 @@ functions: - command: shell.exec params: background: true + shell: "bash" script: | set -o xtrace cd ${DRIVERS_TOOLS}/.evergreen/ocsp - nohup ./venv/Scripts/python.exe ocsp_mock.py \ + . ./activate-ocspvenv.sh + nohup python ocsp_mock.py \ --ca_file ${OCSP_ALGORITHM}/ca.pem \ --ocsp_responder_cert ${OCSP_ALGORITHM}/ca.crt \ --ocsp_responder_key ${OCSP_ALGORITHM}/ca.key \ @@ -656,10 +787,12 @@ functions: - command: shell.exec params: background: true + shell: "bash" script: | set -o xtrace cd ${DRIVERS_TOOLS}/.evergreen/ocsp - nohup ./venv/Scripts/python.exe ocsp_mock.py \ + . ./activate-ocspvenv.sh + nohup python ocsp_mock.py \ --ca_file ${OCSP_ALGORITHM}/ca.pem \ --ocsp_responder_cert ${OCSP_ALGORITHM}/ocsp-responder.crt \ --ocsp_responder_key ${OCSP_ALGORITHM}/ocsp-responder.key \ @@ -673,14 +806,38 @@ functions: params: working_dir: mongo-csharp-driver script: | + ${PREPARE_SHELL} + ${PREPARE_CSFLE} AUTH=${AUTH} \ FRAMEWORK=${FRAMEWORK} \ SERVERLESS_ATLAS_USER="${SERVERLESS_ATLAS_USER}" \ SERVERLESS_ATLAS_PASSWORD="${SERVERLESS_ATLAS_PASSWORD}" \ SERVERLESS_URI="${SERVERLESS_URI}" \ SSL=${SSL} \ + CRYPT_SHARED_LIB_PATH=${CRYPT_SHARED_LIB_PATH} \ evergreen/run-serverless-tests.sh + run-smoke-tests: + - command: shell.exec + type: test + params: + working_dir: mongo-csharp-driver + script: | + set +x + ${PREPARE_SHELL} + OS=${OS} \ + AUTH=${AUTH} \ + SSL=${SSL} \ + MONGODB_URI="${MONGODB_URI}" \ + TOPOLOGY=${TOPOLOGY} \ + OS=${OS} \ + COMPRESSOR=${COMPRESSOR} \ + CLIENT_PEM=${DRIVERS_TOOLS}/.evergreen/x509gen/client.pem \ + REQUIRE_API_VERSION=${REQUIRE_API_VERSION} \ + TARGET="SmokeTests" \ + FRAMEWORK=${FRAMEWORK} \ + evergreen/run-tests.sh + create-serverless-instance: - command: shell.exec params: @@ -713,59 +870,93 @@ functions: start-kms-mock-servers: - command: shell.exec params: + shell: "bash" script: | ${PREPARE_SHELL} cd ${DRIVERS_TOOLS}/.evergreen/csfle - . ./activate_venv.sh + . ./activate-kmstlsvenv.sh - command: shell.exec params: background: true + shell: "bash" script: | #expired client cert - PYTHON=$(Venv="${DRIVERS_TOOLS}/.evergreen/csfle/kmstlsvenv" OS=${OS} ${PROJECT_DIRECTORY}/evergreen/get-python-path.sh); cd ${DRIVERS_TOOLS}/.evergreen/csfle - $PYTHON -u kms_http_server.py -v --ca_file ../x509gen/ca.pem --cert_file ../x509gen/expired.pem --port 8000 + . ./activate-kmstlsvenv.sh + python -u kms_http_server.py -v --ca_file ../x509gen/ca.pem --cert_file ../x509gen/expired.pem --port 8000 - command: shell.exec params: background: true + shell: "bash" script: | #wrong-host client cert - PYTHON=$(Venv="${DRIVERS_TOOLS}/.evergreen/csfle/kmstlsvenv" OS=${OS} ${PROJECT_DIRECTORY}/evergreen/get-python-path.sh); cd ${DRIVERS_TOOLS}/.evergreen/csfle - $PYTHON -u kms_http_server.py -v --ca_file ../x509gen/ca.pem --cert_file ../x509gen/wrong-host.pem --port 8001 + . ./activate-kmstlsvenv.sh + python -u kms_http_server.py -v --ca_file ../x509gen/ca.pem --cert_file ../x509gen/wrong-host.pem --port 8001 - command: shell.exec params: background: true + shell: "bash" script: | #server.pem client cert - PYTHON=$(Venv="${DRIVERS_TOOLS}/.evergreen/csfle/kmstlsvenv" OS=${OS} ${PROJECT_DIRECTORY}/evergreen/get-python-path.sh); cd ${DRIVERS_TOOLS}/.evergreen/csfle - $PYTHON -u kms_http_server.py -v --ca_file ../x509gen/ca.pem --cert_file ../x509gen/server.pem --port 8002 --require_client_cert + . ./activate-kmstlsvenv.sh + python -u kms_http_server.py -v --ca_file ../x509gen/ca.pem --cert_file ../x509gen/server.pem --port 8002 --require_client_cert - start-kms-kmip-server: + start-kms-mock-kmip-server: - command: shell.exec params: + shell: "bash" script: | ${PREPARE_SHELL} cd ${DRIVERS_TOOLS}/.evergreen/csfle - . ./activate_venv.sh + . ./activate-kmstlsvenv.sh - command: shell.exec params: + shell: "bash" background: true script: | - PYTHON=$(Venv="${DRIVERS_TOOLS}/.evergreen/csfle/kmstlsvenv" OS=${OS} ${PROJECT_DIRECTORY}/evergreen/get-python-path.sh); cd ${DRIVERS_TOOLS}/.evergreen/csfle - $PYTHON -u kms_kmip_server.py + . ./activate-kmstlsvenv.sh + python -u kms_kmip_server.py - publish-snapshot: + start-kms-mock-gcp-server: - command: shell.exec - type: test params: - silent: true - working_dir: mongo-csharp-driver + shell: "bash" script: | - # DO NOT ECHO WITH XTRACE (which PREPARE_SHELL does) - PROJECT_DIRECTORY=${PROJECT_DIRECTORY} evergreen/publish.sh + ${PREPARE_SHELL} + cd ${DRIVERS_TOOLS}/.evergreen/csfle + . ./activate-kmstlsvenv.sh + - command: shell.exec + params: + background: true + shell: "bash" + script: | + cd ${DRIVERS_TOOLS}/.evergreen/csfle/gcpkms + . ./activate-kmstlsvenv.sh + python -m pip install PyJWT + mkdir ${DRIVERS_TOOLS}/tmp + echo '${GOOGLE_APPLICATION_CREDENTIALS_CONTENT}' > ${DRIVERS_TOOLS}/tmp/testgcpkms_key_file.json + export GOOGLE_APPLICATION_CREDENTIALS=${DRIVERS_TOOLS}/tmp/testgcpkms_key_file.json + python -u mock_server.py + + start-kms-mock-azure-imds-server: + - command: shell.exec + params: + shell: "bash" + script: | + ${PREPARE_SHELL} + cd ${DRIVERS_TOOLS}/.evergreen/csfle + . ./activate-kmstlsvenv.sh + - command: shell.exec + params: + background: true + shell: "bash" + script: | + cd ${DRIVERS_TOOLS}/.evergreen/csfle + . ./activate-kmstlsvenv.sh + python bottle.py fake_azure:imds cleanup: - command: shell.exec @@ -840,18 +1031,6 @@ post: tasks: - - name: compile - commands: - - func: exec-script - vars: - file: "evergreen/compile.sh" - # - func: upload-build - - - name: test - commands: - - func: bootstrap-mongo-orchestration - - func: run-tests - - name: test-net472 commands: - func: bootstrap-mongo-orchestration @@ -897,7 +1076,9 @@ tasks: - name: test-csfle-with-mocked-kms-tls-net472 commands: - func: start-kms-mock-servers - - func: start-kms-kmip-server + - func: start-kms-mock-kmip-server + - func: start-kms-mock-gcp-server + - func: start-kms-mock-azure-imds-server - func: bootstrap-mongo-orchestration - func: run-csfle-with-mocked-kms-tests vars: @@ -906,7 +1087,9 @@ tasks: - name: test-csfle-with-mocked-kms-tls-netstandard20 commands: - func: start-kms-mock-servers - - func: start-kms-kmip-server + - func: start-kms-mock-kmip-server + - func: start-kms-mock-gcp-server + - func: start-kms-mock-azure-imds-server - func: bootstrap-mongo-orchestration - func: run-csfle-with-mocked-kms-tests vars: @@ -915,7 +1098,9 @@ tasks: - name: test-csfle-with-mocked-kms-tls-netstandard21 commands: - func: start-kms-mock-servers - - func: start-kms-kmip-server + - func: start-kms-mock-kmip-server + - func: start-kms-mock-gcp-server + - func: start-kms-mock-azure-imds-server - func: bootstrap-mongo-orchestration - func: run-csfle-with-mocked-kms-tests vars: @@ -931,7 +1116,7 @@ tasks: vars: FRAMEWORK: netstandard20 - func: stop-load-balancer - + - name: test-load-balancer-netstandard21 commands: - func: bootstrap-mongo-orchestration @@ -981,12 +1166,15 @@ tasks: ORCHESTRATION_FILE: "auth-aws.json" TOPOLOGY: "server" - func: add-aws-auth-variables-to-file + # This step also creates test related users, so don't avoid this step in order to run other tests - func: run-aws-auth-test-with-regular-aws-credentials - func: run-aws-auth-test-with-assume-role-credentials - func: run-aws-auth-test-with-aws-credentials-as-environment-variables + # This step requires running run-aws-auth-test-with-assume-role-credentials before to explicitly set up instance profile - func: run-aws-auth-test-with-aws-credentials-and-session-token-as-environment-variables - func: run-aws-auth-test-with-aws-EC2-credentials - # ECS test is skipped untill testing on Linux becomes possible + - func: run-aws-auth-test-with-aws-ECS-credentials + - func: run-aws-auth-test-with-aws-web-identity-credentials - name: stable-api-tests-net472 commands: @@ -1023,12 +1211,9 @@ tasks: - func: bootstrap-mongohoused - func: run-atlas-data-lake-test - - name: publish-snapshot - depends_on: - - variant: ".tests-variant" - name: test + - name: atlas-search-test commands: - - func: publish-snapshot + - func: run-atlas-search-test - name: test-serverless-net472 exec_timeout_secs: 2700 # 45 minutes: 15 for setup + 30 for tests @@ -1216,6 +1401,41 @@ tasks: OCSP_ALGORITHM: "rsa" OCSP_TLS_SHOULD_SUCCEED: "false" + - name: test-smoke-tests-net472 + commands: + - func: bootstrap-mongo-orchestration + - func: run-smoke-tests + vars: + FRAMEWORK: net472 + + - name: test-smoke-tests-netcoreapp21 + commands: + - func: bootstrap-mongo-orchestration + - func: run-smoke-tests + vars: + FRAMEWORK: netcoreapp21 + + - name: test-smoke-tests-netcoreapp31 + commands: + - func: bootstrap-mongo-orchestration + - func: run-smoke-tests + vars: + FRAMEWORK: netcoreapp31 + + - name: test-smoke-tests-net50 + commands: + - func: bootstrap-mongo-orchestration + - func: run-smoke-tests + vars: + FRAMEWORK: net50 + + - name: test-smoke-tests-net60 + commands: + - func: bootstrap-mongo-orchestration + - func: run-smoke-tests + vars: + FRAMEWORK: net60 + # ECDSA tests # Disabled until https://jira.mongodb.org/browse/SPEC-1589 is resolved # - name: test-ocsp-ecdsa-valid-cert-server-staples-ca-responder @@ -1380,6 +1600,72 @@ tasks: # OCSP_ALGORITHM: "ecdsa" # OCSP_TLS_SHOULD_SUCCEED: "false" + - name: test-csfle-with-azure-kms + commands: + - command: shell.exec + type: setup + params: + working_dir: mongo-csharp-driver + shell: "bash" + script: | + ${PREPARE_SHELL} + echo "Copying files ... begin" + export AZUREKMS_RESOURCEGROUP=${testazurekms_resourcegroup} + export AZUREKMS_VMNAME=${AZUREKMS_VMNAME} + export AZUREKMS_PRIVATEKEYPATH=/tmp/testazurekms_privatekey + tar czf /tmp/mongo-csharp-driver.tgz . + AZUREKMS_SRC=/tmp/mongo-csharp-driver.tgz AZUREKMS_DST="~/" $DRIVERS_TOOLS/.evergreen/csfle/azurekms/copy-file.sh + echo "Copying files ... end" + echo "Untarring file ... begin" + AZUREKMS_CMD="tar xf mongo-csharp-driver.tgz" $DRIVERS_TOOLS/.evergreen/csfle/azurekms/run-command.sh + echo "Untarring file ... end" + + - command: shell.exec + type: test + params: + working_dir: "mongo-csharp-driver" + shell: "bash" + script: | + ${PREPARE_SHELL} + export AZUREKMS_RESOURCEGROUP=${testazurekms_resourcegroup} + export AZUREKMS_VMNAME=${AZUREKMS_VMNAME} + export AZUREKMS_PRIVATEKEYPATH=/tmp/testazurekms_privatekey + AZUREKMS_CMD="MONGODB_URI='mongodb://localhost:27017' KEY_NAME='${testazurekms_keyname}' KEY_VAULT_ENDPOINT='${testazurekms_keyvaultendpoint}' ./evergreen/run-csfle-azure-tests.sh" $DRIVERS_TOOLS/.evergreen/csfle/azurekms/run-command.sh + + - name: test-csfle-with-gcp-kms + commands: + - command: shell.exec + type: setup + params: + working_dir: mongo-csharp-driver + shell: "bash" + script: | + ${PREPARE_SHELL} + echo "Copying files ... begin" + export GCPKMS_GCLOUD=${GCPKMS_GCLOUD} + export GCPKMS_PROJECT=${GCPKMS_PROJECT} + export GCPKMS_ZONE=${GCPKMS_ZONE} + export GCPKMS_INSTANCENAME=${GCPKMS_INSTANCENAME} + tar czf /tmp/mongo-csharp-driver.tgz . + GCPKMS_SRC=/tmp/mongo-csharp-driver.tgz GCPKMS_DST=$GCPKMS_INSTANCENAME: $DRIVERS_TOOLS/.evergreen/csfle/gcpkms/copy-file.sh + echo "Copying files ... end" + echo "Untarring file ... begin" + GCPKMS_CMD="tar xf mongo-csharp-driver.tgz" $DRIVERS_TOOLS/.evergreen/csfle/gcpkms/run-command.sh + echo "Untarring file ... end" + + - command: shell.exec + type: test + params: + working_dir: "mongo-csharp-driver" + shell: "bash" + script: | + ${PREPARE_SHELL} + export GCPKMS_GCLOUD=${GCPKMS_GCLOUD} + export GCPKMS_PROJECT=${GCPKMS_PROJECT} + export GCPKMS_ZONE=${GCPKMS_ZONE} + export GCPKMS_INSTANCENAME=${GCPKMS_INSTANCENAME} + GCPKMS_CMD="MONGODB_URI='mongodb://localhost:27017' ./evergreen/run-csfle-gcp-tests.sh" $DRIVERS_TOOLS/.evergreen/csfle/gcpkms/run-command.sh + axes: - id: version display_name: MongoDB Version @@ -1424,17 +1710,30 @@ axes: display_name: "Windows 64-bit" variables: OS: "windows-64" + skip_ECS_auth_test: true + skip_web_identity_auth_test: true run_on: windows-64-vs2017-test - id: "ubuntu-1804" display_name: "Ubuntu 18.04" variables: OS: "ubuntu-1804" run_on: ubuntu1804-test - - id: "macos-1015" - display_name: "macOS 10.15" + - id: "macos-1100" + display_name: "macOS 11.00" variables: - OS: "macos-1015" - run_on: macos-1015 + OS: "macos-1100" + skip_EC2_auth_test: true + skip_ECS_auth_test: true + skip_web_identity_auth_test: true + run_on: macos-1100 + - id: "macos-1100-arm64" + display_name: "macOS 11.00 M1" + variables: + OS: "macos-1100-arm64" + skip_EC2_auth_test: true + skip_ECS_auth_test: true + skip_web_identity_auth_test: true + run_on: macos-1100-arm64 - id: topology display_name: Topology @@ -1492,26 +1791,91 @@ axes: variables: COMPRESSOR: "zstd" -buildvariants: +task_groups: + - name: testazurekms-task-group + setup_group_can_fail_task: true + setup_group_timeout_secs: 1800 # 30 minutes + setup_group: + - func: fetch-source + - func: prepare-resources + - func: windows-fix + - func: fix-absolute-paths + - func: init-test-results + - func: make-files-executable + - command: shell.exec + params: + shell: "bash" + script: | + ${PREPARE_SHELL} + echo '${testazurekms_publickey}' > /tmp/testazurekms_publickey + echo '${testazurekms_privatekey}' > /tmp/testazurekms_privatekey + # Set 600 permissions on private key file. Otherwise ssh / scp may error with permissions "are too open". + chmod 600 /tmp/testazurekms_privatekey + export AZUREKMS_CLIENTID=${testazurekms_clientid} + export AZUREKMS_TENANTID=${testazurekms_tenantid} + export AZUREKMS_SECRET=${testazurekms_secret} + export AZUREKMS_DRIVERS_TOOLS=$DRIVERS_TOOLS + export AZUREKMS_RESOURCEGROUP=${testazurekms_resourcegroup} + export AZUREKMS_PUBLICKEYPATH=/tmp/testazurekms_publickey + export AZUREKMS_PRIVATEKEYPATH=/tmp/testazurekms_privatekey + export AZUREKMS_SCOPE=${testazurekms_scope} + export AZUREKMS_VMNAME_PREFIX=CSHARPDRIVER + $DRIVERS_TOOLS/.evergreen/csfle/azurekms/create-and-setup-vm.sh + - command: expansions.update + params: + file: testazurekms-expansions.yml + teardown_group: + - command: shell.exec + params: + shell: "bash" + script: | + ${PREPARE_SHELL} + export AZUREKMS_VMNAME=${AZUREKMS_VMNAME} + export AZUREKMS_RESOURCEGROUP=${testazurekms_resourcegroup} + $DRIVERS_TOOLS/.evergreen/csfle/azurekms/delete-vm.sh + tasks: + - test-csfle-with-azure-kms + + - name: testgcpkms-task-group + setup_group_can_fail_task: true + setup_group_timeout_secs: 1800 # 30 minutes + setup_group: + - func: fetch-source + - func: prepare-resources + - func: windows-fix + - func: fix-absolute-paths + - func: init-test-results + - func: make-files-executable + - command: shell.exec + params: + shell: "bash" + script: | + ${PREPARE_SHELL} + echo '${GOOGLE_APPLICATION_CREDENTIALS_CONTENT}' > /tmp/testgcpkms_key_file.json + export GCPKMS_KEYFILE=/tmp/testgcpkms_key_file.json + export GCPKMS_DRIVERS_TOOLS=$DRIVERS_TOOLS + export GCPKMS_SERVICEACCOUNT="${GCPKMS_SERVICEACCOUNT}" + export GCPKMS_MACHINETYPE="e2-standard-4" + $DRIVERS_TOOLS/.evergreen/csfle/gcpkms/create-and-setup-instance.sh + # Load the GCPKMS_GCLOUD, GCPKMS_INSTANCE, GCPKMS_REGION, and GCPKMS_ZONE expansions. + - command: expansions.update + params: + file: testgcpkms-expansions.yml + teardown_group: + - command: shell.exec + params: + shell: "bash" + script: | + ${PREPARE_SHELL} + export GCPKMS_GCLOUD=${GCPKMS_GCLOUD} + export GCPKMS_PROJECT=${GCPKMS_PROJECT} + export GCPKMS_ZONE=${GCPKMS_ZONE} + export GCPKMS_INSTANCENAME=${GCPKMS_INSTANCENAME} + $DRIVERS_TOOLS/.evergreen/csfle/gcpkms/delete-instance.sh + tasks: + - test-csfle-with-gcp-kms -- name: windows-64-compile - display_name: "Windows 64-bit compile" - run_on: - - windows-64-vs2017-compile - tasks: - - name: compile -- name: ubuntu1804-compile - display_name: "Ubuntu 18.04 compile" - run_on: - - ubuntu1804-test - tasks: - - name: compile -- name: macos1015-compile - display_name: "macOS 10.15 compile" - run_on: - - macos-1015 - tasks: - - name: compile +buildvariants: - matrix_name: "secure-tests" matrix_spec: { version: "*", topology: "*", auth: "auth", ssl: "ssl", os: "windows-64" } @@ -1558,7 +1922,7 @@ buildvariants: - name: test-netstandard21 - matrix_name: "tests-snappy-compression-macOS" - matrix_spec: { compressor : "snappy", auth: "noauth", ssl: "nossl", version: ["5.0", "6.0", "rapid", "latest"], topology: "standalone", os: "macos-1015" } + matrix_spec: { compressor : "snappy", auth: "noauth", ssl: "nossl", version: ["5.0", "6.0", "rapid", "latest"], topology: "standalone", os: ["macos-1100", "macos-1100-arm64"] } display_name: "${version} ${compressor} ${topology} ${auth} ${ssl} ${os} " tags: ["tests-variant"] tasks: @@ -1582,7 +1946,7 @@ buildvariants: - name: test-netstandard21 - matrix_name: "tests-zstandard-compression-macOS" - matrix_spec: { compressor : "zstandard", auth: "noauth", ssl: "nossl", version: ["5.0", "6.0", "rapid", "latest"], topology: "standalone", os: "macos-1015" } + matrix_spec: { compressor : "zstandard", auth: "noauth", ssl: "nossl", version: ["5.0", "6.0", "rapid", "latest"], topology: "standalone", os: ["macos-1100", "macos-1100-arm64"] } display_name: "${version} ${compressor} ${topology} ${auth} ${ssl} ${os} " tags: ["tests-variant"] tasks: @@ -1606,14 +1970,14 @@ buildvariants: - name: test-netstandard21 - matrix_name: "secure-tests-macOS" - matrix_spec: { version: ["5.0", "6.0", "rapid", "latest"], topology: "replicaset", auth: "auth", ssl: "ssl", os: "macos-1015" } + matrix_spec: { version: ["5.0", "6.0", "rapid", "latest"], topology: "replicaset", auth: "auth", ssl: "ssl", os: ["macos-1100", "macos-1100-arm64"] } display_name: "${version} ${topology} ${auth} ${ssl} ${os}" tags: ["tests-variant"] tasks: - name: test-netstandard21 - matrix_name: "unsecure-tests-macOS" - matrix_spec: { version: ["5.0", "6.0", "rapid", "latest"], topology: "replicaset", auth: "noauth", ssl: "nossl", os: "macos-1015" } + matrix_spec: { version: ["5.0", "6.0", "rapid", "latest"], topology: "replicaset", auth: "noauth", ssl: "nossl", os: ["macos-1100", "macos-1100-arm64"] } display_name: "${version} ${topology} ${auth} ${ssl} ${os}" tags: ["tests-variant"] tasks: @@ -1634,7 +1998,7 @@ buildvariants: tasks: - name: ".ocsp" -- matrix_name: aws-auth-tests +- matrix_name: aws-auth-tests-windows matrix_spec: { version: ["4.4", "5.0", "6.0", "rapid", "latest"], topology: "standalone", os: "windows-64" } display_name: "MONGODB-AWS Auth test ${version} ${os}" run_on: @@ -1642,6 +2006,22 @@ buildvariants: tasks: - name: aws-auth-tests +- matrix_name: aws-auth-tests-linux + matrix_spec: { version: ["6.0", "rapid", "latest"], topology: "standalone", os: "ubuntu-1804" } + display_name: "MONGODB-AWS Auth test ${version} ${os}" + run_on: + - ubuntu1804-test + tasks: + - name: aws-auth-tests + +- matrix_name: aws-auth-tests-macos + matrix_spec: { version: ["6.0", "rapid", "latest"], topology: "standalone", os: "macos-1100" } + display_name: "MONGODB-AWS Auth test ${version} ${os}" + run_on: + - macos-1100 + tasks: + - name: aws-auth-tests + - matrix_name: stable-api-tests matrix_spec: { version: ["5.0", "6.0", "rapid", "latest"], topology: "standalone", auth: "auth", ssl: "nossl", os: "windows-64" } display_name: "Stable API ${version} ${topology} ${auth} ${ssl} ${os}" @@ -1654,7 +2034,7 @@ buildvariants: - matrix_name: plain-auth-tests matrix_spec: { os: "*" } - display_name: "PLAIN (LDAP) Auth Tests" + display_name: "PLAIN (LDAP) Auth Tests ${os}" tasks: - name: plain-auth-tests @@ -1701,6 +2081,13 @@ buildvariants: tasks: - name: atlas-data-lake-test +- name: atlas-search-test + display_name: "Atlas Search Tests" + run_on: + - windows-64-vs2017-test + tasks: + - name: atlas-search-test + - name: gssapi-auth-tests-windows run_on: - windows-64-vs2017-test @@ -1719,43 +2106,75 @@ buildvariants: - name: test-gssapi-netstandard21 - matrix_name: "csfle-with-mocked-kms-tests-windows" - matrix_spec: { os: "windows-64", ssl: "nossl", version: [ "5.0", "6.0", "rapid", "latest" ], topology: ["standalone"] } + matrix_spec: { os: "windows-64", ssl: "nossl", version: [ "4.2", "4.4", "5.0", "6.0", "rapid", "latest" ], topology: ["replicaset"] } display_name: "CSFLE Mocked KMS ${version} ${os}" tasks: - name: test-csfle-with-mocked-kms-tls-net472 - name: test-csfle-with-mocked-kms-tls-netstandard20 - name: test-csfle-with-mocked-kms-tls-netstandard21 + - name: test-csfle-with-mongocryptd-net472 + - name: test-csfle-with-mongocryptd-netstandard20 + - name: test-csfle-with-mongocryptd-netstandard21 - matrix_name: "csfle-with-mocked-kms-tests-linux" - matrix_spec: { os: "ubuntu-1804", ssl: "nossl", version: [ "5.0", "6.0", "rapid", "latest" ], topology: ["standalone"] } + matrix_spec: { os: "ubuntu-1804", ssl: "nossl", version: [ "4.2", "4.4", "5.0", "6.0", "rapid", "latest" ], topology: ["replicaset"] } display_name: "CSFLE Mocked KMS ${version} ${os}" tasks: - name: test-csfle-with-mocked-kms-tls-netstandard20 - name: test-csfle-with-mocked-kms-tls-netstandard21 + - name: test-csfle-with-mongocryptd-netstandard20 + - name: test-csfle-with-mongocryptd-netstandard21 - matrix_name: "csfle-with-mocked-kms-tests-macOS" - matrix_spec: { os: "macos-1015", ssl: "nossl", version: [ "5.0", "6.0", "rapid", "latest" ], topology: ["standalone"] } + matrix_spec: { os: [ "macos-1100", "macos-1100-arm64" ], ssl: "nossl", version: [ "4.2", "4.4", "5.0", "6.0", "rapid", "latest" ], topology: ["replicaset"] } display_name: "CSFLE Mocked KMS ${version} ${os}" tasks: - name: test-csfle-with-mocked-kms-tls-netstandard21 + - name: test-csfle-with-mongocryptd-netstandard21 -- matrix_name: "csfle-with-mongocryptd-windows" - matrix_spec: { os: "windows-64", ssl: "nossl", version: [ "4.2", "4.4", "5.0", "6.0", "latest" ], topology: ["replicaset"] } - display_name: "CSFLE with mongocryptd ${version} ${os}" +- matrix_name: "csfle-with-azure-kms-tests-linux" + matrix_spec: { ssl: "nossl", os: "ubuntu-1804" } + display_name: "CSFLE with AZURE KMS ${os}" + batchtime: 20160 # 14 days tasks: - - name: test-csfle-with-mongocryptd-net472 - - name: test-csfle-with-mongocryptd-netstandard20 + - name: testazurekms-task-group - name: test-csfle-with-mongocryptd-netstandard21 -- matrix_name: "csfle-with-mongocryptd-linux" - matrix_spec: { os: "ubuntu-1804", ssl: "nossl", version: [ "4.2", "4.4", "5.0", "6.0", "latest" ], topology: ["replicaset"] } - display_name: "CSFLE with mongocryptd ${version} ${os}" +- matrix_name: "csfle-with-gcp-kms-tests-linux" + matrix_spec: { ssl: "nossl", os: "ubuntu-1804" } + display_name: "CSFLE with GCP KMS ${os}" + batchtime: 20160 # 14 days tasks: - - name: test-csfle-with-mongocryptd-netstandard20 + - name: testgcpkms-task-group - name: test-csfle-with-mongocryptd-netstandard21 -- matrix_name: "csfle-with-mongocryptd-macOS" - matrix_spec: { os: "macos-1015", ssl: "nossl", version: [ "4.2", "4.4", "5.0", "6.0", "latest" ], topology: ["replicaset"] } - display_name: "CSFLE with mongocryptd ${version} ${os}" +- matrix_name: "smoke-tests-windows" + matrix_spec: { os: "windows-64", ssl: "nossl", version: [ "5.0", "6.0", "latest" ], topology: ["replicaset"] } + display_name: "smoke-tests ${version} ${os}" + batchtime: 1440 # 1 day + tasks: + - name: test-smoke-tests-net472 + - name: test-smoke-tests-netcoreapp21 + - name: test-smoke-tests-netcoreapp31 + - name: test-smoke-tests-net50 + - name: test-smoke-tests-net60 + +- matrix_name: "smoke-tests-linux" + matrix_spec: { os: "ubuntu-1804", ssl: "nossl", version: [ "5.0", "6.0", "latest" ], topology: ["replicaset"] } + display_name: "smoke-tests ${version} ${os}" + batchtime: 1440 # 1 day + tasks: + - name: test-smoke-tests-netcoreapp21 + - name: test-smoke-tests-netcoreapp31 + - name: test-smoke-tests-net50 + - name: test-smoke-tests-net60 + +- matrix_name: "smoke-tests-macOS" + matrix_spec: { os: "macos-1100", ssl: "nossl", version: [ "5.0", "6.0", "latest" ], topology: ["replicaset"] } + display_name: "smoke-tests ${version} ${os}" + batchtime: 1440 # 1 day tasks: - - name: test-csfle-with-mongocryptd-netstandard21 \ No newline at end of file + - name: test-smoke-tests-netcoreapp21 + - name: test-smoke-tests-netcoreapp31 + - name: test-smoke-tests-net50 + - name: test-smoke-tests-net60 diff --git a/evergreen/fetch-crypt_shared-library.sh b/evergreen/fetch-crypt_shared-library.sh deleted file mode 100644 index 3114708ea95..00000000000 --- a/evergreen/fetch-crypt_shared-library.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash - -# Fetch csfle shared library. -# -# Environment variables used as input: -# OS The current operating system -# DRIVERS_TOOLS -# -# Environment variables produced as output: -# MONGODB_CSFLE_PATH The MONGODB_CSFLE_PATH path - -set -o xtrace # Write all commands first to stderr -set -o errexit # Exit the script with an error if any of the commands fail - - -PYTHON=$(OS=${OS} ${PROJECT_DIRECTORY}/evergreen/get-python-path.sh) -$PYTHON -u ${DRIVERS_TOOLS}/.evergreen/mongodl.py --component crypt_shared --out ${DRIVERS_TOOLS}/evergreen/csfle --version 6.0.0-rc13 - -if [[ "$OS" =~ Windows|windows ]]; then - export CRYPT_SHARED_LIB_PATH="${DRIVERS_TOOLS}/evergreen/csfle/bin/mongo_crypt_v1.dll" -elif [[ "$OS" =~ Mac|mac ]]; then - export CRYPT_SHARED_LIB_PATH="${DRIVERS_TOOLS}/evergreen/csfle/lib/mongo_crypt_v1.dylib" -else - export CRYPT_SHARED_LIB_PATH="${DRIVERS_TOOLS}/evergreen/csfle/lib/mongo_crypt_v1.so" -fi - -echo "crypt shared library path $CRYPT_SHARED_LIB_PATH" \ No newline at end of file diff --git a/evergreen/get-python-path.sh b/evergreen/get-python-path.sh deleted file mode 100644 index a99b3155f22..00000000000 --- a/evergreen/get-python-path.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env bash - -# Find the version of python on the system. -# -# Environment variables used as input: -# Venv Venv path -# OS The current operating system -# -# Environment variables produced as output: -# PYTHON The python path - -# Don't write anything to output -if [ -z "$Venv" ]; then - if [ -e "/cygdrive/c/python/Python39/python" ]; then - echo "/cygdrive/c/python/Python39/python" - elif [ -e "/opt/mongodbtoolchain/v3/bin/python3" ]; then - echo "/opt/mongodbtoolchain/v3/bin/python3" - elif python3 --version >/dev/null 2>&1; then - echo python3 - else - echo python - fi -else - if [[ "$OS" =~ Windows|windows ]]; then - echo "${Venv}/Scripts/python" - else - echo "${Venv}/bin/python" - fi -fi diff --git a/evergreen/publish.sh b/evergreen/publish.sh deleted file mode 100755 index 4db7b6711ba..00000000000 --- a/evergreen/publish.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash - -# DO NOT ECHO COMMANDS AS THEY CONTAIN SECRETS! - -set -o errexit # Exit the script with error if any of the commands fail - -############################################ -# Main Program # -############################################ - -echo "Publishing .NET driver" diff --git a/evergreen/run-atlas-search-test.sh b/evergreen/run-atlas-search-test.sh new file mode 100644 index 00000000000..88b9b151a85 --- /dev/null +++ b/evergreen/run-atlas-search-test.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +set -o xtrace +set -o errexit # Exit the script with error if any of the commands fail + +# Environment variables produced as output +# ATLAS_SEARCH_TESTS_ENABLED Enable atlas search tests. + +############################################ +# Main Program # +############################################ + +echo "Running Atlas Search driver tests" + +export ATLAS_SEARCH_TESTS_ENABLED=true + +powershell.exe .\\build.ps1 --target=TestAtlasSearch \ No newline at end of file diff --git a/evergreen/run-csfle-azure-tests.sh b/evergreen/run-csfle-azure-tests.sh new file mode 100644 index 00000000000..52556db44b9 --- /dev/null +++ b/evergreen/run-csfle-azure-tests.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +set -o xtrace +set -o errexit # Exit the script with error if any of the commands fail + +# Environment variables used as input: +# MONGODB_URI Set the URI, including an optional username/password to use to connect to the server +# KEY_NAME Set azure kms key name +# KEY_VAULT_ENDPOINT Set azure kms key vault endpoint +# +# Environment variables produced as output +# DOTNET_SYSTEM_GLOBALIZATION_INVARIANT Workaround for the https://github.com/dotnet/core/issues/2186 issue. +# CSFLE_AZURE_KMS_TESTS_ENABLED Enable csfle azure kms tests. + +############################################ +# Main Program # +############################################ + +echo "Running Azure Credential Acquisition Test" + +# fixing https://github.com/dotnet/core/issues/2186#issuecomment-671105420 +export DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 +export CSFLE_AZURE_KMS_TESTS_ENABLED=true + +./build.sh --target=TestCsfleWithAzureKms diff --git a/evergreen/run-csfle-gcp-tests.sh b/evergreen/run-csfle-gcp-tests.sh new file mode 100644 index 00000000000..7f4819ea4b9 --- /dev/null +++ b/evergreen/run-csfle-gcp-tests.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +set -o xtrace +set -o errexit # Exit the script with error if any of the commands fail + +# Supported/used environment variables: +# MONGODB_URI Set the URI, including an optional username/password to use to connect to the server +############################################ +# Main Program # +############################################ + +echo "Running GCP Credential Acquisition Test" + +# fixing https://github.com/dotnet/core/issues/2186#issuecomment-671105420 +export DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 +export CSFLE_GCP_KMS_TESTS_ENABLED=true + +./build.sh --target=TestCsfleWithGcpKms diff --git a/evergreen/run-mongodb-aws-ecs-test.sh b/evergreen/run-mongodb-aws-ecs-test.sh new file mode 100644 index 00000000000..cbf6390c6eb --- /dev/null +++ b/evergreen/run-mongodb-aws-ecs-test.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +# Don't trace since the URI contains a password that shouldn't show up in the logs +set -o errexit # Exit the script with error if any of the commands fail + +# Environment variables used as input: +# MONGODB_URI Set the URI, including username/password to use to connect to the server via MONGODBAWS authentication mechanism +# ASSERT_NO_URI_CREDS Determines whether we need assert existence credentials in connection string or not +# +# Environment variables used as output: +# AWS_TESTS_ENABLED Allows running AWS tests +# AWS_ECS_ENABLED Allows running ECS tests +# +############################################ +# Main Program # +############################################ + +if [[ -z "$1" ]]; then + echo "usage: $0 " + exit 1 +fi +export MONGODB_URI="$1" + +if echo "$MONGODB_URI" | grep -q "@"; then + echo "MONGODB_URI unexpectedly contains user credentials in ECS test!"; + exit 1 +fi +# Now we can safely enable xtrace +set -o xtrace +export AWS_TESTS_ENABLED=true +export AWS_ECS_ENABLED=true + +# EG scripts for ECS assume that a root folder is "src" and all driver side scripts are placed in ".evergreen" folder. +# So that script is copied into "src/.evergreen" before running +cd src + +./build.sh --target=TestAwsAuthentication diff --git a/evergreen/run-mongodb-aws-test.sh b/evergreen/run-mongodb-aws-test.sh index 646b2860a42..3b9eda5fd04 100755 --- a/evergreen/run-mongodb-aws-test.sh +++ b/evergreen/run-mongodb-aws-test.sh @@ -5,15 +5,23 @@ set -o errexit # Exit the script with error if any of the commands fail # Supported/used environment variables: # MONGODB_URI Set the URI, including username/password to use to connect to the server via MONGODBAWS authentication mechanism +# OS Current operation system +# ASSERT_NO_URI_CREDS Determines whether we need assert existence credentials in connection string or not ############################################ # Main Program # ############################################ echo "Running MONGODB-AWS authentication tests" +echo "OS: $OS" -# Provision the correct connection string and set up SSL if needed -for var in TMP TEMP NUGET_PACKAGES NUGET_HTTP_CACHE_PATH APPDATA; do setx $var z:\\data\\tmp; export $var=z:\\data\\tmp; done +for var in TMP TEMP NUGET_PACKAGES NUGET_HTTP_CACHE_PATH APPDATA; do + if [[ "$OS" =~ Windows|windows ]]; then + export $var=z:\\data\\tmp; + else + export $var=/data/tmp; + fi +done # ensure no secrets are printed in log files set +x @@ -22,6 +30,7 @@ set +x shopt -s expand_aliases # needed for `urlencode` alias [ -s "${PROJECT_DIRECTORY}/prepare_mongodb_aws.sh" ] && source "${PROJECT_DIRECTORY}/prepare_mongodb_aws.sh" +# Provision the correct connection string if [ -z ${MONGODB_URI+x} ]; then echo "MONGODB_URI is not set"; exit 1 @@ -30,10 +39,21 @@ MONGODB_URI="${MONGODB_URI}/aws?authMechanism=MONGODB-AWS" if [[ -n ${SESSION_TOKEN} ]]; then MONGODB_URI="${MONGODB_URI}&authMechanismProperties=AWS_SESSION_TOKEN:${SESSION_TOKEN}" fi +if [ "$ASSERT_NO_URI_CREDS" = "true" ]; then + if echo "$MONGODB_URI" | grep -q "@"; then + echo "MONGODB_URI unexpectedly contains user credentials!"; + exit 1 + fi +fi + export MONGODB_URI export AWS_TESTS_ENABLED=true # show test output set -x -powershell.exe .\\build.ps1 --target TestAwsAuthentication +if [[ "$OS" =~ Windows|windows ]]; then + powershell.exe .\\build.ps1 --target=TestAwsAuthentication +else + ./build.sh --target=TestAwsAuthentication +fi \ No newline at end of file diff --git a/evergreen/run-serverless-tests.sh b/evergreen/run-serverless-tests.sh index d5d90252043..101dd3452d5 100644 --- a/evergreen/run-serverless-tests.sh +++ b/evergreen/run-serverless-tests.sh @@ -11,6 +11,7 @@ set -o errexit # Exit the script with error if any of the commands fail # SERVERLESS_ATLAS_PASSWORD Authentiction password, must be set # SERVERLESS_URI Single atlas proxy serverless uri, must be set # SSL TLS connection flag, must be "ssl" +# CRYPT_SHARED_LIB_PATH The path to crypt_shared library # Modified/exported environment variables: # MONGODB_URI MONGODB_URI for single host with auth details and TLS and compressor parameters # MONGODB_URI_WITH_MULTIPLE_MONGOSES MONGODB_URI with auth details and TLS and compressor parameters @@ -20,6 +21,8 @@ set -o errexit # Exit the script with error if any of the commands fail # Main Program # ############################################ +echo "CRYPT_SHARED_LIB_PATH: ${CRYPT_SHARED_LIB_PATH}" + if [[ "$AUTH" != "auth" ]]; then echo "Serverless tests require AUTH to be enabled" exit 1 diff --git a/evergreen/run-tests.sh b/evergreen/run-tests.sh index 2c6927f0155..6ff7643df24 100755 --- a/evergreen/run-tests.sh +++ b/evergreen/run-tests.sh @@ -69,19 +69,6 @@ provision_compressor () { # Main Program # ############################################ echo "CRYPT_SHARED_LIB_PATH:" $CRYPT_SHARED_LIB_PATH - -if [ "$TARGET" == "TestCsfleWithMongocryptd" ]; then - if [ ! -z "${CRYPT_SHARED_LIB_PATH}" ]; then - echo "CRYPT_SHARED_LIB_PATH must be unassigned for CSFLE tests with mongocryptd, but was ${CRYPT_SHARED_LIB_PATH}" 1>&2 # write to stderr - exit 1 - fi -else - if [ -z "${CRYPT_SHARED_LIB_PATH}" ]; then - echo "CRYPT_SHARED_LIB_PATH must be assigned, but wasn't" 1>&2 # write to stderr" - exit 1 - fi -fi - echo "Initial MongoDB URI:" $MONGODB_URI echo "Framework: " $FRAMEWORK diff --git a/evergreen/set-temp-fle-aws-creds.sh b/evergreen/set-temp-fle-aws-creds.sh index 53a39c5ea5f..35f36335978 100644 --- a/evergreen/set-temp-fle-aws-creds.sh +++ b/evergreen/set-temp-fle-aws-creds.sh @@ -29,10 +29,11 @@ echo "Triggering temporary CSFLE credentials" get_creds() { $PYTHON - "$@" << 'EOF' +import sys import boto3 client = boto3.client("sts") credentials = client.get_session_token()["Credentials"] -print (credentials["AccessKeyId"] + " " + credentials["SecretAccessKey"] + " " + credentials["SessionToken"]) +sys.stdout.write(credentials["AccessKeyId"] + " " + credentials["SecretAccessKey"] + " " + credentials["SessionToken"]) EOF } diff --git a/tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake/tests/README.rst b/specifications/atlas-data-lake-testing/tests/README.rst similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake/tests/README.rst rename to specifications/atlas-data-lake-testing/tests/README.rst diff --git a/tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake/tests/aggregate.json b/specifications/atlas-data-lake-testing/tests/aggregate.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake/tests/aggregate.json rename to specifications/atlas-data-lake-testing/tests/aggregate.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake/tests/aggregate.yml b/specifications/atlas-data-lake-testing/tests/aggregate.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake/tests/aggregate.yml rename to specifications/atlas-data-lake-testing/tests/aggregate.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake/tests/estimatedDocumentCount.json b/specifications/atlas-data-lake-testing/tests/estimatedDocumentCount.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake/tests/estimatedDocumentCount.json rename to specifications/atlas-data-lake-testing/tests/estimatedDocumentCount.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake/tests/estimatedDocumentCount.yml b/specifications/atlas-data-lake-testing/tests/estimatedDocumentCount.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake/tests/estimatedDocumentCount.yml rename to specifications/atlas-data-lake-testing/tests/estimatedDocumentCount.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake/tests/find.json b/specifications/atlas-data-lake-testing/tests/find.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake/tests/find.json rename to specifications/atlas-data-lake-testing/tests/find.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake/tests/find.yml b/specifications/atlas-data-lake-testing/tests/find.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake/tests/find.yml rename to specifications/atlas-data-lake-testing/tests/find.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake/tests/getMore.json b/specifications/atlas-data-lake-testing/tests/getMore.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake/tests/getMore.json rename to specifications/atlas-data-lake-testing/tests/getMore.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake/tests/getMore.yml b/specifications/atlas-data-lake-testing/tests/getMore.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake/tests/getMore.yml rename to specifications/atlas-data-lake-testing/tests/getMore.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake/tests/listCollections.json b/specifications/atlas-data-lake-testing/tests/listCollections.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake/tests/listCollections.json rename to specifications/atlas-data-lake-testing/tests/listCollections.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake/tests/listCollections.yml b/specifications/atlas-data-lake-testing/tests/listCollections.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake/tests/listCollections.yml rename to specifications/atlas-data-lake-testing/tests/listCollections.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake/tests/listDatabases.json b/specifications/atlas-data-lake-testing/tests/listDatabases.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake/tests/listDatabases.json rename to specifications/atlas-data-lake-testing/tests/listDatabases.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake/tests/listDatabases.yml b/specifications/atlas-data-lake-testing/tests/listDatabases.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake/tests/listDatabases.yml rename to specifications/atlas-data-lake-testing/tests/listDatabases.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake/tests/runCommand.json b/specifications/atlas-data-lake-testing/tests/runCommand.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake/tests/runCommand.json rename to specifications/atlas-data-lake-testing/tests/runCommand.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake/tests/runCommand.yml b/specifications/atlas-data-lake-testing/tests/runCommand.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake/tests/runCommand.yml rename to specifications/atlas-data-lake-testing/tests/runCommand.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/auth/tests/README.rst b/specifications/auth/tests/README.rst similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/auth/tests/README.rst rename to specifications/auth/tests/README.rst diff --git a/tests/MongoDB.Driver.Tests/Specifications/auth/tests/connection-string.json b/specifications/auth/tests/connection-string.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/auth/tests/connection-string.json rename to specifications/auth/tests/connection-string.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/auth/tests/connection-string.yml b/specifications/auth/tests/connection-string.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/auth/tests/connection-string.yml rename to specifications/auth/tests/connection-string.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/auth/tests/mongodb-aws.rst b/specifications/auth/tests/mongodb-aws.rst similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/auth/tests/mongodb-aws.rst rename to specifications/auth/tests/mongodb-aws.rst diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/array.json b/specifications/bson-corpus/tests/array.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/array.json rename to specifications/bson-corpus/tests/array.json diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/binary.json b/specifications/bson-corpus/tests/binary.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/binary.json rename to specifications/bson-corpus/tests/binary.json diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/boolean.json b/specifications/bson-corpus/tests/boolean.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/boolean.json rename to specifications/bson-corpus/tests/boolean.json diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/bsonview b/specifications/bson-corpus/tests/bsonview similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/bsonview rename to specifications/bson-corpus/tests/bsonview diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/code.json b/specifications/bson-corpus/tests/code.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/code.json rename to specifications/bson-corpus/tests/code.json diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/code_w_scope.json b/specifications/bson-corpus/tests/code_w_scope.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/code_w_scope.json rename to specifications/bson-corpus/tests/code_w_scope.json diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/datetime.json b/specifications/bson-corpus/tests/datetime.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/datetime.json rename to specifications/bson-corpus/tests/datetime.json diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/dbpointer.json b/specifications/bson-corpus/tests/dbpointer.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/dbpointer.json rename to specifications/bson-corpus/tests/dbpointer.json diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/dbref.json b/specifications/bson-corpus/tests/dbref.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/dbref.json rename to specifications/bson-corpus/tests/dbref.json diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/decimal128-1.json b/specifications/bson-corpus/tests/decimal128-1.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/decimal128-1.json rename to specifications/bson-corpus/tests/decimal128-1.json diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/decimal128-2.json b/specifications/bson-corpus/tests/decimal128-2.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/decimal128-2.json rename to specifications/bson-corpus/tests/decimal128-2.json diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/decimal128-3.json b/specifications/bson-corpus/tests/decimal128-3.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/decimal128-3.json rename to specifications/bson-corpus/tests/decimal128-3.json diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/decimal128-4.json b/specifications/bson-corpus/tests/decimal128-4.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/decimal128-4.json rename to specifications/bson-corpus/tests/decimal128-4.json diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/decimal128-5.json b/specifications/bson-corpus/tests/decimal128-5.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/decimal128-5.json rename to specifications/bson-corpus/tests/decimal128-5.json diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/decimal128-6.json b/specifications/bson-corpus/tests/decimal128-6.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/decimal128-6.json rename to specifications/bson-corpus/tests/decimal128-6.json diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/decimal128-7.json b/specifications/bson-corpus/tests/decimal128-7.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/decimal128-7.json rename to specifications/bson-corpus/tests/decimal128-7.json diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/document.json b/specifications/bson-corpus/tests/document.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/document.json rename to specifications/bson-corpus/tests/document.json diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/double.json b/specifications/bson-corpus/tests/double.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/double.json rename to specifications/bson-corpus/tests/double.json diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/int32.json b/specifications/bson-corpus/tests/int32.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/int32.json rename to specifications/bson-corpus/tests/int32.json diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/int64.json b/specifications/bson-corpus/tests/int64.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/int64.json rename to specifications/bson-corpus/tests/int64.json diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/maxkey.json b/specifications/bson-corpus/tests/maxkey.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/maxkey.json rename to specifications/bson-corpus/tests/maxkey.json diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/minkey.json b/specifications/bson-corpus/tests/minkey.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/minkey.json rename to specifications/bson-corpus/tests/minkey.json diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/multi-type-deprecated.json b/specifications/bson-corpus/tests/multi-type-deprecated.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/multi-type-deprecated.json rename to specifications/bson-corpus/tests/multi-type-deprecated.json diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/multi-type.json b/specifications/bson-corpus/tests/multi-type.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/multi-type.json rename to specifications/bson-corpus/tests/multi-type.json diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/null.json b/specifications/bson-corpus/tests/null.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/null.json rename to specifications/bson-corpus/tests/null.json diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/oid.json b/specifications/bson-corpus/tests/oid.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/oid.json rename to specifications/bson-corpus/tests/oid.json diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/regex.json b/specifications/bson-corpus/tests/regex.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/regex.json rename to specifications/bson-corpus/tests/regex.json diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/string.json b/specifications/bson-corpus/tests/string.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/string.json rename to specifications/bson-corpus/tests/string.json diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/symbol.json b/specifications/bson-corpus/tests/symbol.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/symbol.json rename to specifications/bson-corpus/tests/symbol.json diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/timestamp.json b/specifications/bson-corpus/tests/timestamp.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/timestamp.json rename to specifications/bson-corpus/tests/timestamp.json diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/top.json b/specifications/bson-corpus/tests/top.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/top.json rename to specifications/bson-corpus/tests/top.json diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/undefined.json b/specifications/bson-corpus/tests/undefined.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson-corpus/tests/undefined.json rename to specifications/bson-corpus/tests/undefined.json diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson/tests/README.md b/specifications/bson-decimal128/tests/README.md similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson/tests/README.md rename to specifications/bson-decimal128/tests/README.md diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson/tests/decimal128-1.json b/specifications/bson-decimal128/tests/decimal128-1.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson/tests/decimal128-1.json rename to specifications/bson-decimal128/tests/decimal128-1.json diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson/tests/decimal128-2.json b/specifications/bson-decimal128/tests/decimal128-2.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson/tests/decimal128-2.json rename to specifications/bson-decimal128/tests/decimal128-2.json diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson/tests/decimal128-3.json b/specifications/bson-decimal128/tests/decimal128-3.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson/tests/decimal128-3.json rename to specifications/bson-decimal128/tests/decimal128-3.json diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson/tests/decimal128-4.json b/specifications/bson-decimal128/tests/decimal128-4.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson/tests/decimal128-4.json rename to specifications/bson-decimal128/tests/decimal128-4.json diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson/tests/decimal128-5.json b/specifications/bson-decimal128/tests/decimal128-5.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson/tests/decimal128-5.json rename to specifications/bson-decimal128/tests/decimal128-5.json diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson/tests/decimal128-6.json b/specifications/bson-decimal128/tests/decimal128-6.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson/tests/decimal128-6.json rename to specifications/bson-decimal128/tests/decimal128-6.json diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson/tests/decimal128-7.json b/specifications/bson-decimal128/tests/decimal128-7.json similarity index 100% rename from tests/MongoDB.Bson.Tests/Specifications/bson/tests/decimal128-7.json rename to specifications/bson-decimal128/tests/decimal128-7.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/change-streams/tests/README.rst b/specifications/change-streams/tests/README.rst similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/change-streams/tests/README.rst rename to specifications/change-streams/tests/README.rst diff --git a/specifications/change-streams/tests/unified/change-streams-clusterTime.json b/specifications/change-streams/tests/unified/change-streams-clusterTime.json new file mode 100644 index 00000000000..55b4ae3fbcb --- /dev/null +++ b/specifications/change-streams/tests/unified/change-streams-clusterTime.json @@ -0,0 +1,82 @@ +{ + "description": "change-streams-clusterTime", + "schemaVersion": "1.4", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "collection0" + } + } + ], + "runOnRequirements": [ + { + "minServerVersion": "4.0.0", + "topologies": [ + "replicaset", + "sharded-replicaset", + "load-balanced", + "sharded" + ], + "serverless": "forbid" + } + ], + "initialData": [ + { + "collectionName": "collection0", + "databaseName": "database0", + "documents": [] + } + ], + "tests": [ + { + "description": "clusterTime is present", + "operations": [ + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "ns": { + "db": "database0", + "coll": "collection0" + }, + "clusterTime": { + "$$exists": true + } + } + } + ] + } + ] +} diff --git a/specifications/change-streams/tests/unified/change-streams-clusterTime.yml b/specifications/change-streams/tests/unified/change-streams-clusterTime.yml new file mode 100644 index 00000000000..997d4d57618 --- /dev/null +++ b/specifications/change-streams/tests/unified/change-streams-clusterTime.yml @@ -0,0 +1,41 @@ +description: "change-streams-clusterTime" +schemaVersion: "1.4" +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + - database: + id: &database0 database0 + client: *client0 + databaseName: *database0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: *collection0 + +runOnRequirements: + - minServerVersion: "4.0.0" + topologies: [ replicaset, sharded-replicaset, load-balanced, sharded ] + serverless: forbid + +initialData: + - collectionName: *collection0 + databaseName: *database0 + documents: [] + +tests: + - description: "clusterTime is present" + operations: + - name: createChangeStream + object: *collection0 + arguments: { pipeline: [] } + saveResultAsEntity: &changeStream0 changeStream0 + - name: insertOne + object: *collection0 + arguments: + document: { _id: 1 } + - name: iterateUntilDocumentOrError + object: *changeStream0 + expectResult: + ns: { db: *database0, coll: *collection0 } + clusterTime: { $$exists: true } diff --git a/specifications/change-streams/tests/unified/change-streams-disambiguatedPaths.json b/specifications/change-streams/tests/unified/change-streams-disambiguatedPaths.json new file mode 100644 index 00000000000..91d8e66da20 --- /dev/null +++ b/specifications/change-streams/tests/unified/change-streams-disambiguatedPaths.json @@ -0,0 +1,252 @@ +{ + "description": "disambiguatedPaths", + "schemaVersion": "1.4", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "collection0" + } + } + ], + "runOnRequirements": [ + { + "minServerVersion": "6.1.0", + "topologies": [ + "replicaset", + "sharded-replicaset", + "load-balanced", + "sharded" + ], + "serverless": "forbid" + } + ], + "initialData": [ + { + "collectionName": "collection0", + "databaseName": "database0", + "documents": [] + } + ], + "tests": [ + { + "description": "disambiguatedPaths is not present when showExpandedEvents is false/unset", + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1, + "a": { + "1": 1 + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "a.1": 2 + } + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "update", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "updateDescription": { + "updatedFields": { + "$$exists": true + }, + "removedFields": { + "$$exists": true + }, + "truncatedArrays": { + "$$exists": true + }, + "disambiguatedPaths": { + "$$exists": false + } + } + } + } + ] + }, + { + "description": "disambiguatedPaths is present on updateDescription when an ambiguous path is present", + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1, + "a": { + "1": 1 + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [], + "showExpandedEvents": true + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "a.1": 2 + } + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "update", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "updateDescription": { + "updatedFields": { + "$$exists": true + }, + "removedFields": { + "$$exists": true + }, + "truncatedArrays": { + "$$exists": true + }, + "disambiguatedPaths": { + "a.1": [ + "a", + "1" + ] + } + } + } + } + ] + }, + { + "description": "disambiguatedPaths returns array indices as integers", + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1, + "a": [ + { + "1": 1 + } + ] + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [], + "showExpandedEvents": true + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "a.0.1": 2 + } + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "update", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "updateDescription": { + "updatedFields": { + "$$exists": true + }, + "removedFields": { + "$$exists": true + }, + "truncatedArrays": { + "$$exists": true + }, + "disambiguatedPaths": { + "a.0.1": [ + "a", + { + "$$type": "int" + }, + "1" + ] + } + } + } + } + ] + } + ] +} diff --git a/specifications/change-streams/tests/unified/change-streams-disambiguatedPaths.yml b/specifications/change-streams/tests/unified/change-streams-disambiguatedPaths.yml new file mode 100644 index 00000000000..a2524d23cee --- /dev/null +++ b/specifications/change-streams/tests/unified/change-streams-disambiguatedPaths.yml @@ -0,0 +1,103 @@ +description: "disambiguatedPaths" +schemaVersion: "1.4" +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + - database: + id: &database0 database0 + client: *client0 + databaseName: *database0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: *collection0 + +runOnRequirements: + - minServerVersion: "6.1.0" + topologies: [ replicaset, sharded-replicaset, load-balanced, sharded ] + serverless: forbid + +initialData: + - collectionName: *collection0 + databaseName: *database0 + documents: [] + +tests: + - description: "disambiguatedPaths is not present when showExpandedEvents is false/unset" + operations: + - name: insertOne + object: *collection0 + arguments: + document: { _id: 1, 'a': { '1': 1 } } + - name: createChangeStream + object: *collection0 + arguments: { pipeline: [] } + saveResultAsEntity: &changeStream0 changeStream0 + - name: updateOne + object: *collection0 + arguments: + filter: { _id: 1 } + update: { $set: { 'a.1': 2 } } + - name: iterateUntilDocumentOrError + object: *changeStream0 + expectResult: + operationType: "update" + ns: { db: *database0, coll: *collection0 } + updateDescription: + updatedFields: { $$exists: true } + removedFields: { $$exists: true } + truncatedArrays: { $$exists: true } + disambiguatedPaths: { $$exists: false } + + - description: "disambiguatedPaths is present on updateDescription when an ambiguous path is present" + operations: + - name: insertOne + object: *collection0 + arguments: + document: { _id: 1, 'a': { '1': 1 } } + - name: createChangeStream + object: *collection0 + arguments: { pipeline: [], showExpandedEvents: true } + saveResultAsEntity: &changeStream0 changeStream0 + - name: updateOne + object: *collection0 + arguments: + filter: { _id: 1 } + update: { $set: { 'a.1': 2 } } + - name: iterateUntilDocumentOrError + object: *changeStream0 + expectResult: + operationType: "update" + ns: { db: *database0, coll: *collection0 } + updateDescription: + updatedFields: { $$exists: true } + removedFields: { $$exists: true } + truncatedArrays: { $$exists: true } + disambiguatedPaths: { 'a.1': ['a', '1'] } + + - description: "disambiguatedPaths returns array indices as integers" + operations: + - name: insertOne + object: *collection0 + arguments: + document: { _id: 1, 'a': [{'1': 1 }] } + - name: createChangeStream + object: *collection0 + arguments: { pipeline: [], showExpandedEvents: true } + saveResultAsEntity: &changeStream0 changeStream0 + - name: updateOne + object: *collection0 + arguments: + filter: { _id: 1 } + update: { $set: { 'a.0.1': 2 } } + - name: iterateUntilDocumentOrError + object: *changeStream0 + expectResult: + operationType: "update" + ns: { db: *database0, coll: *collection0 } + updateDescription: + updatedFields: { $$exists: true } + removedFields: { $$exists: true } + truncatedArrays: { $$exists: true } + disambiguatedPaths: { 'a.0.1': ['a', { $$type: 'int' }, '1'] } diff --git a/tests/MongoDB.Driver.Tests/Specifications/change-streams/tests/unified/change-streams-errors.json b/specifications/change-streams/tests/unified/change-streams-errors.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/change-streams/tests/unified/change-streams-errors.json rename to specifications/change-streams/tests/unified/change-streams-errors.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/change-streams/tests/unified/change-streams-errors.yml b/specifications/change-streams/tests/unified/change-streams-errors.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/change-streams/tests/unified/change-streams-errors.yml rename to specifications/change-streams/tests/unified/change-streams-errors.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/change-streams/tests/unified/change-streams-pre_and_post_images.json b/specifications/change-streams/tests/unified/change-streams-pre_and_post_images.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/change-streams/tests/unified/change-streams-pre_and_post_images.json rename to specifications/change-streams/tests/unified/change-streams-pre_and_post_images.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/change-streams/tests/unified/change-streams-pre_and_post_images.yml b/specifications/change-streams/tests/unified/change-streams-pre_and_post_images.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/change-streams/tests/unified/change-streams-pre_and_post_images.yml rename to specifications/change-streams/tests/unified/change-streams-pre_and_post_images.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/change-streams/tests/unified/change-streams-resume-allowlist.json b/specifications/change-streams/tests/unified/change-streams-resume-allowlist.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/change-streams/tests/unified/change-streams-resume-allowlist.json rename to specifications/change-streams/tests/unified/change-streams-resume-allowlist.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/change-streams/tests/unified/change-streams-resume-allowlist.yml b/specifications/change-streams/tests/unified/change-streams-resume-allowlist.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/change-streams/tests/unified/change-streams-resume-allowlist.yml rename to specifications/change-streams/tests/unified/change-streams-resume-allowlist.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/change-streams/tests/unified/change-streams-resume-errorLabels.json b/specifications/change-streams/tests/unified/change-streams-resume-errorLabels.json similarity index 99% rename from tests/MongoDB.Driver.Tests/Specifications/change-streams/tests/unified/change-streams-resume-errorLabels.json rename to specifications/change-streams/tests/unified/change-streams-resume-errorLabels.json index c156b550ce9..f5f4505a9f9 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/change-streams/tests/unified/change-streams-resume-errorLabels.json +++ b/specifications/change-streams/tests/unified/change-streams-resume-errorLabels.json @@ -1478,6 +1478,11 @@ }, { "description": "change stream resumes after StaleShardVersion", + "runOnRequirements": [ + { + "maxServerVersion": "6.0.99" + } + ], "operations": [ { "name": "failPoint", diff --git a/tests/MongoDB.Driver.Tests/Specifications/change-streams/tests/unified/change-streams-resume-errorLabels.yml b/specifications/change-streams/tests/unified/change-streams-resume-errorLabels.yml similarity index 99% rename from tests/MongoDB.Driver.Tests/Specifications/change-streams/tests/unified/change-streams-resume-errorLabels.yml rename to specifications/change-streams/tests/unified/change-streams-resume-errorLabels.yml index 93374598b1c..5879b59c6ac 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/change-streams/tests/unified/change-streams-resume-errorLabels.yml +++ b/specifications/change-streams/tests/unified/change-streams-resume-errorLabels.yml @@ -743,6 +743,9 @@ tests: databaseName: *database0 - description: change stream resumes after StaleShardVersion + runOnRequirements: + # StaleShardVersion is obsolete as of 6.1 and is no longer marked as resumable. + - maxServerVersion: "6.0.99" operations: - name: failPoint object: testRunner diff --git a/tests/MongoDB.Driver.Tests/Specifications/change-streams/tests/unified/change-streams-showExpandedEvents.json b/specifications/change-streams/tests/unified/change-streams-showExpandedEvents.json similarity index 98% rename from tests/MongoDB.Driver.Tests/Specifications/change-streams/tests/unified/change-streams-showExpandedEvents.json rename to specifications/change-streams/tests/unified/change-streams-showExpandedEvents.json index fe852b5443a..a59a818493c 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/change-streams/tests/unified/change-streams-showExpandedEvents.json +++ b/specifications/change-streams/tests/unified/change-streams-showExpandedEvents.json @@ -8,7 +8,8 @@ "replicaset", "sharded-replicaset", "sharded" - ] + ], + "serverless": "forbid" } ], "createEntities": [ @@ -275,7 +276,15 @@ "name": "createChangeStream", "object": "collection0", "arguments": { - "pipeline": [], + "pipeline": [ + { + "$match": { + "operationType": { + "$ne": "create" + } + } + } + ], "showExpandedEvents": true }, "saveResultAsEntity": "changeStream0" diff --git a/tests/MongoDB.Driver.Tests/Specifications/change-streams/tests/unified/change-streams-showExpandedEvents.yml b/specifications/change-streams/tests/unified/change-streams-showExpandedEvents.yml similarity index 94% rename from tests/MongoDB.Driver.Tests/Specifications/change-streams/tests/unified/change-streams-showExpandedEvents.yml rename to specifications/change-streams/tests/unified/change-streams-showExpandedEvents.yml index 002e17ed330..7a15ba4b54c 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/change-streams/tests/unified/change-streams-showExpandedEvents.yml +++ b/specifications/change-streams/tests/unified/change-streams-showExpandedEvents.yml @@ -3,6 +3,7 @@ schemaVersion: "1.7" runOnRequirements: - minServerVersion: "6.0.0" topologies: [ replicaset, sharded-replicaset, sharded ] + serverless: forbid createEntities: - client: id: &client0 client0 @@ -160,7 +161,15 @@ tests: - name: createChangeStream object: *collection0 arguments: - pipeline: [] + pipeline: + # On sharded clusters, the create command run when loading initial + # data sometimes is still reported in the change stream. To avoid + # this, we exclude the create command when creating the change + # stream, but specifically don't exclude other events to still catch + # driver errors. + - $match: + operationType: + $ne: create showExpandedEvents: true saveResultAsEntity: &changeStream0 changeStream0 - name: createIndex diff --git a/tests/MongoDB.Driver.Tests/Specifications/change-streams/tests/unified/change-streams.json b/specifications/change-streams/tests/unified/change-streams.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/change-streams/tests/unified/change-streams.json rename to specifications/change-streams/tests/unified/change-streams.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/change-streams/tests/unified/change-streams.yml b/specifications/change-streams/tests/unified/change-streams.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/change-streams/tests/unified/change-streams.yml rename to specifications/change-streams/tests/unified/change-streams.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/corpus/corpus-encrypted.json b/specifications/client-side-encryption/prose-tests/corpus/corpus-encrypted.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/corpus/corpus-encrypted.json rename to specifications/client-side-encryption/prose-tests/corpus/corpus-encrypted.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/corpus/corpus-key-aws.json b/specifications/client-side-encryption/prose-tests/corpus/corpus-key-aws.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/corpus/corpus-key-aws.json rename to specifications/client-side-encryption/prose-tests/corpus/corpus-key-aws.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/corpus/corpus-key-azure.json b/specifications/client-side-encryption/prose-tests/corpus/corpus-key-azure.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/corpus/corpus-key-azure.json rename to specifications/client-side-encryption/prose-tests/corpus/corpus-key-azure.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/corpus/corpus-key-gcp.json b/specifications/client-side-encryption/prose-tests/corpus/corpus-key-gcp.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/corpus/corpus-key-gcp.json rename to specifications/client-side-encryption/prose-tests/corpus/corpus-key-gcp.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/corpus/corpus-key-kmip.json b/specifications/client-side-encryption/prose-tests/corpus/corpus-key-kmip.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/corpus/corpus-key-kmip.json rename to specifications/client-side-encryption/prose-tests/corpus/corpus-key-kmip.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/corpus/corpus-key-local.json b/specifications/client-side-encryption/prose-tests/corpus/corpus-key-local.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/corpus/corpus-key-local.json rename to specifications/client-side-encryption/prose-tests/corpus/corpus-key-local.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/corpus/corpus-schema.json b/specifications/client-side-encryption/prose-tests/corpus/corpus-schema.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/corpus/corpus-schema.json rename to specifications/client-side-encryption/prose-tests/corpus/corpus-schema.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/corpus/corpus.json b/specifications/client-side-encryption/prose-tests/corpus/corpus.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/corpus/corpus.json rename to specifications/client-side-encryption/prose-tests/corpus/corpus.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/etc/data/encryptedFields.json b/specifications/client-side-encryption/prose-tests/etc/data/encryptedFields.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/etc/data/encryptedFields.json rename to specifications/client-side-encryption/prose-tests/etc/data/encryptedFields.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/etc/data/keys/key1-document.json b/specifications/client-side-encryption/prose-tests/etc/data/keys/key1-document.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/etc/data/keys/key1-document.json rename to specifications/client-side-encryption/prose-tests/etc/data/keys/key1-document.json diff --git a/specifications/client-side-encryption/prose-tests/etc/data/range-encryptedFields-Date.json b/specifications/client-side-encryption/prose-tests/etc/data/range-encryptedFields-Date.json new file mode 100644 index 00000000000..e19fc1e1826 --- /dev/null +++ b/specifications/client-side-encryption/prose-tests/etc/data/range-encryptedFields-Date.json @@ -0,0 +1,30 @@ +{ + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] +} diff --git a/specifications/client-side-encryption/prose-tests/etc/data/range-encryptedFields-DecimalNoPrecision.json b/specifications/client-side-encryption/prose-tests/etc/data/range-encryptedFields-DecimalNoPrecision.json new file mode 100644 index 00000000000..c6d129d4ca1 --- /dev/null +++ b/specifications/client-side-encryption/prose-tests/etc/data/range-encryptedFields-DecimalNoPrecision.json @@ -0,0 +1,21 @@ +{ + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalNoPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "sparsity": { + "$numberInt": "1" + } + } + } + ] + } + \ No newline at end of file diff --git a/specifications/client-side-encryption/prose-tests/etc/data/range-encryptedFields-DecimalPrecision.json b/specifications/client-side-encryption/prose-tests/etc/data/range-encryptedFields-DecimalPrecision.json new file mode 100644 index 00000000000..c23c3fa923c --- /dev/null +++ b/specifications/client-side-encryption/prose-tests/etc/data/range-encryptedFields-DecimalPrecision.json @@ -0,0 +1,29 @@ +{ + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "sparsity": { + "$numberInt": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] +} diff --git a/specifications/client-side-encryption/prose-tests/etc/data/range-encryptedFields-DoubleNoPrecision.json b/specifications/client-side-encryption/prose-tests/etc/data/range-encryptedFields-DoubleNoPrecision.json new file mode 100644 index 00000000000..4af6422714b --- /dev/null +++ b/specifications/client-side-encryption/prose-tests/etc/data/range-encryptedFields-DoubleNoPrecision.json @@ -0,0 +1,21 @@ +{ + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoubleNoPrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + \ No newline at end of file diff --git a/specifications/client-side-encryption/prose-tests/etc/data/range-encryptedFields-DoublePrecision.json b/specifications/client-side-encryption/prose-tests/etc/data/range-encryptedFields-DoublePrecision.json new file mode 100644 index 00000000000..c1f388219db --- /dev/null +++ b/specifications/client-side-encryption/prose-tests/etc/data/range-encryptedFields-DoublePrecision.json @@ -0,0 +1,30 @@ +{ + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + \ No newline at end of file diff --git a/specifications/client-side-encryption/prose-tests/etc/data/range-encryptedFields-Int.json b/specifications/client-side-encryption/prose-tests/etc/data/range-encryptedFields-Int.json new file mode 100644 index 00000000000..217bf6743c8 --- /dev/null +++ b/specifications/client-side-encryption/prose-tests/etc/data/range-encryptedFields-Int.json @@ -0,0 +1,27 @@ +{ + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + \ No newline at end of file diff --git a/specifications/client-side-encryption/prose-tests/etc/data/range-encryptedFields-Long.json b/specifications/client-side-encryption/prose-tests/etc/data/range-encryptedFields-Long.json new file mode 100644 index 00000000000..0fb87edaeff --- /dev/null +++ b/specifications/client-side-encryption/prose-tests/etc/data/range-encryptedFields-Long.json @@ -0,0 +1,27 @@ +{ + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + \ No newline at end of file diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/external/external-key.json b/specifications/client-side-encryption/prose-tests/external/external-key.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/external/external-key.json rename to specifications/client-side-encryption/prose-tests/external/external-key.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/external/external-schema.json b/specifications/client-side-encryption/prose-tests/external/external-schema.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/external/external-schema.json rename to specifications/client-side-encryption/prose-tests/external/external-schema.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/limits/limits-doc.json b/specifications/client-side-encryption/prose-tests/limits/limits-doc.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/limits/limits-doc.json rename to specifications/client-side-encryption/prose-tests/limits/limits-doc.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/limits/limits-key.json b/specifications/client-side-encryption/prose-tests/limits/limits-key.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/limits/limits-key.json rename to specifications/client-side-encryption/prose-tests/limits/limits-key.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/limits/limits-schema.json b/specifications/client-side-encryption/prose-tests/limits/limits-schema.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/limits/limits-schema.json rename to specifications/client-side-encryption/prose-tests/limits/limits-schema.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/README.rst b/specifications/client-side-encryption/tests/legacy/README.rst similarity index 89% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/README.rst rename to specifications/client-side-encryption/tests/legacy/README.rst index c22f71f876f..b2a48f9fda7 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/README.rst +++ b/specifications/client-side-encryption/tests/legacy/README.rst @@ -330,6 +330,11 @@ Using ``crypt_shared`` On platforms where crypt_shared_ is available, drivers should prefer to test with the ``crypt_shared`` library instead of spawning mongocryptd. +crypt_shared_ is released alongside the server. +crypt_shared_ is only available in versions 6.0 and above. +Drivers SHOULD prefer testing a version of crypt_shared_ that matches the server version being tested. +Driver tests on server versions less than 6.0 SHOULD use mongocryptd. + Drivers MUST continue to run all tests with mongocryptd on at least one platform for all tested server versions. @@ -1656,6 +1661,7 @@ Use ``clientEncryption`` to encrypt the value "encrypted indexed value" with the class EncryptOpts { keyId : algorithm: "Indexed", + contentionFactor: 0 } Store the result in ``insertPayload``. @@ -1669,7 +1675,8 @@ Use ``clientEncryption`` to encrypt the value "encrypted indexed value" with the class EncryptOpts { keyId : algorithm: "Indexed", - queryType: Equality + queryType: "equality", + contentionFactor: 0 } Store the result in ``findPayload``. @@ -1704,7 +1711,8 @@ Use ``clientEncryption`` to encrypt the value "encrypted indexed value" with the class EncryptOpts { keyId : algorithm: "Indexed", - queryType: Equality + queryType: "equality", + contentionFactor: 0 } Store the result in ``findPayload``. @@ -1720,7 +1728,7 @@ Use ``clientEncryption`` to encrypt the value "encrypted indexed value" with the class EncryptOpts { keyId : algorithm: "Indexed", - queryType: Equality, + queryType: "equality", contentionFactor: 10 } @@ -1760,6 +1768,7 @@ Use ``clientEncryption`` to encrypt the value "encrypted indexed value" with the class EncryptOpts { keyId : algorithm: "Indexed", + contentionFactor: 0 } Store the result in ``payload``. @@ -1785,6 +1794,8 @@ Use ``clientEncryption`` to decrypt ``payload``. Assert the returned value equal 13. Unique Index on keyAltNames ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The following setup must occur before running each of the following test cases. + Setup ````` @@ -1817,8 +1828,8 @@ The command should be equivalent to: 5. Using ``client_encryption``, create a data key with a ``local`` KMS provider and the keyAltName "def". -Case 1: createKey() -``````````````````` +Case 1: createDataKey() +``````````````````````` 1. Use ``client_encryption`` to create a new local data key with a keyAltName "abc" and assert the operation does not fail. @@ -1833,8 +1844,273 @@ Case 2: addKeyAltName() 2. Use ``client_encryption`` to add a keyAltName "abc" to the key created in Step 1 and assert the operation does not fail. -3. Repeat Step 2 and assert the operation does not fail. +3. Repeat Step 2, assert the operation does not fail, and assert the returned key document contains the keyAltName "abc" added in Step 2. 4. Use ``client_encryption`` to add a keyAltName "def" to the key created in Step 1 and assert the operation fails due to a duplicate key server error (error code 11000). -5. Use ``client_encryption`` to add a keyAltName "def" to the existing key and assert the operation does not fail. +5. Use ``client_encryption`` to add a keyAltName "def" to the existing key, assert the operation does not fail, and assert the returned key document contains the keyAltName "def" added during Setup. + +14. Decryption Events +~~~~~~~~~~~~~~~~~~~~~ + +Before running each of the following test cases, perform the following Test Setup. + +Test Setup +`````````` + +Create a MongoClient named ``setupClient``. + +Drop and create the collection ``db.decryption_events``. + +Create a ClientEncryption object named ``clientEncryption`` with these options: + +.. code:: typescript + + ClientEncryptionOpts { + keyVaultClient: , + keyVaultNamespace: "keyvault.datakeys", + kmsProviders: { "local": { "key": } } + } + +Create a data key with the "local" KMS provider. Storing the result in a variable named ``keyID``. + +Use ``clientEncryption`` to encrypt the string "hello" with the following ``EncryptOpts``: + +.. code:: typescript + + EncryptOpts { + keyId: , + algorithm: "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + +Store the result in a variable named ``ciphertext``. + +Copy ``ciphertext`` into a variable named ``malformedCiphertext``. Change the +last byte to a different value. This will produce an invalid HMAC tag. + +Create a MongoClient named ``encryptedClient`` with these ``AutoEncryptionOpts``: + +.. code:: typescript + + AutoEncryptionOpts { + keyVaultNamespace: "keyvault.datakeys"; + kmsProviders: { "local": { "key": } } + } + +Configure ``encryptedClient`` with "retryReads=false". +Register a listener for CommandSucceeded events on ``encryptedClient``. +The listener must store the most recent ``CommandSucceededEvent`` reply for the "aggregate" command. +The listener must store the most recent ``CommandFailedEvent`` error for the "aggregate" command. + +Case 1: Command Error +````````````````````` + +Use ``setupClient`` to configure the following failpoint: + +.. code:: typescript + + { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "errorCode": 123, + "failCommands": [ + "aggregate" + ] + } + } + +Use ``encryptedClient`` to run an aggregate on ``db.decryption_events``. + +Expect an exception to be thrown from the command error. Expect a ``CommandFailedEvent``. + +Case 2: Network Error +````````````````````` + +Use ``setupClient`` to configure the following failpoint: + +.. code:: typescript + + { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "errorCode": 123, + "closeConnection": true, + "failCommands": [ + "aggregate" + ] + } + } + +Use ``encryptedClient`` to run an aggregate on ``db.decryption_events``. + +Expect an exception to be thrown from the network error. Expect a ``CommandFailedEvent``. + +Case 3: Decrypt Error +````````````````````` + +Use ``encryptedClient`` to insert the document ``{ "encrypted": }`` into ``db.decryption_events``. + +Use ``encryptedClient`` to run an aggregate on ``db.decryption_events`` with an empty pipeline. + +Expect an exception to be thrown from the decryption error. +Expect a ``CommandSucceededEvent``. Expect the ``CommandSucceededEvent.reply`` to contain BSON binary for the field ``cursor.firstBatch.encrypted``. + +Case 4: Decrypt Success +``````````````````````` + +Use ``encryptedClient`` to insert the document ``{ "encrypted": }`` into ``db.decryption_events``. + +Use ``encryptedClient`` to run an aggregate on ``db.decryption_events`` with an empty pipeline. + +Expect no exception. +Expect a ``CommandSucceededEvent``. Expect the ``CommandSucceededEvent.reply`` to contain BSON binary for the field ``cursor.firstBatch.encrypted``. + + +15. On-demand AWS Credentials +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +These tests require valid AWS credentials. Refer: `Automatic AWS Credentials`_. + +For these cases, create a ClientEncryption_ object :math:`C` with the following +options: + +.. code-block:: typescript + + ClientEncryptionOpts { + keyVaultClient: , + keyVaultNamespace: "keyvault.datakeys", + kmsProviders: { "aws": {} }, + } + +Case 1: Failure +``````````````` + +Do not run this test case in an environment where AWS credentials are available +(e.g. via environment variables or a metadata URL). (Refer: +`Obtaining credentials for AWS `_) + +Attempt to create a datakey with :math:`C` using the ``"aws"`` KMS provider. +Expect this to fail due to a lack of KMS provider credentials. + +Case 2: Success +``````````````` + +For this test case, the environment variables ``AWS_ACCESS_KEY_ID`` and +``AWS_SECRET_ACCESS_KEY`` must be defined and set to a valid set of AWS +credentials. + +Use the client encryption to create a datakey using the ``"aws"`` KMS provider. +This should successfully load and use the AWS credentials that were defined in +the environment. + +.. _Automatic AWS Credentials: ../client-side-encryption.rst#automatic-aws-credentials +.. _ClientEncryption: ../client-side-encryption.rst#clientencryption +.. _auth-aws: ../../auth/auth.rst#obtaining-credentials + +16. Rewrap +~~~~~~~~~~ + +Case 1: Rewrap with separate ClientEncryption +````````````````````````````````````````````` + +When the following test case requests setting ``masterKey``, use the following values based on the KMS provider: + +For "aws": + +.. code:: javascript + + { + "region": "us-east-1", + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" + } + +For "azure": + +.. code:: javascript + + { + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + } + +For "gcp": + +.. code:: javascript + + { + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + } + +For "kmip": + +.. code:: javascript + + {} + +For "local", do not set a masterKey document. + +Run the following test case for each pair of KMS providers (referred to as ``srcProvider`` and ``dstProvider``). +Include pairs where ``srcProvider`` equals ``dstProvider``. + +1. Drop the collection ``keyvault.datakeys``. + +2. Create a ``ClientEncryption`` object named ``clientEncryption1`` with these options: + .. code:: typescript + + ClientEncryptionOpts { + keyVaultClient: ; + keyVaultNamespace: "keyvault.datakeys"; + kmsProviders: + } + +3. Call ``clientEncryption1.createDataKey`` with ``srcProvider`` and these options: + .. code:: typescript + + class DataKeyOpts { + masterKey: + } + + Store the return value in ``keyID``. + +4. Call ``clientEncryption1.encrypt`` with the value "test" and these options: + .. code:: typescript + + class EncryptOpts { + keyId : keyID, + algorithm: "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + + Store the return value in ``ciphertext``. + +5. Create a ``ClientEncryption`` object named ``clientEncryption2`` with these options: + .. code:: typescript + + ClientEncryptionOpts { + keyVaultClient: ; + keyVaultNamespace: "keyvault.datakeys"; + kmsProviders: + } + +6. Call ``clientEncryption2.rewrapManyDataKey`` with an empty ``filter`` and these options: + + .. code:: typescript + + class RewrapManyDataKeyOpts { + provider: dstProvider + masterKey: + } + + Assert that the returned ``RewrapManyDataKeyResult.bulkWriteResult.modifiedCount`` is 1. + +7. Call ``clientEncryption1.decrypt`` with the ``ciphertext``. Assert the return value is "test". + +8. Call ``clientEncryption2.decrypt`` with the ``ciphertext``. Assert the return value is "test". \ No newline at end of file diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/aggregate.json b/specifications/client-side-encryption/tests/legacy/aggregate.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/aggregate.json rename to specifications/client-side-encryption/tests/legacy/aggregate.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/aggregate.yml b/specifications/client-side-encryption/tests/legacy/aggregate.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/aggregate.yml rename to specifications/client-side-encryption/tests/legacy/aggregate.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/awsTemporary.json b/specifications/client-side-encryption/tests/legacy/awsTemporary.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/awsTemporary.json rename to specifications/client-side-encryption/tests/legacy/awsTemporary.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/awsTemporary.yml b/specifications/client-side-encryption/tests/legacy/awsTemporary.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/awsTemporary.yml rename to specifications/client-side-encryption/tests/legacy/awsTemporary.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/azureKMS.json b/specifications/client-side-encryption/tests/legacy/azureKMS.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/azureKMS.json rename to specifications/client-side-encryption/tests/legacy/azureKMS.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/azureKMS.yml b/specifications/client-side-encryption/tests/legacy/azureKMS.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/azureKMS.yml rename to specifications/client-side-encryption/tests/legacy/azureKMS.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/badQueries.json b/specifications/client-side-encryption/tests/legacy/badQueries.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/badQueries.json rename to specifications/client-side-encryption/tests/legacy/badQueries.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/badQueries.yml b/specifications/client-side-encryption/tests/legacy/badQueries.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/badQueries.yml rename to specifications/client-side-encryption/tests/legacy/badQueries.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/badSchema.json b/specifications/client-side-encryption/tests/legacy/badSchema.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/badSchema.json rename to specifications/client-side-encryption/tests/legacy/badSchema.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/badSchema.yml b/specifications/client-side-encryption/tests/legacy/badSchema.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/badSchema.yml rename to specifications/client-side-encryption/tests/legacy/badSchema.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/basic.json b/specifications/client-side-encryption/tests/legacy/basic.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/basic.json rename to specifications/client-side-encryption/tests/legacy/basic.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/basic.yml b/specifications/client-side-encryption/tests/legacy/basic.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/basic.yml rename to specifications/client-side-encryption/tests/legacy/basic.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/bulk.json b/specifications/client-side-encryption/tests/legacy/bulk.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/bulk.json rename to specifications/client-side-encryption/tests/legacy/bulk.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/bulk.yml b/specifications/client-side-encryption/tests/legacy/bulk.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/bulk.yml rename to specifications/client-side-encryption/tests/legacy/bulk.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/bypassAutoEncryption.json b/specifications/client-side-encryption/tests/legacy/bypassAutoEncryption.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/bypassAutoEncryption.json rename to specifications/client-side-encryption/tests/legacy/bypassAutoEncryption.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/bypassAutoEncryption.yml b/specifications/client-side-encryption/tests/legacy/bypassAutoEncryption.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/bypassAutoEncryption.yml rename to specifications/client-side-encryption/tests/legacy/bypassAutoEncryption.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/bypassedCommand.json b/specifications/client-side-encryption/tests/legacy/bypassedCommand.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/bypassedCommand.json rename to specifications/client-side-encryption/tests/legacy/bypassedCommand.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/bypassedCommand.yml b/specifications/client-side-encryption/tests/legacy/bypassedCommand.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/bypassedCommand.yml rename to specifications/client-side-encryption/tests/legacy/bypassedCommand.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/count.json b/specifications/client-side-encryption/tests/legacy/count.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/count.json rename to specifications/client-side-encryption/tests/legacy/count.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/count.yml b/specifications/client-side-encryption/tests/legacy/count.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/count.yml rename to specifications/client-side-encryption/tests/legacy/count.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/countDocuments.json b/specifications/client-side-encryption/tests/legacy/countDocuments.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/countDocuments.json rename to specifications/client-side-encryption/tests/legacy/countDocuments.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/countDocuments.yml b/specifications/client-side-encryption/tests/legacy/countDocuments.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/countDocuments.yml rename to specifications/client-side-encryption/tests/legacy/countDocuments.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/create-and-createIndexes.json b/specifications/client-side-encryption/tests/legacy/create-and-createIndexes.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/create-and-createIndexes.json rename to specifications/client-side-encryption/tests/legacy/create-and-createIndexes.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/create-and-createIndexes.yml b/specifications/client-side-encryption/tests/legacy/create-and-createIndexes.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/create-and-createIndexes.yml rename to specifications/client-side-encryption/tests/legacy/create-and-createIndexes.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/delete.json b/specifications/client-side-encryption/tests/legacy/delete.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/delete.json rename to specifications/client-side-encryption/tests/legacy/delete.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/delete.yml b/specifications/client-side-encryption/tests/legacy/delete.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/delete.yml rename to specifications/client-side-encryption/tests/legacy/delete.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/distinct.json b/specifications/client-side-encryption/tests/legacy/distinct.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/distinct.json rename to specifications/client-side-encryption/tests/legacy/distinct.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/distinct.yml b/specifications/client-side-encryption/tests/legacy/distinct.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/distinct.yml rename to specifications/client-side-encryption/tests/legacy/distinct.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/explain.json b/specifications/client-side-encryption/tests/legacy/explain.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/explain.json rename to specifications/client-side-encryption/tests/legacy/explain.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/explain.yml b/specifications/client-side-encryption/tests/legacy/explain.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/explain.yml rename to specifications/client-side-encryption/tests/legacy/explain.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/find.json b/specifications/client-side-encryption/tests/legacy/find.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/find.json rename to specifications/client-side-encryption/tests/legacy/find.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/find.yml b/specifications/client-side-encryption/tests/legacy/find.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/find.yml rename to specifications/client-side-encryption/tests/legacy/find.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/findOneAndDelete.json b/specifications/client-side-encryption/tests/legacy/findOneAndDelete.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/findOneAndDelete.json rename to specifications/client-side-encryption/tests/legacy/findOneAndDelete.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/findOneAndDelete.yml b/specifications/client-side-encryption/tests/legacy/findOneAndDelete.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/findOneAndDelete.yml rename to specifications/client-side-encryption/tests/legacy/findOneAndDelete.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/findOneAndReplace.json b/specifications/client-side-encryption/tests/legacy/findOneAndReplace.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/findOneAndReplace.json rename to specifications/client-side-encryption/tests/legacy/findOneAndReplace.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/findOneAndReplace.yml b/specifications/client-side-encryption/tests/legacy/findOneAndReplace.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/findOneAndReplace.yml rename to specifications/client-side-encryption/tests/legacy/findOneAndReplace.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/findOneAndUpdate.json b/specifications/client-side-encryption/tests/legacy/findOneAndUpdate.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/findOneAndUpdate.json rename to specifications/client-side-encryption/tests/legacy/findOneAndUpdate.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/findOneAndUpdate.yml b/specifications/client-side-encryption/tests/legacy/findOneAndUpdate.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/findOneAndUpdate.yml rename to specifications/client-side-encryption/tests/legacy/findOneAndUpdate.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-BypassQueryAnalysis.json b/specifications/client-side-encryption/tests/legacy/fle2-BypassQueryAnalysis.json similarity index 99% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-BypassQueryAnalysis.json rename to specifications/client-side-encryption/tests/legacy/fle2-BypassQueryAnalysis.json index 629faf189db..b8d06e8bcdd 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-BypassQueryAnalysis.json +++ b/specifications/client-side-encryption/tests/legacy/fle2-BypassQueryAnalysis.json @@ -4,7 +4,8 @@ "minServerVersion": "6.0.0", "topology": [ "replicaset", - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-BypassQueryAnalysis.yml b/specifications/client-side-encryption/tests/legacy/fle2-BypassQueryAnalysis.yml similarity index 98% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-BypassQueryAnalysis.yml rename to specifications/client-side-encryption/tests/legacy/fle2-BypassQueryAnalysis.yml index f653527d367..e6b3d66505a 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-BypassQueryAnalysis.yml +++ b/specifications/client-side-encryption/tests/legacy/fle2-BypassQueryAnalysis.yml @@ -1,7 +1,7 @@ runOn: - minServerVersion: "6.0.0" # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded" ] + topology: [ "replicaset", "sharded", "load-balanced" ] database_name: &database_name "default" collection_name: &collection_name "default" data: [] diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-Compact.json b/specifications/client-side-encryption/tests/legacy/fle2-Compact.json similarity index 99% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-Compact.json rename to specifications/client-side-encryption/tests/legacy/fle2-Compact.json index 46da99cbfc3..6ca0f9ba02c 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-Compact.json +++ b/specifications/client-side-encryption/tests/legacy/fle2-Compact.json @@ -4,7 +4,8 @@ "minServerVersion": "6.0.0", "topology": [ "replicaset", - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-Compact.yml b/specifications/client-side-encryption/tests/legacy/fle2-Compact.yml similarity index 98% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-Compact.yml rename to specifications/client-side-encryption/tests/legacy/fle2-Compact.yml index b5e788f3698..6e3a198d021 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-Compact.yml +++ b/specifications/client-side-encryption/tests/legacy/fle2-Compact.yml @@ -1,7 +1,7 @@ runOn: - minServerVersion: "6.0.0" # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded" ] + topology: [ "replicaset", "sharded", "load-balanced" ] database_name: &database_name "default" collection_name: &collection_name "default" data: [] diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-CreateCollection.json b/specifications/client-side-encryption/tests/legacy/fle2-CreateCollection.json similarity index 99% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-CreateCollection.json rename to specifications/client-side-encryption/tests/legacy/fle2-CreateCollection.json index 6836f40e040..9f8db41f871 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-CreateCollection.json +++ b/specifications/client-side-encryption/tests/legacy/fle2-CreateCollection.json @@ -4,7 +4,8 @@ "minServerVersion": "6.0.0", "topology": [ "replicaset", - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-CreateCollection.yml b/specifications/client-side-encryption/tests/legacy/fle2-CreateCollection.yml similarity index 99% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-CreateCollection.yml rename to specifications/client-side-encryption/tests/legacy/fle2-CreateCollection.yml index 160da07ff91..78d7e55e074 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-CreateCollection.yml +++ b/specifications/client-side-encryption/tests/legacy/fle2-CreateCollection.yml @@ -2,7 +2,7 @@ runOn: - minServerVersion: "6.0.0" # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded" ] + topology: [ "replicaset", "sharded", "load-balanced" ] database_name: &database_name "default" collection_name: &collection_name "default" diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-DecryptExistingData.json b/specifications/client-side-encryption/tests/legacy/fle2-DecryptExistingData.json similarity index 98% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-DecryptExistingData.json rename to specifications/client-side-encryption/tests/legacy/fle2-DecryptExistingData.json index c6d0bca0d1a..e622d3334de 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-DecryptExistingData.json +++ b/specifications/client-side-encryption/tests/legacy/fle2-DecryptExistingData.json @@ -4,7 +4,8 @@ "minServerVersion": "6.0.0", "topology": [ "replicaset", - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-DecryptExistingData.yml b/specifications/client-side-encryption/tests/legacy/fle2-DecryptExistingData.yml similarity index 97% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-DecryptExistingData.yml rename to specifications/client-side-encryption/tests/legacy/fle2-DecryptExistingData.yml index c799eae21dd..d1d0e2384a1 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-DecryptExistingData.yml +++ b/specifications/client-side-encryption/tests/legacy/fle2-DecryptExistingData.yml @@ -1,7 +1,7 @@ runOn: - minServerVersion: "6.0.0" # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded" ] + topology: [ "replicaset", "sharded", "load-balanced" ] database_name: &database_name "default" collection_name: &collection_name "default" data: [ diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-Delete.json b/specifications/client-side-encryption/tests/legacy/fle2-Delete.json similarity index 99% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-Delete.json rename to specifications/client-side-encryption/tests/legacy/fle2-Delete.json index 0e3e06396e5..86871277480 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-Delete.json +++ b/specifications/client-side-encryption/tests/legacy/fle2-Delete.json @@ -4,7 +4,8 @@ "minServerVersion": "6.0.0", "topology": [ "replicaset", - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-Delete.yml b/specifications/client-side-encryption/tests/legacy/fle2-Delete.yml similarity index 98% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-Delete.yml rename to specifications/client-side-encryption/tests/legacy/fle2-Delete.yml index 6b6714233a7..7dfe1176818 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-Delete.yml +++ b/specifications/client-side-encryption/tests/legacy/fle2-Delete.yml @@ -1,7 +1,7 @@ runOn: - minServerVersion: "6.0.0" # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded" ] + topology: [ "replicaset", "sharded", "load-balanced" ] database_name: &database_name "default" collection_name: &collection_name "default" data: [] diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-EncryptedFields-vs-EncryptedFieldsMap.json b/specifications/client-side-encryption/tests/legacy/fle2-EncryptedFields-vs-EncryptedFieldsMap.json similarity index 99% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-EncryptedFields-vs-EncryptedFieldsMap.json rename to specifications/client-side-encryption/tests/legacy/fle2-EncryptedFields-vs-EncryptedFieldsMap.json index ea3eb4850c1..911b428633a 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-EncryptedFields-vs-EncryptedFieldsMap.json +++ b/specifications/client-side-encryption/tests/legacy/fle2-EncryptedFields-vs-EncryptedFieldsMap.json @@ -4,7 +4,8 @@ "minServerVersion": "6.0.0", "topology": [ "replicaset", - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-EncryptedFields-vs-EncryptedFieldsMap.yml b/specifications/client-side-encryption/tests/legacy/fle2-EncryptedFields-vs-EncryptedFieldsMap.yml similarity index 98% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-EncryptedFields-vs-EncryptedFieldsMap.yml rename to specifications/client-side-encryption/tests/legacy/fle2-EncryptedFields-vs-EncryptedFieldsMap.yml index 5d26e2e8b13..ba4e7a73612 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-EncryptedFields-vs-EncryptedFieldsMap.yml +++ b/specifications/client-side-encryption/tests/legacy/fle2-EncryptedFields-vs-EncryptedFieldsMap.yml @@ -1,7 +1,7 @@ runOn: - minServerVersion: "6.0.0" # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded" ] + topology: [ "replicaset", "sharded", "load-balanced" ] database_name: &database_name "default" collection_name: &collection_name "default" data: [] diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-EncryptedFields-vs-jsonSchema.json b/specifications/client-side-encryption/tests/legacy/fle2-EncryptedFields-vs-jsonSchema.json similarity index 99% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-EncryptedFields-vs-jsonSchema.json rename to specifications/client-side-encryption/tests/legacy/fle2-EncryptedFields-vs-jsonSchema.json index 1d3227ee7f0..f4386483da9 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-EncryptedFields-vs-jsonSchema.json +++ b/specifications/client-side-encryption/tests/legacy/fle2-EncryptedFields-vs-jsonSchema.json @@ -4,7 +4,8 @@ "minServerVersion": "6.0.0", "topology": [ "replicaset", - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-EncryptedFields-vs-jsonSchema.yml b/specifications/client-side-encryption/tests/legacy/fle2-EncryptedFields-vs-jsonSchema.yml similarity index 98% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-EncryptedFields-vs-jsonSchema.yml rename to specifications/client-side-encryption/tests/legacy/fle2-EncryptedFields-vs-jsonSchema.yml index 6b822ff7249..2ff2e4da1c1 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-EncryptedFields-vs-jsonSchema.yml +++ b/specifications/client-side-encryption/tests/legacy/fle2-EncryptedFields-vs-jsonSchema.yml @@ -1,7 +1,7 @@ runOn: - minServerVersion: "6.0.0" # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded" ] + topology: [ "replicaset", "sharded", "load-balanced" ] database_name: &database_name "default" collection_name: &collection_name "default" data: [] diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-EncryptedFieldsMap-defaults.json b/specifications/client-side-encryption/tests/legacy/fle2-EncryptedFieldsMap-defaults.json similarity index 98% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-EncryptedFieldsMap-defaults.json rename to specifications/client-side-encryption/tests/legacy/fle2-EncryptedFieldsMap-defaults.json index 030952e0564..60820aae95e 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-EncryptedFieldsMap-defaults.json +++ b/specifications/client-side-encryption/tests/legacy/fle2-EncryptedFieldsMap-defaults.json @@ -4,7 +4,8 @@ "minServerVersion": "6.0.0", "topology": [ "replicaset", - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-EncryptedFieldsMap-defaults.yml b/specifications/client-side-encryption/tests/legacy/fle2-EncryptedFieldsMap-defaults.yml similarity index 97% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-EncryptedFieldsMap-defaults.yml rename to specifications/client-side-encryption/tests/legacy/fle2-EncryptedFieldsMap-defaults.yml index 395b8c27bd1..0ee418db058 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-EncryptedFieldsMap-defaults.yml +++ b/specifications/client-side-encryption/tests/legacy/fle2-EncryptedFieldsMap-defaults.yml @@ -1,7 +1,7 @@ runOn: - minServerVersion: "6.0.0" # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded" ] + topology: [ "replicaset", "sharded", "load-balanced" ] database_name: &database_name "default" collection_name: &collection_name "default" data: [] diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-FindOneAndUpdate.json b/specifications/client-side-encryption/tests/legacy/fle2-FindOneAndUpdate.json similarity index 99% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-FindOneAndUpdate.json rename to specifications/client-side-encryption/tests/legacy/fle2-FindOneAndUpdate.json index b31438876f9..de1b5c5aad0 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-FindOneAndUpdate.json +++ b/specifications/client-side-encryption/tests/legacy/fle2-FindOneAndUpdate.json @@ -4,7 +4,8 @@ "minServerVersion": "6.0.0", "topology": [ "replicaset", - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-FindOneAndUpdate.yml b/specifications/client-side-encryption/tests/legacy/fle2-FindOneAndUpdate.yml similarity index 99% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-FindOneAndUpdate.yml rename to specifications/client-side-encryption/tests/legacy/fle2-FindOneAndUpdate.yml index 96ce954bec1..a5596c2c24b 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-FindOneAndUpdate.yml +++ b/specifications/client-side-encryption/tests/legacy/fle2-FindOneAndUpdate.yml @@ -1,7 +1,7 @@ runOn: - minServerVersion: "6.0.0" # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded" ] + topology: [ "replicaset", "sharded", "load-balanced" ] database_name: &database_name "default" collection_name: &collection_name "default" data: [] diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-InsertFind-Indexed.json b/specifications/client-side-encryption/tests/legacy/fle2-InsertFind-Indexed.json similarity index 99% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-InsertFind-Indexed.json rename to specifications/client-side-encryption/tests/legacy/fle2-InsertFind-Indexed.json index 81a549590e7..84b69d7de91 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-InsertFind-Indexed.json +++ b/specifications/client-side-encryption/tests/legacy/fle2-InsertFind-Indexed.json @@ -4,7 +4,8 @@ "minServerVersion": "6.0.0", "topology": [ "replicaset", - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-InsertFind-Indexed.yml b/specifications/client-side-encryption/tests/legacy/fle2-InsertFind-Indexed.yml similarity index 98% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-InsertFind-Indexed.yml rename to specifications/client-side-encryption/tests/legacy/fle2-InsertFind-Indexed.yml index 5b785d8cf23..5c5efbcc5ef 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-InsertFind-Indexed.yml +++ b/specifications/client-side-encryption/tests/legacy/fle2-InsertFind-Indexed.yml @@ -1,7 +1,7 @@ runOn: - minServerVersion: "6.0.0" # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded" ] + topology: [ "replicaset", "sharded", "load-balanced" ] database_name: &database_name "default" collection_name: &collection_name "default" data: [] diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-InsertFind-Unindexed.json b/specifications/client-side-encryption/tests/legacy/fle2-InsertFind-Unindexed.json similarity index 98% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-InsertFind-Unindexed.json rename to specifications/client-side-encryption/tests/legacy/fle2-InsertFind-Unindexed.json index 1a75095907b..9b314385256 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-InsertFind-Unindexed.json +++ b/specifications/client-side-encryption/tests/legacy/fle2-InsertFind-Unindexed.json @@ -4,7 +4,8 @@ "minServerVersion": "6.0.0", "topology": [ "replicaset", - "sharded" + "sharded", + "load-balanced" ] } ], @@ -241,7 +242,7 @@ } }, "result": { - "errorContains": "Cannot query" + "errorContains": "encrypt" } } ] diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-InsertFind-Unindexed.yml b/specifications/client-side-encryption/tests/legacy/fle2-InsertFind-Unindexed.yml similarity index 87% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-InsertFind-Unindexed.yml rename to specifications/client-side-encryption/tests/legacy/fle2-InsertFind-Unindexed.yml index 26071fff60b..71f1993e56b 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-InsertFind-Unindexed.yml +++ b/specifications/client-side-encryption/tests/legacy/fle2-InsertFind-Unindexed.yml @@ -1,7 +1,7 @@ runOn: - minServerVersion: "6.0.0" # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded" ] + topology: [ "replicaset", "sharded", "load-balanced" ] database_name: &database_name "default" collection_name: &collection_name "default" data: [] @@ -80,4 +80,8 @@ tests: arguments: filter: { encryptedUnindexed: "value123" } result: - errorContains: "Cannot query" \ No newline at end of file + # Expected error message changed in https://github.com/10gen/mongo-enterprise-modules/commit/212b584d4f7a44bed41c826a180a4aff00923d7a#diff-5f12b55e8d5c52c2f62853ec595dc2c1e2e5cb4fdbf7a32739a8e3acb3c6f818 + # Before the message was "cannot query non-indexed fields with the randomized encryption algorithm" + # After: "can only execute encrypted equality queries with an encrypted equality index" + # Use a small common substring. + errorContains: "encrypt" \ No newline at end of file diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-MissingKey.json b/specifications/client-side-encryption/tests/legacy/fle2-MissingKey.json similarity index 98% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-MissingKey.json rename to specifications/client-side-encryption/tests/legacy/fle2-MissingKey.json index 2db1cd77025..4210da09e43 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-MissingKey.json +++ b/specifications/client-side-encryption/tests/legacy/fle2-MissingKey.json @@ -4,7 +4,8 @@ "minServerVersion": "6.0.0", "topology": [ "replicaset", - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-MissingKey.yml b/specifications/client-side-encryption/tests/legacy/fle2-MissingKey.yml similarity index 97% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-MissingKey.yml rename to specifications/client-side-encryption/tests/legacy/fle2-MissingKey.yml index 94c624538b7..937c82ee9ef 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-MissingKey.yml +++ b/specifications/client-side-encryption/tests/legacy/fle2-MissingKey.yml @@ -1,7 +1,7 @@ runOn: - minServerVersion: "6.0.0" # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded" ] + topology: [ "replicaset", "sharded", "load-balanced" ] database_name: &database_name "default" collection_name: &collection_name "default" data: [ diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-NoEncryption.json b/specifications/client-side-encryption/tests/legacy/fle2-NoEncryption.json similarity index 97% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-NoEncryption.json rename to specifications/client-side-encryption/tests/legacy/fle2-NoEncryption.json index e9dd586c26c..9d255bd4938 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-NoEncryption.json +++ b/specifications/client-side-encryption/tests/legacy/fle2-NoEncryption.json @@ -4,7 +4,8 @@ "minServerVersion": "6.0.0", "topology": [ "replicaset", - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-NoEncryption.yml b/specifications/client-side-encryption/tests/legacy/fle2-NoEncryption.yml similarity index 95% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-NoEncryption.yml rename to specifications/client-side-encryption/tests/legacy/fle2-NoEncryption.yml index 7a547eee0fd..8725ac1ef79 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-NoEncryption.yml +++ b/specifications/client-side-encryption/tests/legacy/fle2-NoEncryption.yml @@ -1,7 +1,7 @@ runOn: - minServerVersion: "6.0.0" # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded" ] + topology: [ "replicaset", "sharded", "load-balanced" ] database_name: &database_name "default" collection_name: &collection_name "default" data: [] diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Date-Aggregate.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-Date-Aggregate.json new file mode 100644 index 00000000000..a35321cd358 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Date-Aggregate.json @@ -0,0 +1,514 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Date. Aggregate.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$gt": { + "$date": { + "$numberLong": "0" + } + } + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + } + ], + "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": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDate": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDate": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "default", + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$gt": { + "$binary": { + "base64": "CnEFAAADcGF5bG9hZABBBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVjACAAAAAAKs6X8gaa+AJ6PpxVicsk7iCXpSYIkQ5zB0KnXqlfCWgAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWMAIAAAAABYTWOKsDZ7cgPhiKDR2bE5CFGz4MuEwZbzZ0z46eGzBQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFYwAgAAAAABSqgKlxv5LXoxUO3zNmqGztlZhL2S150WiR/eycv7CWAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVjACAAAAAAfsofSP7nQHv8ic8ZW0aNlWxplS46Z+mywPR4rQk+wcgAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWMAIAAAAADu3L42J/oqNLe/XNYpodf2fhmPdcLhqO4J24o0hiveXwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFYwAgAAAAAAUljtCIvjRnLB2UPTEeRIz11Snl5SOBUNaXMXig4rqKAAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVjACAAAAAAD51NYesiO4Fo7w7iWBfqAFxEqkaDVctpvzZ28nT4SE8AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWMAIAAAAABJDtFhJ2tPbowp1UUmOCN/rqSqHRL1dtMu0c47vIlK4wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FYwAgAAAAAE6kvmXPqTnYIH4EJmNhy8OLVJZFOmdiBXLMorhELjKWAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVjACAAAAAA85AiE+bNFAYQTXQAFexgeczdVhf8FUnf16WzJlI/kmsAAAVlACAAAAAA65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+wSY20AAAAAAAAAAAAAEHBheWxvYWRJZAAAAAAAEGZpcnN0T3BlcmF0b3IAAQAAAAA=", + "subType": "06" + } + } + } + } + } + ], + "cursor": {}, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "command_name": "aggregate" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDate": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedDate": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Date-Aggregate.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-Date-Aggregate.yml new file mode 100644 index 00000000000..4d17344dc53 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Date-Aggregate.yml @@ -0,0 +1,227 @@ +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDate', 'bsonType': 'date', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$date': {'$numberLong': '0'}}, 'max': {'$date': {'$numberLong': '200'}}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "FLE2 Range Date. Aggregate." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDate: {$date: { $numberLong: "0" }} } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDate: {$date: { $numberLong: "1" }} } + - name: aggregate + arguments: + pipeline: [{ $match: { "encryptedDate": { $gt: {$date: {$numberLong: "0" }}} } }] + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + aggregate: *collection_name + pipeline: [ + { + "$match": { + "encryptedDate": { + "$gt": { + "$binary": { + "base64": "CnEFAAADcGF5bG9hZABBBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVjACAAAAAAKs6X8gaa+AJ6PpxVicsk7iCXpSYIkQ5zB0KnXqlfCWgAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWMAIAAAAABYTWOKsDZ7cgPhiKDR2bE5CFGz4MuEwZbzZ0z46eGzBQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFYwAgAAAAABSqgKlxv5LXoxUO3zNmqGztlZhL2S150WiR/eycv7CWAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVjACAAAAAAfsofSP7nQHv8ic8ZW0aNlWxplS46Z+mywPR4rQk+wcgAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWMAIAAAAADu3L42J/oqNLe/XNYpodf2fhmPdcLhqO4J24o0hiveXwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFYwAgAAAAAAUljtCIvjRnLB2UPTEeRIz11Snl5SOBUNaXMXig4rqKAAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVjACAAAAAAD51NYesiO4Fo7w7iWBfqAFxEqkaDVctpvzZ28nT4SE8AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWMAIAAAAABJDtFhJ2tPbowp1UUmOCN/rqSqHRL1dtMu0c47vIlK4wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FYwAgAAAAAE6kvmXPqTnYIH4EJmNhy8OLVJZFOmdiBXLMorhELjKWAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVjACAAAAAA85AiE+bNFAYQTXQAFexgeczdVhf8FUnf16WzJlI/kmsAAAVlACAAAAAA65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+wSY20AAAAAAAAAAAAAEHBheWxvYWRJZAAAAAAAEGZpcnN0T3BlcmF0b3IAAQAAAAA=", + "subType": "06" + } + } + } + } + } + ] + cursor: {} + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: aggregate + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDate": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + } + - + { + "_id": 1, + "encryptedDate": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", + "subType": "00" + } + } + ] + } \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Date-Correctness.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-Date-Correctness.json new file mode 100644 index 00000000000..5832e854183 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Date-Correctness.json @@ -0,0 +1,1842 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "Find with $gt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$gt": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + } + ] + }, + { + "description": "Find with $gte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$gte": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + }, + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + } + ] + }, + { + "description": "Find with $gt with no results", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$gt": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + "result": [] + } + ] + }, + { + "description": "Find with $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$lt": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + "result": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + ] + } + ] + }, + { + "description": "Find with $lte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$lte": { + "$date": { + "$numberLong": "1" + } + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + }, + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + } + ] + }, + { + "description": "Find with $lt below min", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$lt": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + "result": { + "errorContains": "must be greater than the range minimum" + } + } + ] + }, + { + "description": "Find with $gt above max", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$gt": { + "$date": { + "$numberLong": "200" + } + } + } + } + }, + "result": { + "errorContains": "must be less than the range maximum" + } + } + ] + }, + { + "description": "Find with $gt and $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$gt": { + "$date": { + "$numberLong": "0" + } + }, + "$lt": { + "$date": { + "$numberLong": "2" + } + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + } + ] + }, + { + "description": "Find with equality", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "result": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + ] + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + } + ] + }, + { + "description": "Find with full range", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$gte": { + "$date": { + "$numberLong": "0" + } + }, + "$lte": { + "$date": { + "$numberLong": "200" + } + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + }, + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + } + ] + }, + { + "description": "Find with $in", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$in": [ + { + "$date": { + "$numberLong": "0" + } + } + ] + } + } + }, + "result": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + ] + } + ] + }, + { + "description": "Insert out of range", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "-1" + } + } + } + }, + "result": { + "errorContains": "value must be greater than or equal to the minimum value" + } + } + ] + }, + { + "description": "Insert min and max", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 200, + "encryptedDate": { + "$date": { + "$numberLong": "200" + } + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + }, + { + "_id": 200, + "encryptedDate": { + "$date": { + "$numberLong": "200" + } + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$gte": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + }, + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gt with no results", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$gt": { + "$date": { + "$numberLong": "1" + } + } + } + } + } + ] + }, + "result": [] + } + ] + }, + { + "description": "Aggregate with $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$lt": { + "$date": { + "$numberLong": "1" + } + } + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + ] + } + ] + }, + { + "description": "Aggregate with $lte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$lte": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + }, + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + } + ] + }, + { + "description": "Aggregate with $lt below min", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$lt": { + "$date": { + "$numberLong": "0" + } + } + } + } + } + ] + }, + "result": { + "errorContains": "must be greater than the range minimum" + } + } + ] + }, + { + "description": "Aggregate with $gt above max", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$gt": { + "$date": { + "$numberLong": "200" + } + } + } + } + } + ] + }, + "result": { + "errorContains": "must be less than the range maximum" + } + } + ] + }, + { + "description": "Aggregate with $gt and $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$gt": { + "$date": { + "$numberLong": "0" + } + }, + "$lt": { + "$date": { + "$numberLong": "2" + } + } + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + } + ] + }, + { + "description": "Aggregate with equality", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + ] + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + } + ] + }, + { + "description": "Aggregate with full range", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$gte": { + "$date": { + "$numberLong": "0" + } + }, + "$lte": { + "$date": { + "$numberLong": "200" + } + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + }, + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + } + ] + }, + { + "description": "Aggregate with $in", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$in": [ + { + "$date": { + "$numberLong": "0" + } + } + ] + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + ] + } + ] + }, + { + "description": "Wrong type: Insert Double", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$numberDouble": "0" + } + } + }, + "result": { + "errorContains": "cannot encrypt element" + } + } + ] + }, + { + "description": "Wrong type: Find Double", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$gte": { + "$numberDouble": "0" + } + } + } + }, + "result": { + "errorContains": "value type is a date" + } + } + ] + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Date-Correctness.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-Date-Correctness.yml new file mode 100644 index 00000000000..65382eec6b5 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Date-Correctness.yml @@ -0,0 +1,420 @@ +# Test correctness results. +# Does not include command monitoring expectations or outcome assertions to make tests more readable. +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDate', 'bsonType': 'date', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$date': {'$numberLong': '0'}}, 'max': {'$date': {'$numberLong': '200'}}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "Find with $gt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDate: { $date: { $numberLong: "0" } } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDate: { $date: { $numberLong: "1" } } } + - name: find + arguments: + filter: { encryptedDate: { $gt: { $date: { $numberLong: "0" } } }} + result: [*doc1] + + - description: "Find with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDate: { $gte: { $date: { $numberLong: "0" } } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDate: { $gt: { $date: { $numberLong: "1" } } }} + result: [] + + - description: "Find with $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDate: { $lt: { $date: { $numberLong: "1" } } }} + result: [*doc0] + + - description: "Find with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDate: { $lte: { $date: { $numberLong: "1" } } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $lt below min" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDate: { $lt: { $date: { $numberLong: "0" } } }} + result: + errorContains: must be greater than the range minimum + + - description: "Find with $gt above max" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDate: { $gt: { $date: { $numberLong: "200" } } }} + result: + errorContains: must be less than the range maximum + + - description: "Find with $gt and $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDate: { $gt: { $date: { $numberLong: "0" } }, $lt: { $date: {$numberLong: "2"}} }} + result: [*doc1] + + - description: "Find with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDate: { $date: { $numberLong: "0" } } } + result: [*doc0] + - name: find + arguments: + filter: { encryptedDate: { $date: { $numberLong: "1" } } } + result: [*doc1] + + - description: "Find with full range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDate: { $gte: { $date: {$numberLong: "0"}}, $lte: { $date: {$numberLong: "200"} } } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDate: { $in: [ {$date: {$numberLong: "0"}} ] } } + result: [*doc0] + + - description: "Insert out of range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedDate: {$date: { $numberLong: "-1" }}} + result: + errorContains: value must be greater than or equal to the minimum value + + - description: "Insert min and max" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: *doc0 + - name: insertOne + arguments: + document: &doc200 { _id: 200, encryptedDate: { $date: { $numberLong: "200" } }} + - name: find + arguments: + filter: {} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc200] + + - description: "Aggregate with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDate: { $gte: { $date: { $numberLong: "0" } } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDate: { $gt: { $date: { $numberLong: "1" } } }} } + result: [] + + - description: "Aggregate with $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDate: { $lt: { $date: { $numberLong: "1" } } }} } + result: [*doc0] + + - description: "Aggregate with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDate: { $lte: { $date: { $numberLong: "1" } } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $lt below min" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDate: { $lt: { $date: { $numberLong: "0" } } }} } + result: + errorContains: must be greater than the range minimum + + - description: "Aggregate with $gt above max" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDate: { $gt: { $date: { $numberLong: "200" } } }} } + result: + errorContains: must be less than the range maximum + + - description: "Aggregate with $gt and $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDate: { $gt: { $date: { $numberLong: "0" } }, $lt: { $date: {$numberLong: "2"}} }} } + result: [*doc1] + + - description: "Aggregate with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDate: { $date: { $numberLong: "0" } } } } + result: [*doc0] + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDate: { $date: { $numberLong: "1" } } } } + result: [*doc1] + + - description: "Aggregate with full range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDate: { $gte: {$date: {$numberLong: "0"}}, $lte: {$date: {$numberLong: "200"}} } } } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDate: { $in: [ {$date: {$numberLong: "0"}} ] } } } + result: [*doc0] + + - description: "Wrong type: Insert Double" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: { _id: 0, encryptedDate: { $numberDouble: "0" }} } + result: + # Expect an error from mongocryptd. + errorContains: "cannot encrypt element" + + - description: "Wrong type: Find Double" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: find + arguments: + filter: { encryptedDate: { $gte: { $numberDouble: "0" } }} + result: + # expect an error mongocryptd. + errorContains: "value type is a date" \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Date-Delete.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-Date-Delete.json new file mode 100644 index 00000000000..b5856e76206 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Date-Delete.json @@ -0,0 +1,459 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Date. Delete.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "deleteOne", + "arguments": { + "filter": { + "encryptedDate": { + "$gt": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + "result": { + "deletedCount": 1 + } + } + ], + "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": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDate": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDate": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "delete": "default", + "deletes": [ + { + "q": { + "encryptedDate": { + "$gt": { + "$binary": { + "base64": "CnEFAAADcGF5bG9hZABBBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVjACAAAAAAKs6X8gaa+AJ6PpxVicsk7iCXpSYIkQ5zB0KnXqlfCWgAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWMAIAAAAABYTWOKsDZ7cgPhiKDR2bE5CFGz4MuEwZbzZ0z46eGzBQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFYwAgAAAAABSqgKlxv5LXoxUO3zNmqGztlZhL2S150WiR/eycv7CWAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVjACAAAAAAfsofSP7nQHv8ic8ZW0aNlWxplS46Z+mywPR4rQk+wcgAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWMAIAAAAADu3L42J/oqNLe/XNYpodf2fhmPdcLhqO4J24o0hiveXwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFYwAgAAAAAAUljtCIvjRnLB2UPTEeRIz11Snl5SOBUNaXMXig4rqKAAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVjACAAAAAAD51NYesiO4Fo7w7iWBfqAFxEqkaDVctpvzZ28nT4SE8AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWMAIAAAAABJDtFhJ2tPbowp1UUmOCN/rqSqHRL1dtMu0c47vIlK4wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FYwAgAAAAAE6kvmXPqTnYIH4EJmNhy8OLVJZFOmdiBXLMorhELjKWAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVjACAAAAAA85AiE+bNFAYQTXQAFexgeczdVhf8FUnf16WzJlI/kmsAAAVlACAAAAAA65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+wSY20AAAAAAAAAAAAAEHBheWxvYWRJZAAAAAAAEGZpcnN0T3BlcmF0b3IAAQAAAAA=", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ], + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + }, + "deleteTokens": { + "default.default": { + "encryptedDate": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + } + }, + "command_name": "delete" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDate": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Date-Delete.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-Date-Delete.yml new file mode 100644 index 00000000000..6bb2535bb46 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Date-Delete.yml @@ -0,0 +1,185 @@ +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDate', 'bsonType': 'date', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$date': {'$numberLong': '0'}}, 'max': {'$date': {'$numberLong': '200'}}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "FLE2 Range Date. Delete." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDate: {$date: { $numberLong: "0" }} } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDate: {$date: { $numberLong: "1" }} } + - name: deleteOne + arguments: + filter: { "encryptedDate": { $gt: {$date: {$numberLong: "0" }}} } + result: + deletedCount: 1 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + delete: *collection_name + deletes: [ + { + "q": { + "encryptedDate": { + "$gt": { + "$binary": { + "base64": "CnEFAAADcGF5bG9hZABBBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVjACAAAAAAKs6X8gaa+AJ6PpxVicsk7iCXpSYIkQ5zB0KnXqlfCWgAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWMAIAAAAABYTWOKsDZ7cgPhiKDR2bE5CFGz4MuEwZbzZ0z46eGzBQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFYwAgAAAAABSqgKlxv5LXoxUO3zNmqGztlZhL2S150WiR/eycv7CWAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVjACAAAAAAfsofSP7nQHv8ic8ZW0aNlWxplS46Z+mywPR4rQk+wcgAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWMAIAAAAADu3L42J/oqNLe/XNYpodf2fhmPdcLhqO4J24o0hiveXwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFYwAgAAAAAAUljtCIvjRnLB2UPTEeRIz11Snl5SOBUNaXMXig4rqKAAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVjACAAAAAAD51NYesiO4Fo7w7iWBfqAFxEqkaDVctpvzZ28nT4SE8AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWMAIAAAAABJDtFhJ2tPbowp1UUmOCN/rqSqHRL1dtMu0c47vIlK4wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FYwAgAAAAAE6kvmXPqTnYIH4EJmNhy8OLVJZFOmdiBXLMorhELjKWAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVjACAAAAAA85AiE+bNFAYQTXQAFexgeczdVhf8FUnf16WzJlI/kmsAAAVlACAAAAAA65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+wSY20AAAAAAAAAAAAAEHBheWxvYWRJZAAAAAAAEGZpcnN0T3BlcmF0b3IAAQAAAAA=", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ] + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + "deleteTokens": { + "default.default": { + "encryptedDate": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + command_name: delete + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDate": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + } \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Date-FindOneAndUpdate.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-Date-FindOneAndUpdate.json new file mode 100644 index 00000000000..a59258a466e --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Date-FindOneAndUpdate.json @@ -0,0 +1,538 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Date. FindOneAndUpdate.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "encryptedDate": { + "$gt": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "update": { + "$set": { + "encryptedDate": { + "$date": { + "$numberLong": "2" + } + } + } + }, + "returnDocument": "Before" + }, + "result": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + ], + "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": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDate": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDate": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "findAndModify": "default", + "query": { + "encryptedDate": { + "$gt": { + "$binary": { + "base64": "CnEFAAADcGF5bG9hZABBBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVjACAAAAAAKs6X8gaa+AJ6PpxVicsk7iCXpSYIkQ5zB0KnXqlfCWgAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWMAIAAAAABYTWOKsDZ7cgPhiKDR2bE5CFGz4MuEwZbzZ0z46eGzBQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFYwAgAAAAABSqgKlxv5LXoxUO3zNmqGztlZhL2S150WiR/eycv7CWAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVjACAAAAAAfsofSP7nQHv8ic8ZW0aNlWxplS46Z+mywPR4rQk+wcgAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWMAIAAAAADu3L42J/oqNLe/XNYpodf2fhmPdcLhqO4J24o0hiveXwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFYwAgAAAAAAUljtCIvjRnLB2UPTEeRIz11Snl5SOBUNaXMXig4rqKAAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVjACAAAAAAD51NYesiO4Fo7w7iWBfqAFxEqkaDVctpvzZ28nT4SE8AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWMAIAAAAABJDtFhJ2tPbowp1UUmOCN/rqSqHRL1dtMu0c47vIlK4wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FYwAgAAAAAE6kvmXPqTnYIH4EJmNhy8OLVJZFOmdiBXLMorhELjKWAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVjACAAAAAA85AiE+bNFAYQTXQAFexgeczdVhf8FUnf16WzJlI/kmsAAAVlACAAAAAA65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+wSY20AAAAAAAAAAAAAEHBheWxvYWRJZAAAAAAAEGZpcnN0T3BlcmF0b3IAAQAAAAA=", + "subType": "06" + } + } + } + }, + "update": { + "$set": { + "encryptedDate": { + "$$type": "binData" + } + } + }, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + }, + "deleteTokens": { + "default.default": { + "encryptedDate": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + } + }, + "command_name": "findAndModify" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDate": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedDate": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hyDcE6QQjPrYJaIS/n7evEZFYcm31Tj89CpEYGF45cI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ty4cnzJdAlbQKnh7px3GEYjBnvO+jIOaKjoTRDtmh3M=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Date-FindOneAndUpdate.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-Date-FindOneAndUpdate.yml new file mode 100644 index 00000000000..d220bf8ddf7 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Date-FindOneAndUpdate.yml @@ -0,0 +1,243 @@ +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDate', 'bsonType': 'date', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$date': {'$numberLong': '0'}}, 'max': {'$date': {'$numberLong': '200'}}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "FLE2 Range Date. FindOneAndUpdate." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDate: {$date: { $numberLong: "0" }} } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDate: {$date: { $numberLong: "1" }} } + - name: findOneAndUpdate + arguments: + filter: { encryptedDate: { $gt: {$date: {$numberLong: "0"}}} } + update: { "$set": { "encryptedDate": {$date: {$numberLong: "2"}}}} + returnDocument: Before + result: *doc1 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + findAndModify: *collection_name + query: { + "encryptedDate": { + "$gt": { + "$binary": { + "base64": "CnEFAAADcGF5bG9hZABBBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVjACAAAAAAKs6X8gaa+AJ6PpxVicsk7iCXpSYIkQ5zB0KnXqlfCWgAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWMAIAAAAABYTWOKsDZ7cgPhiKDR2bE5CFGz4MuEwZbzZ0z46eGzBQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFYwAgAAAAABSqgKlxv5LXoxUO3zNmqGztlZhL2S150WiR/eycv7CWAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVjACAAAAAAfsofSP7nQHv8ic8ZW0aNlWxplS46Z+mywPR4rQk+wcgAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWMAIAAAAADu3L42J/oqNLe/XNYpodf2fhmPdcLhqO4J24o0hiveXwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFYwAgAAAAAAUljtCIvjRnLB2UPTEeRIz11Snl5SOBUNaXMXig4rqKAAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVjACAAAAAAD51NYesiO4Fo7w7iWBfqAFxEqkaDVctpvzZ28nT4SE8AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWMAIAAAAABJDtFhJ2tPbowp1UUmOCN/rqSqHRL1dtMu0c47vIlK4wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FYwAgAAAAAE6kvmXPqTnYIH4EJmNhy8OLVJZFOmdiBXLMorhELjKWAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVjACAAAAAA85AiE+bNFAYQTXQAFexgeczdVhf8FUnf16WzJlI/kmsAAAVlACAAAAAA65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+wSY20AAAAAAAAAAAAAEHBheWxvYWRJZAAAAAAAEGZpcnN0T3BlcmF0b3IAAQAAAAA=", + "subType": "06" + } + } + } + } + update: { "$set": {"encryptedDate": { $$type: "binData" }} } + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + "deleteTokens": { + "default.default": { + "encryptedDate": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + command_name: findAndModify + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDate": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + } + - + { + "_id": 1, + "encryptedDate": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hyDcE6QQjPrYJaIS/n7evEZFYcm31Tj89CpEYGF45cI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ty4cnzJdAlbQKnh7px3GEYjBnvO+jIOaKjoTRDtmh3M=", + "subType": "00" + } + } + ] + } \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Date-InsertFind.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-Date-InsertFind.json new file mode 100644 index 00000000000..4357fafeea5 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Date-InsertFind.json @@ -0,0 +1,505 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Date. Insert and Find.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$gt": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + } + ], + "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": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDate": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDate": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "encryptedDate": { + "$gt": { + "$binary": { + "base64": "CnEFAAADcGF5bG9hZABBBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVjACAAAAAAKs6X8gaa+AJ6PpxVicsk7iCXpSYIkQ5zB0KnXqlfCWgAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWMAIAAAAABYTWOKsDZ7cgPhiKDR2bE5CFGz4MuEwZbzZ0z46eGzBQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFYwAgAAAAABSqgKlxv5LXoxUO3zNmqGztlZhL2S150WiR/eycv7CWAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVjACAAAAAAfsofSP7nQHv8ic8ZW0aNlWxplS46Z+mywPR4rQk+wcgAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWMAIAAAAADu3L42J/oqNLe/XNYpodf2fhmPdcLhqO4J24o0hiveXwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFYwAgAAAAAAUljtCIvjRnLB2UPTEeRIz11Snl5SOBUNaXMXig4rqKAAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVjACAAAAAAD51NYesiO4Fo7w7iWBfqAFxEqkaDVctpvzZ28nT4SE8AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWMAIAAAAABJDtFhJ2tPbowp1UUmOCN/rqSqHRL1dtMu0c47vIlK4wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FYwAgAAAAAE6kvmXPqTnYIH4EJmNhy8OLVJZFOmdiBXLMorhELjKWAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVjACAAAAAA85AiE+bNFAYQTXQAFexgeczdVhf8FUnf16WzJlI/kmsAAAVlACAAAAAA65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+wSY20AAAAAAAAAAAAAEHBheWxvYWRJZAAAAAAAEGZpcnN0T3BlcmF0b3IAAQAAAAA=", + "subType": "06" + } + } + } + }, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDate": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedDate": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Date-InsertFind.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-Date-InsertFind.yml new file mode 100644 index 00000000000..50a06cedafd --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Date-InsertFind.yml @@ -0,0 +1,221 @@ +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDate', 'bsonType': 'date', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$date': {'$numberLong': '0'}}, 'max': {'$date': {'$numberLong': '200'}}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "FLE2 Range Date. Insert and Find." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDate: { $date: { $numberLong: "0" }} } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDate: { $date: { $numberLong: "1" }} } + - name: find + arguments: + filter: { encryptedDate: { $gt: { $date: { $numberLong: "0" }} } } + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: + "encryptedDate": { + "$gt": { + "$binary": { + "base64": "CnEFAAADcGF5bG9hZABBBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVjACAAAAAAKs6X8gaa+AJ6PpxVicsk7iCXpSYIkQ5zB0KnXqlfCWgAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWMAIAAAAABYTWOKsDZ7cgPhiKDR2bE5CFGz4MuEwZbzZ0z46eGzBQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFYwAgAAAAABSqgKlxv5LXoxUO3zNmqGztlZhL2S150WiR/eycv7CWAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVjACAAAAAAfsofSP7nQHv8ic8ZW0aNlWxplS46Z+mywPR4rQk+wcgAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWMAIAAAAADu3L42J/oqNLe/XNYpodf2fhmPdcLhqO4J24o0hiveXwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFYwAgAAAAAAUljtCIvjRnLB2UPTEeRIz11Snl5SOBUNaXMXig4rqKAAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVjACAAAAAAD51NYesiO4Fo7w7iWBfqAFxEqkaDVctpvzZ28nT4SE8AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWMAIAAAAABJDtFhJ2tPbowp1UUmOCN/rqSqHRL1dtMu0c47vIlK4wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FYwAgAAAAAE6kvmXPqTnYIH4EJmNhy8OLVJZFOmdiBXLMorhELjKWAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVjACAAAAAA85AiE+bNFAYQTXQAFexgeczdVhf8FUnf16WzJlI/kmsAAAVlACAAAAAA65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+wSY20AAAAAAAAAAAAAEHBheWxvYWRJZAAAAAAAEGZpcnN0T3BlcmF0b3IAAQAAAAA=", + "subType": "06" + } + } + } + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDate": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + } + - + { + "_id": 1, + "encryptedDate": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", + "subType": "00" + } + } + ] + } \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Date-Update.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-Date-Update.json new file mode 100644 index 00000000000..fd170554f62 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Date-Update.json @@ -0,0 +1,540 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Date. Update.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "updateOne", + "arguments": { + "filter": { + "encryptedDate": { + "$gt": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "update": { + "$set": { + "encryptedDate": { + "$date": { + "$numberLong": "2" + } + } + } + } + }, + "result": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "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": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDate": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDate": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command_name": "update", + "command": { + "update": "default", + "ordered": true, + "updates": [ + { + "q": { + "encryptedDate": { + "$gt": { + "$binary": { + "base64": "CnEFAAADcGF5bG9hZABBBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVjACAAAAAAKs6X8gaa+AJ6PpxVicsk7iCXpSYIkQ5zB0KnXqlfCWgAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWMAIAAAAABYTWOKsDZ7cgPhiKDR2bE5CFGz4MuEwZbzZ0z46eGzBQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFYwAgAAAAABSqgKlxv5LXoxUO3zNmqGztlZhL2S150WiR/eycv7CWAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVjACAAAAAAfsofSP7nQHv8ic8ZW0aNlWxplS46Z+mywPR4rQk+wcgAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWMAIAAAAADu3L42J/oqNLe/XNYpodf2fhmPdcLhqO4J24o0hiveXwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFYwAgAAAAAAUljtCIvjRnLB2UPTEeRIz11Snl5SOBUNaXMXig4rqKAAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVjACAAAAAAD51NYesiO4Fo7w7iWBfqAFxEqkaDVctpvzZ28nT4SE8AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWMAIAAAAABJDtFhJ2tPbowp1UUmOCN/rqSqHRL1dtMu0c47vIlK4wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FYwAgAAAAAE6kvmXPqTnYIH4EJmNhy8OLVJZFOmdiBXLMorhELjKWAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVjACAAAAAA85AiE+bNFAYQTXQAFexgeczdVhf8FUnf16WzJlI/kmsAAAVlACAAAAAA65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+wSY20AAAAAAAAAAAAAEHBheWxvYWRJZAAAAAAAEGZpcnN0T3BlcmF0b3IAAQAAAAA=", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encryptedDate": { + "$$type": "binData" + } + } + } + } + ], + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + }, + "deleteTokens": { + "default.default": { + "encryptedDate": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + }, + "$db": "default" + } + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDate": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedDate": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hyDcE6QQjPrYJaIS/n7evEZFYcm31Tj89CpEYGF45cI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ty4cnzJdAlbQKnh7px3GEYjBnvO+jIOaKjoTRDtmh3M=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Date-Update.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-Date-Update.yml new file mode 100644 index 00000000000..ba327caad7d --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Date-Update.yml @@ -0,0 +1,260 @@ +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDate', 'bsonType': 'date', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$date': {'$numberLong': '0'}}, 'max': {'$date': {'$numberLong': '200'}}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "FLE2 Range Date. Update." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDate: { $date: { $numberLong: "0" } }} + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDate: { $date: { $numberLong: "1" } }} + - name: updateOne + arguments: + filter: { encryptedDate: { $gt: { $date: { $numberLong: "0" } } }} + update: { "$set": { "encryptedDate": { $date: { $numberLong: "2" } }}} + result: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command_name: update + command: + { + "update": "default", + "ordered": true, + "updates": [ + { + "q": { + "encryptedDate": { + "$gt": { + "$binary": { + "base64": "CnEFAAADcGF5bG9hZABBBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVjACAAAAAAKs6X8gaa+AJ6PpxVicsk7iCXpSYIkQ5zB0KnXqlfCWgAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWMAIAAAAABYTWOKsDZ7cgPhiKDR2bE5CFGz4MuEwZbzZ0z46eGzBQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFYwAgAAAAABSqgKlxv5LXoxUO3zNmqGztlZhL2S150WiR/eycv7CWAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVjACAAAAAAfsofSP7nQHv8ic8ZW0aNlWxplS46Z+mywPR4rQk+wcgAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWMAIAAAAADu3L42J/oqNLe/XNYpodf2fhmPdcLhqO4J24o0hiveXwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFYwAgAAAAAAUljtCIvjRnLB2UPTEeRIz11Snl5SOBUNaXMXig4rqKAAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVjACAAAAAAD51NYesiO4Fo7w7iWBfqAFxEqkaDVctpvzZ28nT4SE8AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWMAIAAAAABJDtFhJ2tPbowp1UUmOCN/rqSqHRL1dtMu0c47vIlK4wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FYwAgAAAAAE6kvmXPqTnYIH4EJmNhy8OLVJZFOmdiBXLMorhELjKWAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVjACAAAAAA85AiE+bNFAYQTXQAFexgeczdVhf8FUnf16WzJlI/kmsAAAVlACAAAAAA65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+wSY20AAAAAAAAAAAAAEHBheWxvYWRJZAAAAAAAEGZpcnN0T3BlcmF0b3IAAQAAAAA=", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encryptedDate": { $$type: "binData" } + } + } + } + ], + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": *encrypted_fields + }, + "deleteTokens": { + "default.default": { + "encryptedDate": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + }, + "$db": "default" + } + + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDate": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + } + - + { + "_id": 1, + "encryptedDate": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hyDcE6QQjPrYJaIS/n7evEZFYcm31Tj89CpEYGF45cI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ty4cnzJdAlbQKnh7px3GEYjBnvO+jIOaKjoTRDtmh3M=", + "subType": "00" + } + } + ] + } \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Decimal-Aggregate.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-Decimal-Aggregate.json new file mode 100644 index 00000000000..73d2cf4892d --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Decimal-Aggregate.json @@ -0,0 +1,1908 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimal", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Decimal. Aggregate.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimal": { + "$numberDecimal": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimal": { + "$numberDecimal": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimal": { + "$gt": { + "$numberDecimal": "0" + } + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedDecimal": { + "$numberDecimal": "1" + } + } + ] + } + ], + "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": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDecimal": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimal", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDecimal": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimal", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "default", + "pipeline": [ + { + "$match": { + "encryptedDecimal": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + } + } + ], + "cursor": {}, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimal", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "aggregate" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimal": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rbf3AeBEv4wWFAKknqDxRW5cLNkFvbIs6iJjc6LShQY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0l86Ag5OszXpa78SlOUV3K9nff5iC1p0mRXtLg9M1s4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Hn6yuxFHodeyu7ISlhYrbSf9pTiH4TDEvbYLWjTwFO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zdf4y2etKBuIpkEU1zMwoCkCsdisfXZCh8QPamm+drY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rOQ9oMdiK5xxGH+jPzOvwVqdGGnF3+HkJXxn81s6hp4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "61aKKsE3+BJHHWYvs3xSIBvlRmKswmaOo5rygQJguUg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KuDb/GIzqDM8wv7m7m8AECiWJbae5EKKtJRugZx7kR0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Q+t8t2TmNUiCIorVr9F3AlVnX+Mpt2ZYvN+s8UGict8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJRZIpKxUgHyL83kW8cvfjkxN3z6WoNnUg+SQw+LK+k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnUsYjip8SvW0+m9mR5WWTkpK+p6uwJ6yBUAlBnFKMk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PArHlz+yPRYDycAP/PgnI/AkP8Wgmfg++Vf4UG1Bf0E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wnIh53Q3jeK8jEBe1n8kJLa89/H0BxO26ZU8SRIAs9Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4F8U59gzBLGhq58PEWQk2nch+R0Va7eTUoxMneReUIA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ihKagIW3uT1dm22ROr/g5QaCpxZVj2+Fs/YSdM2Noco=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EJtUOOwjkrPUi9mavYAi+Gom9Y2DuFll7aDwo4mq0M0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dIkr8dbaVRQFskAVT6B286BbcBBt1pZPEOcTZqk4ZcI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aYVAcZYkH/Tieoa1XOjE/zCy5AJcVTHjS0NG2QB7muA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sBidL6y8TenseetpioIAAtn0lK/7C8MoW4JXpVYi3z8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0Dd2klU/t4R86c2WJcJDAd57k/N7OjvYSO5Vf8KH8sw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I3jZ92WEVmZmgaIkLbuWhBxl7EM6bEjiEttgBJunArA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aGHoQMlgJoGvArjfIbc3nnkoc8SWBxcrN7hSmjMRzos=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "bpiWPnF/KVBQr5F6MEwc5ZZayzIRvQOLDAm4ntwOi8g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tI7QVKbE6avWgDD9h4QKyFlnTxFCwd2iLySKakxNR/I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XGsge0CnoaXgE3rcpKm8AEeku5QVfokS3kcI+JKV1lk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JQxlryW2Q5WOwfrjAnaZxDvC83Dg6sjRVP5zegf2WiM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YFuHKJOfoqp1iGVxoFjx7bLYgVdsN4GuUFxEgO9HJ5s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Z6vUdiCR18ylKomf08uxcQHeRtmyav7/Ecvzz4av3k4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SPGo1Ib5AiP/tSllL7Z5PAypvnKdwJLzt8imfIMSEJQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "m94Nh6PFFQFLIib9Cu5LAKavhXnagSHG6F5EF8lD96I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pfEkQI98mB+gm1+JbmVurPAODMFPJ4E8DnqfVyUWbSo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DNj3OVRLbr43s0vd+rgWghOL3FqeO/60npdojC8Ry/M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kAYIQrjHVu49W8FTxyxJeiLVRWWjC9fPcBn+Hx1F+Ss=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aCSO7UVOpoQvu/iridarxkxV1SVxU1i9HVSYXUAeXk4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Gh6hTP/yj1IKlXQ+Q69KTfMlGZjEcXoRLGbQHNFo/1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/gDgIFQ4tAlJk3GN48IS5Qa5IPmErwGk8CHxAbp6gs0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PICyimwPjxpusyKxNssOOwUotAUbygpyEtORsVGXT8g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4lu+cBHyAUvuxC6JUNyHLzHsCogGSWFFnUCkDwfQdgI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pSndkmoNUJwXjgkbkgOrT5f9nSvuoMEZOkwAN9ElRaE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tyW+D4i26QihNM5MuBM+wnt5AdWGSJaJ4X5ydc9iWTU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9Syjr8RoxUgPKr+O5rsCu07AvcebA4P8IVKyS1NVLWc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "67tPfDYnK2tmrioI51fOBG0ygajcV0pLo5+Zm/rEW7U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "y0EiPRxYTuS1eVTIaPQUQBBxwkyxNckbePvKgChwd0M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NWd+2veAaeXQgR3vCvzlI4R1WW67D5YsVLdoXfdb8qg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PY5RQqKQsL2GqBBSPNOEVpojNFRX/NijCghIpxD6CZk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lcvwTyEjFlssCJtdjRpdN6oY+C7bxZY+WA+QAqzj9zg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWE7XRNylvTwO/9Fv56dNqUaQWMmESNS/GNIwgBaEI0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ijwlrUeS8nRYqK1F8kiCYF0mNDolEZS+/lJO1Lg93C8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8KzV+qYGYuIjoNj8eEpnTuHrMYuhzphl80rS6wrODuU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wDyTLjSEFF895hSQsHvmoEQVS6KIkZOtq1c9dVogm9I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SGrtPuMYCjUrfKF0Pq/thdaQzmGBMUvlwN3ORIu9tHU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KySHON3hIoUk4xWcwTqk6IL0kgjzjxgMBObVIkCGvk4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hBIdS9j0XJPeT4ot73ngELkpUoSixvRBvdOL9z48jY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Tx6um0q9HjS5ZvlFhvukpI6ORnyrXMWVW1OoxvgqII0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zFKlyfX5H81+d4A4J3FKn4T5JfG+OWtR06ddyX4Mxas=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cGgCDuPV7MeMMYEDpgOupqyNP4BQ4H7rBnd2QygumgM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IPaUoy98v11EoglTpJ4kBlEawoZ8y7BPwzjLYBpkvHQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Pfo4Am6tOWAyZNn8G9W5HWWGC3ZWmX0igI/RRB870Ro=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fnTSjd7bC1Udoq6iM7UDnHAC/lsIXSHp/Gy332qw+/I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fApBgVRrTDyEumkeWs5p3ag9KB48SbU4Si0dl7Ns9rc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QxudfBItgoCnUj5NXVnSmWH3HK76YtKkMmzn4lyyUYY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sSOvwhKa29Wq94bZ5jGIiJQGbG1uBrKSBfOYBz/oZeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FdaMgwwJ0NKsqmPZLC5oE+/0D74Dfpvig3LaI5yW5Fs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sRWBy12IERN43BSZIrnBfC9+zFBUdvjTlkqIH81NGt4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/4tIRpxKhoOwnXAiFn1Z7Xmric4USOIfKvTYQXk3QTc=", + "subType": "00" + } + } + ] + }, + { + "_id": { + "$numberInt": "1" + }, + "encryptedDecimal": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RGTjNVEsNJb+DG7DpPOam8rQWD5HZAMpRyiTQaw7tk8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I93Md7QNPGmEEGYU1+VVCqBPBEvXdqHPtTJtMOn06Yk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "GecBFQ1PemlECWZWCl7f74vmsL6eB6mzQ9n6tK6FYfs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QpjhZl+O1ORifgtCZuWAdcP6OKL7IZ2cA46v8FJcV28=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RlQWwhU+uVv0a+9IB5cUkEfvHBvOw3B1Sx6WfPWMqes=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ubb81XTC7U+4tcNzf1oYvOY6gR5hC2Izqx54f4GuJ0E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6M4Q5NMQ9TqNnjzGOxIkiUIY8TEL0I3XD1QnhefQUqU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BtInzk9t2FFMCEY6AQ7zN8jwrrZEs2irSv6q0Q4NaIw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6vxXfETu9cuBIpRBo3jUUU04mJIH/aAhLX8K6VI5Xv0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wXPCdS+q23zi1bkPnaVG2j0PsVtxdeSLJ//h6J1x8RU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KY3KkfBAsN2l80wbpj41G0gwBR5KmmFnZcagg7D3ENk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tI8NFAxXCX4VOnY5X73K6KI/Yspd3aR94KV39MhJlAw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "nFxH0UC3mATKA6Vboz+QX/hAjj19kF/SH6H5Cne7qC0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q8hYqIYaIi7nOdG/7qQZYnz8Bsacfi66M1nVku4SH08=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4saA92R4arp4anvD9xFtze+sNcQqTEhPHyl1h70A8NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DbIziOBRRyeQS6RtBR09E37LV+CTKrEjGoRMLSpG6eE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Fv80Plp/7w2gnVqrwawLd6qhJ10G4NCDm3re67cNq4Y=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "T/T2oiQCBBES4YN7EodzPRdabZSFlYIClHBym+bQUZE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ZQgHD3l46Ujqtbnj1VbbeM29C9wJzOhz+yZ/7XdSrxk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ltlFKzWvyZvHxDFOYDd/XXJ6kUiJj0ln2HTCEz2o4Z4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "flW8A7bltC1u8bzx0WJtxosGJdOVsJFfbx33jxnpFGg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SXO+92QbMKwUSG2t27ciunV1c3VvFkUuDmSczpRe008=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+KioGs1GM+xRBzFE67ePTWj04KMSE5/Y6qUF7nJ5kvU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L3xNVbh6YH+RzqABN+5Jgb7T234Efpn766DmUvxIxgg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hPF+60mBYPjh21dEmPlBhKgyc9S2qLtTkypYvnqP2Fc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EletRsETy2HcjaPIm2c8CkT7ch/P3pJJDC8hasepcSU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "r5bMXUaNKqLPxZ+TG9HYTG4aSDgcpim27rN8rQFkM0w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0Q7Erdr8+/S0wUEDDIqlS5XjBVWvhZY65K0uUDb6+Ns=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xEcnhXy35hbXNVBPOOt3TUHbxvKfQ48KjA9b6/rbMqQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "T8bEpiQNgsEudXvyKE9SZlSvbpV/LUaslsdqgSFltyo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hIoiaF2YjnxDbODfhFEB+JGZ5nf8suD3Shck5bwQ3N0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qnA6qzejeRJ0rsZaZ0zOvKAaXyxt5lpscKQNYFZNl4k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "anAKCL2DN/le2VaP0n2ucYSEH/DaaEH/8Sa4OqTZsRA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JCZlBJaFm618oWYSnT9Jr1MtwFVw4BZjOzO+5yWgR90=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yxyk4n9762WzcDVGnTn4jCqUnSMIVCrLDIjCX1QVj34=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fDI6fdKvDJwim5/CQwWZEzcrXE3LHgy7FTtffcC7tXE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Vex+gcz5T+WkzsVZQrkqUR2ryyZbnaOGuWpYvjN0zCw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8TLEXz+Gbbp6llHpZXVjLsdlYY9f6hrKpHVpyfDe0RY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7fTyt5BrunypS65TfOzFW2E2qdIuT4SLeDeGlbQoJCs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8fKGrkqN0/KuSjyXgDBmRauDKrSa//JBKRWHEB9xBf4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s4codmG7uN4ss6P357jL21lazEe90M9GOK5WrOknSV0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RkSpua8XF+NUdxVDU90EbLUTTyZFX3tt3atBTroFaRk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "LnTCuCDyAHK5B9KXzjtwGmWB+qergQk2OCjnIx9MI2A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cBFh0virAX4pVXf/udIGI2951i0+0aZAdJcBVGtYnT4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "G54X6myQXWZ5fw/G31en3QbdgfXzL9+hFTtJpnWMqDI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EdsiiuezcsFJFnYIyGjCOhnqMj1BOwTB5EFxN+ERUkg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dVH9MXLtk0WTwGQ3xmrhOqfropMUkDW3o6paNPGl3NU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sB3HqXKWY3pKbuEH8BTbfNIGfbY+7/ZbOc3XC+JRNNI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WHyDk62Xhqbo4/iie2aLIM4x2uuAjv6102dJSHI58oM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pNUFuHpeNRDUZ/NrtII2c6sNc9eGR1lIUlIyXKERA+0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UPa+pdCqnN0bfAptdzldQOSd01gidrDKy8KhWrpSKAI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "l+7dOAlo+HUffMqFYXL6pgUFeTbwOM9CjKQLxEoLtc4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SRnDXV/rN6C8xwMutv9E1luv3DOUio3VkgPr8Cpm7Ew=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QcH6gl+gX7xZ7OWhUNQMbndJy0Piz49pDo6RsnLkVSA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "t+uL4DnfsI/Zll/KXWW1cOKX3Hu8WIkm3pt9efCVSAQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "myutHDctku/+Uug/nD8gRbYvmx/IovtoAAC2/fz2oHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6C+cjD0e0nSCP6cPqQYbNG7SlOd6Mfvi8hyfm7Ng+D8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zg01JSoOj9oBKT0S1ldJucXzY5AKgreS+h2xJreWTOs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7qQ80/FjodHl1m1py/Oii0/9C/xWbLdhaRXQ+kkCP10=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YwWMNH07vL6c5Nhg+MRnVByhzUunu8y0VLM9z/XvR5U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Dle8bU98+fudAbc14SToZFkwvV3tcYVsjDug0NWljpc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "J+eKL1vPJmlzltvhI6Li5Fz/TJmi3Ng+ehRTcs46API=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zB3XzfFygLwC3WHkj0up+VbEd25KKoce1vOpG/5bwK4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vnVnmOnL+z2pqwE+A6cVKS0Iwy4F4/2IiElJca9bUQM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+lG5r/Fpqry3BtFuvY67+RntmHAMDoLVOSGc6ZoXPb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L5MXQertqc6uj7ADe8aWKbd1sYHPCE7P1VYVg9Zc3VI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "imKONuZgopt0bhM3GMX2WVPwQYMTobuUUEdhcLfHs4c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "eOkU1J1uVbiVFWBerbXsSIVcF2nqiicTkFy4x7kFHB8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gI0uDhXeoH/UatDQKEf4qo8FHzWZDhb/wuWTqbq/ID4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cOkd5Aa3btYhtojE/smsF/PJnULqQ4NNqTkU6KXTFmo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "AWNJMs1MTe294oFipp8Y6P0CjpkZ4qCZoClQF3XcHq8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6gJtlzXOFhGYrVbTuRMmvMlDTwXdNtR9aGBlHZPwIMw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "LEmwVGA/xsEG7UrcOoYLFu6KCXgijzFznenknuDacm8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "mIRFPTXRrGaPtp/Ydij2jgkRe4uoUvAKxW2d8b9zYL0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "B+Uv2u48WALOO0L311z+eryjYQzKJVMfdHMZPhOAFmY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "INXXp0wDyVCq+NtfIrrC2ciETmyW/dWB/48/u4yLEZ4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "se7DGo8XrlrQDLEcco1tZrQt9kDe+0RTyl2bw/quG4w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vr0m2+Zk9lbN6UgWCyn8xJWJOokU3IDYab5U5q1+CgQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XI+eJ8Gy2JktG1gICgoj1qpsfy1tKmH0kglWbaQH6DA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A+UCuNnuAUqnQzspA6TVqUPRmtZmpSex5HFw7THRxs0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xaH2Ehfljd19uo0Fvb3iwkdaiWEVQd2YPoitgEPkhSM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "S/iZBJGcc8+qZxyMtab65MMBoSglybwk3x58Nb86gnY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "w14ZE5qqY5YgkS4Zcs9YNbrQbY1XfGOOHNn9bOYnFVQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0MhGd/jEF1vjkKGp+ZMn9SjLK54jkp9W4Hg+Sp/oxaI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "92QZ73e/NRTYgCm4aifaKth6aAsKnLLccBc0zx/qUTY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WOjzemCgFJOiGIp81RSVh/tFlzSTj9eFWcBnsiv2Ycs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DrsP9CmfKPjw5yLL8bnSeAxfNzAwlb+Z8OqCiKgBY7o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lMogqg8veBv6mri3/drMe9afJiKMvevkmGcw9BedfLo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "TxqwNcY8Tg2MPpNdkPBwvfpuTttSYRHU26DGECKYQ9o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "l0u1b4b4vYACWIwfnB7PZac4oDEgjQZCzHruNPTgAIY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "iVSGQ+cCfhbWIrY/v/WBORK92elu9gfRKyGhr6r/k00=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yK1forG50diEXte8ECzjfpHeYsPyuQ/dgxbxn/nzY5k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gIfTLCD3VwnOwkC0zPXWTqaITxX6ZplA69PO2a6zolc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "O/Zxlgh3WqpzJ7+Sd8XWMVID4/GXJUUWaSqfgDUi3b0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ZQ6yv368zwahUqSUYH/StL0Qgz/TwS1CzlMjVDvCciI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "m2rPEYkjwyiKdonMrKlcF7hya4lFOAUwEePJ3SgrNx8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Mq0yl5iVKlq71bT/dT/fXOWf2n90bTnXFnOdGDN0JOc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6qDGMXipPLC2O6EAAMjO2F9xx4rdqZso4IkPpH2304U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jvQHRQQa2RIszE2LX2Hv2LbRhYawJ6qmtRt8HZzFQXg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ovJXQrkZlpeHRciKyE/WWNm5O389gRgzx1W+Dw596X4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "a4kgRNvYctGYqyQv9qScL/WkljTYVylJ9pE9KDULlxU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qV4Q48vPiCJMTjljotzYKI/zfExWpkKOSHGcAjGyDig=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jtI7zbBF+QW/aYYTkn90zzyHLXLgmy7l1bzgMb2oqic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q0KmJl9txPdn962UNvnfe6UFhdk9YaFZuTm33F+csso=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ULNdEqeZJgtmNOhN/Y9INzsE9AnxWYwOMn+pIbRXIFs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "R4oz9+wkdjpKe5tE1jpG7IURAnfvS5fLP4LrD5cZfTE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qG5Z7VhwSu/HT/YFTgDzyAAzJKq51xPw2HeEV5btYC4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OM/1DmIIZ5Qyhtq8TGkHTBEMVKjAnKRZMRXYtTG8ctc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2R5vZbljLXnDFA99YfGuRB7pAdPJVKsT25zLNMC0fUk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OMbavF2EmdAz1fHkLV3ctFEUDfriKhoT2gidwHZ9z1o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MWT4Zrw3/vVvTYMa1Is5Pjr3wEwnBfnEAPPUAHKQhNU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tBkRPfG9yxfKocQx5pAJX0oEHKPL0Tgtr+0UYe09InE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lqxpnDR/H0YgH7RcfKoNoaaRhe1SIazIeMbQ1fu9y3Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "utT1UdR22PWOTrOkZauztX613lAplV4eh/ejTRb7ZSk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "S+Y2yFyKi/a6FXhih4yGo29X8I8OT6/zwEoX6NMKT4o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QSjVppg29x6oS5yBg8OFjrFt0tuTpWCuKxfIy0k8YnE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "y3r6/Xsfvsl3HksXlVYkJgHUqpQGfICxg3x9f8Zw1qM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BSltHzEwDjFN4du9rDHAPvl22atlcTioEtt+gC5L1tk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0arGXjSN0006UnXbrWsGqhvBair569DeFDUME3Df3rA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s/DumaMad08S+PBUUcrS+v42K0z8HgcdiQtrFAEu2Qs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EzJ8Y8N0OQBTlnvrK82PdevDNZZO4E6CNgYVu8Cj6Ks=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VA4vr8jBPI5QdiPrULzzZjBMIUbG3V7Slg5zm0bFcKc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YAOvEB2ZLtq9LQiFViBHWaxxWVVonC2rNYj9tN9s3L0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hgaHMo9aAGS+nBwvqnTjZO+YkiQPY1c1XcIYeaYKHyI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YvaoLt3ZpH0atB0tNzwMjpoxRYJXl0DqSjisMJiGVBE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EMmW6CptFsiLoPOi5/uAJQ2FmeLg6mCpuVLLrRWk7Mc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1jQsNMarSnarlYmXEuoFokeBMg/090qUD9wqo1Zn8Gs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hupXNKhRpJxpyDAAP1TgJ5JMZh9lhbMk6s7D7dMS3C8=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Decimal-Aggregate.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-Decimal-Aggregate.yml new file mode 100644 index 00000000000..20c9cfd51ed --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Decimal-Aggregate.yml @@ -0,0 +1,1673 @@ +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + # Tests for Decimal (without precision) must only run against a replica set. Decimal (without precision) queries are expected to take a long time and may exceed the default mongos timeout. + topology: [ "replicaset" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDecimal', 'bsonType': 'decimal', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "FLE2 Range Decimal. Aggregate." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDecimal: { $numberDecimal: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDecimal: { $numberDecimal: "1" } } + - name: aggregate + arguments: + pipeline: [{ $match: { "encryptedDecimal": { $gt: {$numberDecimal: "0" }} } }] + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDecimal": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDecimal": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + aggregate: *collection_name + pipeline: [ + { + "$match": { + "encryptedDecimal": { + "$gt": { + "$binary": { + "base64": "CgljAAADcGF5bG9hZADZYgAABGcAnWIAAAMwAH0AAAAFZAAgAAAAAJu2KgiI8vM+kz9qD3ZQzFQY5qbgYqCqHG5R4jAlnlwXBXMAIAAAAAAAUXxFXsz764T79sGCdhxvNd5b6E/9p61FonsHyEIhogVjACAAAAAAeEtIU/sp7zwjA/VArYkCzkUUiRiQOmlTaVZvfUqbEp0AAzEAfQAAAAVkACAAAAAAPQPvL0ARjujSv2Rkm8r7spVsgeC1K3FWcskGGZ3OdDIFcwAgAAAAACgNn660GmefR8jLqzgR1u5O+Uocx9GyEHiBqVGko5FZBWMAIAAAAACMKx1UyNAN4yVafFht8jp4w4LNaJcLhnozfSMzDHD3owADMgB9AAAABWQAIAAAAAD2Zi6kcxmaD2mY3VWrP+wYJMPg6cSBIYPapxaFQxYFdQVzACAAAAAAM/cV36BLBY3xFBXsXJY8M9EHHOc/qrmdc2CJmj3M89gFYwAgAAAAAHdek2wlAQ5BQORVjudyziRTV0EKx8qbsnoDiw4HG2xaAAMzAH0AAAAFZAAgAAAAAOaNqI9srQ/mI9gwbk+VkizGBBH/PPWOVusgnfPk3tY1BXMAIAAAAAAc96O/pwKCmHCagT6T/QV/wz4vqO+R22GsZ1dse2Vg6QVjACAAAAAA3tld+twIlVWnCTXElspau5k02CwxXDjh+3u1CQtV/W4AAzQAfQAAAAVkACAAAAAA0XlQgy/Yu97EQOjronl9b3dcR1DFn3deuVhtTLbJZHkFcwAgAAAAACoMnpVl6EFJak8A+t5N4RFnQhkQEBnNAx8wDqmq5U/dBWMAIAAAAABRmXIchytMNz4J439viiY5FZ1OB3AXJBkZ3udUqHpsqAADNQB9AAAABWQAIAAAAAAOtpNexRxfv0yRFvZO9DhlkpU4mDuAb8ykdLnE5Vf1VAVzACAAAAAAeblFKm/30orP16uQpZslvsoS8s0xfNPIBlw3VkHeekYFYwAgAAAAAJQdzGMO2s1AkdKz3ZPytivrQphUFVhvhMcXjvUGzHBDAAM2AH0AAAAFZAAgAAAAAIr8xAFm1zPmrvW4Vy5Ct0W8FxMmyPmFzdWVzesBhAJFBXMAIAAAAABYeeXjJEzTHwxab6pUiCRiZjxgtN59a1y8Szy3hfkg+gVjACAAAAAA161loizzKvXS1Po/3bxeNICmKpsPSK9Q+EpkISK3jVoAAzcAfQAAAAVkACAAAAAAl+ibLk0/+EwoqeC8S8cGgAtjtpQWGEZDsybMPnrrkwEFcwAgAAAAAHPPBudWgQ+HUorLDpJMqhS9VBF2VF5aLcxgrM1s+yU7BWMAIAAAAAACyakB3CZWEjbxK0u9Sflc3+EafBAQFbvBpCxKvxuEQAADOAB9AAAABWQAIAAAAACR9erwLTb+tcWFZgJ2MEfM0PKI9uuwIjDTHADRFgD+SQVzACAAAAAAcOop8TXsGUVQoKhzUllMYWxL93xCOkwtIpV8Q6hiSYYFYwAgAAAAAJ43998lzKoVqWm99ZzKHJLeVscGNCVoKDvpUtt2rDI9AAM5AH0AAAAFZAAgAAAAALv0vCPgh7QpmM8Ug6ad5ioZJCh7pLMdT8FYyQioBQ6KBXMAIAAAAADsCPyIG8t6ApQkRk1fX/sfc1kpuWCWP8gAEpnYoBSHrQVjACAAAAAAj2h1WLr0EFEFZ31djx3BtLIbAtdE05ax54opkJo4/bQAAzEwAH0AAAAFZAAgAAAAAIW4AxJgYoM0pcNTwk1RSbyjZGIqgKL1hcTJmNrnZmoPBXMAIAAAAAAZpfx3EFO0vY0f1eHnE0PazgqeNDTaj+pPJMUNW8lFrAVjACAAAAAAqFD83E8POmo6pdqg7D+jWtngbgcV99mbQBxkbpX7ds4AAzExAH0AAAAFZAAgAAAAAKliO6L9zgeuufjj174hvmQGNRbmYYs9yAirL7OxwEW3BXMAIAAAAAAqU7vs3DWUQ95Eq8OejwWnD0GuXd+ASi/uD6S0l8MM1QVjACAAAAAAp0cwpAh7SiuStPwYNVvp83N5GWyWRWA7UGRxHDDj8g8AAzEyAH0AAAAFZAAgAAAAAOGQcCBkk+j/Kzjt/Cs6g3BZPJG81wIHBS8JewHGpgk+BXMAIAAAAABjrxZXWCkdzrExwCgyHaafuPSQ4V4x2k9kUCAqUaYKDQVjACAAAAAAJr/tEuxScrJJEMECy4itHf1y5+gO2l5sBjgM/EOLXw0AAzEzAH0AAAAFZAAgAAAAAPmCNEt4t97waOSd5hNi2fNCdWEkmcFJ37LI9k4Az4/5BXMAIAAAAABX7DuDPNg+duvELf3NbLWkPMFw2HGLgWGHyVWcPvSNCAVjACAAAAAAeNCEn5Z3A5CgCnIgaBl2N8p0t+5rRY06vGg6ePVMaTUAAzE0AH0AAAAFZAAgAAAAAD4vtVUYRNB+FD9yoQ2FVJH3nMeJeKbi6eZfth638YqbBXMAIAAAAAANCuUB4OdmuD6LaDK2f3vaqfgYYvg40wDXOBbcFjTqLwVjACAAAAAATozHsao4er/Uk5H/pKe/1JEo/3h4Pgah2RgJHXf2PeYAAzE1AH0AAAAFZAAgAAAAAJPIRzjmTjbdIvshG6UslbEOd797ZSIdjGAhGWxVQvK1BXMAIAAAAABgmJ0Jh8WLs9IYs/a7DBjDWd8J3thW/AGJK7zDnMeYOAVjACAAAAAAtD48LbnCkTvacfz8DvR1ixsbYceiJxJRV7tLqDUWvVwAAzE2AH0AAAAFZAAgAAAAAABQyKQPoW8wGPIqnsTv69+DzIdRkohRhOhDmyVHkw9WBXMAIAAAAAAqWA2X4tB/h3O1Xlawtz6ndI6WaTwgU1QYflL35opu5gVjACAAAAAAphx2eRFGSW/tm3HxilqRv5Zj5TtOy4KSGiCIm6yJANYAAzE3AH0AAAAFZAAgAAAAACB7NOyGQ1Id3MYnxtBXqyZ5Ul/lHH6p1b10U63DfT6bBXMAIAAAAADpOryIcndxztkHSfLN3Kzq29sD8djS0PspDSqERMqokQVjACAAAAAAIHYCljTfUu/Ox23Nrjz2Z6cqQpn4s0sJV/SxkC9UtIEAAzE4AH0AAAAFZAAgAAAAAKVfXLfs8XA14CRTB56oZwV+bFJN5BHraTXbqEXZDmTkBXMAIAAAAAASRWTsfGOpqdffiOodoqIgBzG/yzFyjR5CfUsIUIWGpgVjACAAAAAAGs7l0GsFNkQMcJBUuUsEOMuQupbEvi7MDcezspsCY3EAAzE5AH0AAAAFZAAgAAAAAH/aZr4EuS0/noQR9rcF8vwoaxnxrwgOsSJ0ys8PkHhGBXMAIAAAAACd7ObGQW7qfddcvyxRTkPuvq/PHu7+6I5dxwS1Lzy5XAVjACAAAAAAWnfsbdu5Qvb62rEpBR90qh+3jdYfp266dmuNPLoXys4AAzIwAH0AAAAFZAAgAAAAAKvlcpFFNq0oA+urq3w6d80PK1HHHw0H0yVWvU9aHijXBXMAIAAAAADWnAHQ5Fhlcjawki7kWzdqjM2f6IdGJblojrYElWjsZgVjACAAAAAAdNxswYIMjqVta8sLC8wniMm6gOgIgG5eWTPAxItBJJ4AAzIxAH0AAAAFZAAgAAAAAH/2aMezEOddrq+dNOkDrdqf13h2ttOnexZsJxG1G6PNBXMAIAAAAABNtgnibjC4VKy5poYjvdsBBnVvDTF/4mmEAxsXVgZVKgVjACAAAAAArWXr2Myg8T8/0eYBCUXDOsIR//zqZtlE6Lb1YZJkvYcAAzIyAH0AAAAFZAAgAAAAAF2wZoDL6/V59QqO8vdRZWDpXpkV4h4KOCSn5e7x7nmzBXMAIAAAAADLZBu7LCYjbThaVUqMK14H/elrVOYIKJQCx4C9Yjw37gVjACAAAAAAWeF6F8pBeCm27PWjQGrlyN7JHp9WqCNnwFzqZ+V51t0AAzIzAH0AAAAFZAAgAAAAAH27yYaLn9zh2CpvaoomUPercSfJRUmBY6XFqmhcXi9QBXMAIAAAAAAUwumVlIYIs9JhDhSj0R0+59psCMsFk94E62VxkPt42QVjACAAAAAAjbsmlVrfexmULer3RtGwnOf20pF0xv4Z46OHOaIwgzEAAzI0AH0AAAAFZAAgAAAAALMg2kNAO4AFFs/mW3In04yFeN4AP6Vo0klyUoT06RquBXMAIAAAAAAgGWJbeIdwlpqXCyVIYSs0dt54Rfc8JF4b8uYc+YUj0AVjACAAAAAAsNKKan8cMW1oYFmQiyPZbepuJlDD0/CQUS21++CP16kAAzI1AH0AAAAFZAAgAAAAALas/i1T2DFCEmrrLEi7O2ngJZyFHialOoedVXS+OjenBXMAIAAAAAA1kK0QxY4REcGxHeMkgumyF7iwlsRFtw9MlbSSoQY7uAVjACAAAAAAUGZ/BwF6Q9hcFBC8F5ed6RwYWJ1/OahRwljBloMgkFAAAzI2AH0AAAAFZAAgAAAAAP1TejmWg1CEuNSMt6NUgeQ5lT+oBoeyF7d2l5xQrbXWBXMAIAAAAABPX0kj6obggdJShmqtVfueKHplH4ZrXusiwrRDHMOKeQVjACAAAAAApU9zcPAQ2xOspmwhjWN90v2m2clQ4rk7WBmm/nUyOHQAAzI3AH0AAAAFZAAgAAAAAMrKn+QPa/NxYezNhlOX9nyEkN1kE/gW7EuZkVqYl0b8BXMAIAAAAABUoZMSPUywRGfX2EEencJEKH5x/P9ySUVrhStAwgR/LgVjACAAAAAAl/hDhauhBkda/I/kLr/XLxyso2guawC2isj+tDQhfMoAAzI4AH0AAAAFZAAgAAAAAMmD1+a+oVbiUZd1HuZqdgtdVsVKwuWAn3/M1B6QGBM3BXMAIAAAAACLyytOYuZ9WEsIrrtJbXUx4QgipbaAbmlJvSZVkGi0CAVjACAAAAAAVKbMdxvkfCBoqJCUVjLrtKF90a80LocckdFc7LvYuWIAAzI5AH0AAAAFZAAgAAAAAOVKV7IuFwmYP1qVv8h0NvJmfPICu8yQhzjG7oJdTLDoBXMAIAAAAABL70XLfQLKRsw1deJ2MUvxSWKxpF/Ez73jqtbLvqbuogVjACAAAAAARiC2ShVCKMjjiqB6xg+jDIYJNhhWqmceO07EC8rEXkoAAzMwAH0AAAAFZAAgAAAAAKS/1RSAQma+xV9rz04IcdzmavtrBDjOKPM+Z2NEyYfPBXMAIAAAAAAOJDWGORDgfRv8+w5nunh41wXb2hCA0MRzwnLnQtIqPgVjACAAAAAAWU2Vw2Rta8oHxPPfOWQa86tjYWndb0E3RNlZ9gthHt0AAzMxAH0AAAAFZAAgAAAAAFeq8o82uNY1X8cH6OhdTzHNBUnCChsEDs5tm0kPBz3qBXMAIAAAAABaxMBbsaeEj/EDtr8nZfrhhhirBRPJwVamDo5WwbgvTQVjACAAAAAAqcsCyJuLvxG8STrUI1Ccaga760+4fbmIPc6bXodea0gAAzMyAH0AAAAFZAAgAAAAAI8IKIfDrohHh2cjspJHCovqroSr5N3QyVtNzFvT5+FzBXMAIAAAAABXHXteKG0DoOMmECKp6ro1MZNQvXGzqTDdZ0DUc8QfFAVjACAAAAAA47u7VuqtYJ4jt5CcGrUacopqApwpqEpIpkalvCRtul0AAzMzAH0AAAAFZAAgAAAAAJkHvm15kIu1OtAiaByj5ieWqzxiu/epK6c/9+KYIrB0BXMAIAAAAACzg5TcyANk0nes/wCJudd1BwlkWWF6zw3nGclq5v3SJQVjACAAAAAAvTQRtEYdIj2+bfJNamvrgWRoBsYYjMQx9MyuFwSfKmYAAzM0AH0AAAAFZAAgAAAAAAYSOvEWWuSg1Aym7EssNLR+xsY7e9BcwsX4JKlnSHJcBXMAIAAAAABT48eY3PXVDOjw7JpNjOe1j2JyI3LjDnQoqZ8Je5B2KgVjACAAAAAA4eLwHqiSgM1pMqjK3QFgYauOnI3emnlOK3rgQPuec4AAAzM1AH0AAAAFZAAgAAAAAGQxC9L1e9DfO5XZvX1yvc3hTLtQEdKO9FPMkyg0Y9ZABXMAIAAAAADtmcMNJwdWLxQEArMGZQyzpnu+Z5yMmPAkvgq4eAKwNQVjACAAAAAAxA7XR9Cp6671TiZesvqE/EPKVPXfGDaYuDnce+MaZw0AAzM2AH0AAAAFZAAgAAAAADLHK2LNCNRO0pv8n4fAsxwtUqCNnVK8rRgNiQfXpHSdBXMAIAAAAACf16EBIHRKD3SzjRW+LMOl+47QXA3CJhMzlcqyFRW22AVjACAAAAAA9F9Ztm39rTapXoeRmzk6eUoa3MDPatwoQD5PvD+3xGEAAzM3AH0AAAAFZAAgAAAAAHiZJiXKNF8bbukQGsdYkEi95I+FSBHy1I5/hK2uEZruBXMAIAAAAADE+lZBa8HDUJPN+bF6xI9x4N7GF9pj3vBR7y0BcfFhBAVjACAAAAAArsw2CrSC2y02EiWJxViVxlYRIXtWARpUTwO/mfqS7O4AAzM4AH0AAAAFZAAgAAAAAI1oa2OIw5TvhT14tYCGmhanUoYcCZtNbrVbeoMldHNZBXMAIAAAAAAx2nS0Ipblf2XOgBiUOuJFBupBhe7nb6QPLZlA4aMPCgVjACAAAAAAaVoGxV5YH4urmcw3Zocg+QHoq+hA0u6nIkconI0MtZoAAzM5AH0AAAAFZAAgAAAAABgTWS3Yap7Q59hii/uPPimHWXsr+DUmsqfwt/X73qsOBXMAIAAAAACKK05liW5KrmEAvtpCB1WUltruzUylDDpjea//UlWoOAVjACAAAAAAAI3iOqWSA+DpgNr/neIRpt85Q8pLS/81ZKDnbC4io/0AAzQwAH0AAAAFZAAgAAAAABOAnBPXDp6i9TISQXvcNKwGDLepZTu3cKrB4vKnSCjBBXMAIAAAAADjjzZO7UowAAvpwyG8BNOVqLCccMFk3aDK4unUeft5ywVjACAAAAAAIZqKrUcbtze/K3CUKdchuVXQYkgj6jQfIeMZ3GrtYeEAAzQxAH0AAAAFZAAgAAAAAHN8hyvT1lYrAsdiV5GBdd5jhtrAYE/KnSjw2Ka9hjz9BXMAIAAAAAD794JK7EeXBs+D7yOVK7nWF8SbZ/7U8gZ7nnT9JFNwTAVjACAAAAAAQ4K5cDGLM/2L7KbMt537tsG1eiWrboKMj6pXVfBDIBoAAzQyAH0AAAAFZAAgAAAAAO93bPrq8bsnp1AtNd9ETnXIz0lH/2HYN/vuw9wA3fyFBXMAIAAAAABHlls5fbaF2oAGqptC481XQ4eYxInTC29aElfmVZgDUgVjACAAAAAAe0oLBk+ZaipcPgjjHvACAyA42cLY8i+s+V0EsarulZkAAzQzAH0AAAAFZAAgAAAAAL1YsAZm1SA0ztU6ySIrQgCCA74V6rr0/4iIygCcaJL6BXMAIAAAAADTXWTHWovGmUR1Zg9l/Aqq9H5mOCJQQrb/Dfae7e3wKAVjACAAAAAAAeXpcv0oCp9iY/U81w+WJjFjGn60KLQEURJd/pSoaJgAAzQ0AH0AAAAFZAAgAAAAAEoFAeHk0RZ9kD+cJRD3j7PcE5gzWKnyBrF1I/MDNp5mBXMAIAAAAACgHtc2hMBRSZjKw8RAdDHK+Pi1HeyjiBuAslGVNcW5tAVjACAAAAAAmSJiRfjmuwq5cYfadeYQRkPqbGYYrMNTQeyhBEAYClEAAzQ1AH0AAAAFZAAgAAAAAAW+7DmSN/LX+/0uBVJDHIc2dhxAGz4+ehyyz8fAnNGoBXMAIAAAAAA6Ilw42EvvfLJ3Eq8Afd+FjPoPcQutZO6ltmCLEr8kxQVjACAAAAAAUHZ9EwBIvDI4PUeCOzZCuVN9g9h5Zvq2mSpmejPLBE8AAzQ2AH0AAAAFZAAgAAAAANBdV7M7kuYO3EMoQItAbXv4t2cIhfaT9V6+s4cg9djlBXMAIAAAAABvz4MIvZWxxrcJCL5qxLfFhXiUYB1OLHdKEjco94SgDgVjACAAAAAAvz1koQRh+weAUstLWz/iHxFrLReN4KtQjdEU/zrKOFsAAzQ3AH0AAAAFZAAgAAAAAMoAoiAn1kc79j5oPZtlMWHMhhgwNhLUnvqkqIFvcH1NBXMAIAAAAADcJTW7WiCyW0Z9YDUYwppXhLj4Ac1povpJvcAq+i48MQVjACAAAAAARL0k3Uwe7OUwTejHMoNwK+twNHQznmo1vVUZaH7h8BIAAzQ4AH0AAAAFZAAgAAAAACI3j5QP7dWHpcT6WO/OhsWwRJNASBYqIBDNzW8IorEyBXMAIAAAAABxUpBSjXwCKDdGP9hYU+RvyR+96kChfvyyRC4jZmztqAVjACAAAAAA93/qZqyku1BJePH5hmMWsMfuMlF7TOFZp7z2wLJNdeYAAzQ5AH0AAAAFZAAgAAAAAKsbycEuQSeNrF8Qnxqw3x3og8JmQabwGqnDbqzFRVrrBXMAIAAAAACno/3ef2JZJS93SVVzmOZSN+jjJHT8s0XYq2M46d2sLAVjACAAAAAARUbN9bbz8Balyw8pmBLg3L5dy3aQ9s8IgqpVLeQzTkUAAzUwAH0AAAAFZAAgAAAAAPXIcoO8TiULqlxzb74NFg+I8kWX5uXIDUPnh2DobIoMBXMAIAAAAADR6/drkdTpnr9g1XNvKDwtBRBdKn7c2c4ZNUVK5CThdQVjACAAAAAAnJXYvnjljaE6ajRF+h90FXzJ9EJY26D0oG9t61ZMzqgAAzUxAH0AAAAFZAAgAAAAAEa03ZOJmfHT6/nVadvIw71jVxEuIloyvxXraYEW7u7pBXMAIAAAAADzRlBJK75FLiKjz3djqcgjCLo/e3yntI3MnPS48OORhgVjACAAAAAAbFS6SmTSDF3AsPceQrRwMMRoAnzL/k5avSHlIji5zYIAAzUyAH0AAAAFZAAgAAAAAKx8NLSZUU04pSSGmHa5fh2oLHsEN5mmNMNHL95/tuC9BXMAIAAAAAA59hcXVaN3MNdHoo11OcH1aPRzHCwpVjO9mGfMz4xh3QVjACAAAAAAoIvo/iOb/eXJv7w4KSQo74DOyO4uAIO09Ba7JZw9ousAAzUzAH0AAAAFZAAgAAAAAHNKAUxUqBFNS9Ea9NgCZoXMWgwhP4x0/OvoaPRWMquXBXMAIAAAAABUZ551mnP4ZjX+PXU9ttomzuOpo427MVynpkyq+nsYCQVjACAAAAAAlCTphL00SYotztwI+txbHAFTusjh1nNbiya24rM1dUEAAzU0AH0AAAAFZAAgAAAAALfGXDlyDVcGaqtyHkLT0qpuRhJQLgCxtznazhFtuyn/BXMAIAAAAABipxlXDq14C62pXhwAeen5+syA+/C6bN4rtZYcO4zKwAVjACAAAAAA431CBTW4s3IQde024W2cz5iEnj8EJ7p07esepoVDxmQAAzU1AH0AAAAFZAAgAAAAANoEr8sheJjg4UCfBkuUzarU9NFoy1xwbXjs5ifVDeA9BXMAIAAAAABPoyTf6M+xeZVGES4aNzVlq7LgjqZXJ/QunjYVusGUEAVjACAAAAAAywuLdjJJuXewOXMAr25GMBEif8+P74+pGaENUn+XIB8AAzU2AH0AAAAFZAAgAAAAAKvDiK+xjlBe1uQ3SZTNQl2lClIIvpP/5CHwY6Kb3WlgBXMAIAAAAAANnxImq5MFbWaRBHdJp+yD09bVlcFtiFDYsy1eDZj+iQVjACAAAAAAvwntX1RgeNfp5oiD+r3sAKIbMGy1yEblODDYWec1xuIAAzU3AH0AAAAFZAAgAAAAAF49IlFH9RmSUSvUQpEPUedEksrQUcjsOv44nMkwXhjzBXMAIAAAAADJtWGbk0bZzmk20obz+mNsp86UCu/nLLlbg7ppxYn7PgVjACAAAAAAKW8QTERGXwh6fE4gJsULcmu80B4BMkMaZR9USn/C6RgAAzU4AH0AAAAFZAAgAAAAAOuSJyuvz50lp3BzXlFKnq62QkN2quNU1Gq1IDsnFoJCBXMAIAAAAAAqavH1d93XV3IzshWlMnzznucadBF0ND092/2ApI1AcAVjACAAAAAAmVcLt9hf9/q+2O3K4cvFAsvnCsSofHZ5iTzIaR5YKFIAAzU5AH0AAAAFZAAgAAAAALtgC4Whb4ZdkCiI30zY6fwlsxSa7lEaOAU3SfUXr02XBXMAIAAAAACgdZ6U1ZVgUaZZwbIaCdlANpCw6TZV0bwg3DS1NC/mnAVjACAAAAAAYJOnAwVW0gPl1jkCxAL1ZAhKkJ1+ShfwyaIaipflCegAAzYwAH0AAAAFZAAgAAAAAF6PfplcGp6vek1ThwenMHVkbZgrc/dHgdsgx1VdPqZ5BXMAIAAAAACha3qhWkqmuwJSEXPozDO8y1ZdRLyzt9Crt2vjGnT7AAVjACAAAAAA1JYhGDDvtf8b2Pup5jdzQmy325tGo27n/5thjZ/GI4sAAzYxAH0AAAAFZAAgAAAAAKoLEhLvLjKc7lhOJfx+VrGJCx9tXlOSa9bxQzGR6rfbBXMAIAAAAAAIDK5wNnjRMBzET7x/KAMExL/zi1IumJM92XTgXfoPoAVjACAAAAAAhoVLxo0hXbxAzz2BWbeRxAzk08TbVuHxJkdsbCISswkAAzYyAH0AAAAFZAAgAAAAADoQv6lutRmh5scQFvIW6K5JBquLxszuygM1tzBiGknIBXMAIAAAAADAD+JjW7FoBQ76/rsECmmcL76bmyfXpUU/awqIsZdO+wVjACAAAAAAwLIixR/svwADNhtiOZ9mDF2rUqEvuPx2tUSQbhOpersAAzYzAH0AAAAFZAAgAAAAAJNHUGAgn56KekghO19d11nai3lAh0JAlWfeP+6w4lJBBXMAIAAAAAD9XGJlvz59msJvA6St9fKW9CG4JoHV61rlWWnkdBRLzwVjACAAAAAAKMVXumaH5QOt5WVHFip9xWx9e5I/5Y9uEO0UAX6dy7QAAzY0AH0AAAAFZAAgAAAAAHgYoMGjEE6fAlAhICv0+doHcVX8CmMVxyq7+jlyGrvmBXMAIAAAAAC/5MQZgTHuIr/O5Z3mXPvqrom5JTQ8IeSpQGhO9sB+8gVjACAAAAAAPMgD8Rqnd94atKnMyPjlTwthUQ710MKJVqgtwNXLFWwAAzY1AH0AAAAFZAAgAAAAANpIljbxHOM7pydY877gpRQvYY2TGK7igqgGsavqGPBABXMAIAAAAAAqHyEu9gpurPOulApPnr0x9wrygY/7mXe9rAC+tPK80wVjACAAAAAAezZExYLAbjyMsVuSrWGQ+LsXYEcp6AFMANkQ496mTXcAAzY2AH0AAAAFZAAgAAAAAGR+gEaZTeGNgG9BuM1bX2R9ed4FCxBA9F9QvdQDAjZwBXMAIAAAAABSkrYFQ6pf8MZ1flgmeIRkxaSh/Eep4Btdx4QYnGGnwAVjACAAAAAAQ011358xXtVDOyGPpF2+zGoMdiQ8PGCB1QfDcluYUF4AAzY3AH0AAAAFZAAgAAAAAFNprhQ3ZwIcYbuzLolAT5n/vc14P9kUUQComDu6eFyKBXMAIAAAAAAcx9z9pk32YbPV/sfPZl9ALIEVsqoLXgqWLVK/tP+heAVjACAAAAAA3eqduo9CWE5BePqBq7KCh5++QqXnyjCwybB15Zoeiu4AAzY4AH0AAAAFZAAgAAAAADgyPqQdqQrgfmJjRFAILTHzXbdw5kpKyfeoEcy6YYG/BXMAIAAAAAAE+3XsBQ8VAxAkN81au+f3FDeCD/s7KoZD+fnM1MJSSAVjACAAAAAAWHAiQNo6HBkIhf72fVQxJLaCtNeTqn2OuMmYZjT7PRkAAzY5AH0AAAAFZAAgAAAAAI0CT7JNngTCTUSei1Arw7eHWCD0jumv2rb7imjWIlWABXMAIAAAAABSP8t6ya0SyCphXMwnru6ZUDXWElN0NfBvEOhDvW9bJQVjACAAAAAAbfZdoqMQ71hNIsycNHl6JpSmAFDPSfgzdWkGqFZNoScAAzcwAH0AAAAFZAAgAAAAAD/FIrGYFDjyYmVb7oTMVwweWP7A6F9LnyIuNO4MjBnXBXMAIAAAAACIZgJCQRZu7NhuNMyOqCn1tf+DfU1qm10TPCfj5JYV3wVjACAAAAAAZCaO06/DjOBjNrTxB8jdxM7X4XvNu9QTa2Y00kZJwgEAAzcxAH0AAAAFZAAgAAAAAHIkVuNDkSS1cHIThKc/O0r2/ubaABTOi8Q1r/dvBAsEBXMAIAAAAADdHYqchEiJLM340c3Q4vJABmmth3+MKzwLYlsG6GS7sQVjACAAAAAA4byh4Re83zUZxvEH4iJSsnFei+ohsWxjSpB5YoAPawIAAzcyAH0AAAAFZAAgAAAAAJmoDILNhC6kn3masElfnjIjP1VjsjRavGk1gSUIjh1NBXMAIAAAAAD97Ilvp3XF8T6MmVVcxMPcdL80RgQ09UoC6PnoOvZ1IQVjACAAAAAAx9wkqPvtzrL9EFDmFvktI129ti8IAdHn0oudubGPWAAAAzczAH0AAAAFZAAgAAAAAI5bm3YO0Xgf0VT+qjVTTfvckecM3Cwqj7DTKZXf8/NXBXMAIAAAAAD/m+h8fBhWaHm6Ykuz0WX1xL4Eme3ErLObyEVJf8NCywVjACAAAAAAiGdTXD22l1zDxHeF4NXUTiBnNTsdpzJRwM6riTPuOogAAzc0AH0AAAAFZAAgAAAAANqo4+p6qdtCzcB4BX1wQ6llU7eFBnuu4MtZwp4B6mDlBXMAIAAAAAAGiz+VaukMZ+6IH4jtn4KWWdKK4/W+O+gRioQDrfzpMgVjACAAAAAAl/wlBjSJW/hKkll8HSBCGw4Ce1pJ5rZuhVE09hKq4jQAAzc1AH0AAAAFZAAgAAAAAPrFXmHP2Y4YAm7b/aqsdn/DPoDkv7B8egWkfe23XsM1BXMAIAAAAAAGhwpKAr7skeqHm3oseSbO7qKNhmYsuUrECBxJ5k+D2AVjACAAAAAAKSsxERy2OBhb5MiH9/H2BET2oeU6yVihiAoWi/16FroAAzc2AH0AAAAFZAAgAAAAABzjYxwAjXxXc0Uxv18rH8I3my0Aguow0kTwKyxbrm+cBXMAIAAAAADVbqJVr6IdokuhXkEtXF0C2gINLiAjMVN20lE20Vmp2QVjACAAAAAACJ+VAwl9X88mjC76yzkWeL4K6AamNYjbNkpS9B6fQE4AAzc3AH0AAAAFZAAgAAAAAFMm2feF2fFCm/UC6AfIyepX/xJDSmnnolQIBnHcPmb5BXMAIAAAAABLI11kFrQoaNVZFmq/38aRNImPOjdJh0Lo6irI8M/AaAVjACAAAAAAZFgYh4nV8YN+/AAqe/QhKDkNBPg8KraCG7y3bOzhPV4AAzc4AH0AAAAFZAAgAAAAAJvXB3KyNiNtQko4SSzo/9b2qmM2zU9CQTTDfLSBWMgRBXMAIAAAAAAvjuVP7KsLRDeqVqRziTKpBrjVyqKiIbO9Gw8Wl2wFTAVjACAAAAAARuBn3JdQ8so7Gy0XVH0CtlvtRoJWhSrJP6eRIqbyWh8AAzc5AH0AAAAFZAAgAAAAAPGdcxDiid8z8XYnfdDivNMYVPgBKdGOUw6UStU+48CdBXMAIAAAAAARj6g1Ap0eEfuCZ4X2TsEw+Djrhto3fA5nLwPaY0vCTgVjACAAAAAATxLY6SkcLPaoOBJpQsggEqoxgJgiY9seP3fBQM05PckAAzgwAH0AAAAFZAAgAAAAAP5rGPrYGt3aKob5f/ldP0qrW7bmWvqnKY4QwdDWz400BXMAIAAAAADTQkW2ymaaf/bhteOOGmSrIR97bAnJx+yN3yMj1bTeewVjACAAAAAA6O+wIMt3xMITuCxVrqOCNBPX5F122G+/Is+EYkzUvVYAAzgxAH0AAAAFZAAgAAAAAAlz6wJze5UkIxKpJOZFGCOf3v2KByWyI6NB6JM9wNcBBXMAIAAAAABUC7P/neUIHHoZtq0jFVBHY75tSFYr1Y5S16YN5XxC1QVjACAAAAAANv97jMd9JmZ7lMeCzK7jaZHZYsZJNl6N9g9WXzd2om0AAzgyAH0AAAAFZAAgAAAAAFJ8AtHcjia/9Y5pLEc3qVgH5xKiXw12G9Kn2A1EY8McBXMAIAAAAAAxe7Bdw7eUSBk/oAawa7uicTEDgXLymRNhBy1LAxhDvwVjACAAAAAAtOUZ2NeUWwESpq95O92dhqYBt8RtFnjT43E7k9mQF3oAAzgzAH0AAAAFZAAgAAAAAO8uwQUaKFb6vqR3Sv3Wn4QAonC2exOC9lGG1juqP5DtBXMAIAAAAABZf1KyJgQg8/Rf5c02DgDK2aQu0rNCOvaL60ohDHyY+gVjACAAAAAA5nAWj1Ek8p0MLYZEB3yoVFDfBYZ+/ZpIo71u+W9hprcAAzg0AH0AAAAFZAAgAAAAAE8YtqyRsGCeiR6hhiyisR/hccmK4nZqIMzO4lUBmEFzBXMAIAAAAAC1UYOSKqAeG1UJiKjWFVskRhuFKpj9Ezy+lICZvFlN5AVjACAAAAAA37oP30mTzVftI2FL9Uwyls/jqLqbmRDQk7p7nlx44uYAAzg1AH0AAAAFZAAgAAAAAPhCrMausDx1QUIEqp9rUdRKyM6a9AAx7jQ3ILIu8wNIBXMAIAAAAACmH8lotGCiF2q9VQxhsS+7LAZv79VUAsOUALaGxE/EpAVjACAAAAAAhVIkl8p19VZ0gpg32s++Jda3qsVSVB5tV4CKrtjhE3IAAzg2AH0AAAAFZAAgAAAAAOBi/GAYFcstMSJPgp3VkMiuuUUCrZytvqYaU8dwm8v2BXMAIAAAAACEZSZVyD3pKzGlbdwlYmWQhHHTV5SnNLknl2Gw8IaUTQVjACAAAAAAriBex2kK//RPhyVpCYJDBng4l5w8jD3m8BF7dVAP0p8AAzg3AH0AAAAFZAAgAAAAAIQuup+YGfH3mflzWopN8J1X8o8a0d9CSGIvrA5HOzraBXMAIAAAAADYvNLURXsC2ITMqK14LABQBI+hZZ5wNf24JMcKLW+84AVjACAAAAAAGeUzdG2kQrx5XypXJezZmPVzMYuqYZw7Bhbl4EPT0SMAAzg4AH0AAAAFZAAgAAAAAKsh0ADyOnVocFrOrf6MpTrNvAj8iaiE923DPryu124gBXMAIAAAAADg24a8NVE1GyScc6tmnTbmu5ulzO+896fE92lN08MeswVjACAAAAAAbO+DEBY3STVMQN7CbxmHDUBYrDR+80e797u/VmiK4vcAAzg5AH0AAAAFZAAgAAAAAKkVC2Y6HtRmv72tDnPUSjJBvse7SxLqnr09/Uuj9sVVBXMAIAAAAABYNFUkH7ylPMN+Bc3HWX1e0flGYNbtJNCY9SltJCW/UAVjACAAAAAAHlE3RLKcXpXto7Mr8nRgzEsmhjfGh4vcgPxashCSMg4AAzkwAH0AAAAFZAAgAAAAADDggcwcb/Yn1Kk39sOHsv7BO/MfP3m/AJzjGH506Wf9BXMAIAAAAAAYZIsdjICS0+BDyRUPnrSAZfPrwtuMaEDEn0/ijLNQmAVjACAAAAAA1c/sy3255NofBQrnBNSmVkSzMgsGPaaOUJShddVrnuQAAzkxAH0AAAAFZAAgAAAAAEWY7dEUOJBgjOoWVht1wLehsWAzB3rSOBtLgTuM2HC8BXMAIAAAAAAAoswiHRROurjwUW8u8D5EUT+67yvrgpB/j6PzBDAfVwVjACAAAAAALBvQumG8m7/bzJjGWN2cHSAncdN8jMtOSmEhEhGom24AAzkyAH0AAAAFZAAgAAAAAPZaapeAUUFPA7JTCMOWHJa9lnPFh0/gXfAPjA1ezm4ZBXMAIAAAAACmJvLY2nivw7/b3DOKH/X7bBXjJwoowqb1GtEFO3OYgAVjACAAAAAAGCDHuUJusGHKQ+r9nrFChmUUsRcqZKPGsRiLSk5gbFcAAzkzAH0AAAAFZAAgAAAAAK00u6jadxCZAiA+fTsPVDsnW5p5LCr4+kZZZOTDuZlfBXMAIAAAAAAote4zTEYMDgaaQbAdN8Dzv93ljPLdGjJzvnRn3KXgtQVjACAAAAAApZ6l+OrocOqgFek7WOqOP9JruTWZ+iW+5zdL3DZwzhkAAzk0AH0AAAAFZAAgAAAAAFbgabdyymiEVYYwtJSWa7lfl/oYuj/SukzJeDOR6wPVBXMAIAAAAADAFGFjS1vPbN6mQEhkDYTD6V2V23Ys9gUEUMGNvMPkaAVjACAAAAAAeICKly5Xtwmfd4JFD+5e1UWpu4V3KoRim7jeBICDs+UAAzk1AH0AAAAFZAAgAAAAABNMR6UBv2E627CqLtQ/eDYx7OEwQ7JrR4mSHFa1N8tLBXMAIAAAAAAxH4gucI4UmNVB7625C6hFSVCuIpJO3lusJlPuL8H5EQVjACAAAAAA0/uh1hrAlGGna/njCVaCqBwubxkifzF0zjCDQrIJOtUAAzk2AH0AAAAFZAAgAAAAAG8cd6WBneNunlqrQ2EmNf35W7OGObGq9WL4ePX+LUDmBXMAIAAAAAAjJ2+sX87NSis9hBsgb1QprVRnO7Bf+GObCGoUqyPE4wVjACAAAAAAAoJ4Ufdq45T9Aun5FupHlaBCIUsmUn6dXgV9KorpFikAAzk3AH0AAAAFZAAgAAAAAFWOUGkUpy8yf6gB3dio/aOfRKh7XuhvsUj48iESFJrGBXMAIAAAAAAY7sCDMcrUXvNuL6dO0m11WyijzXZvPIcOKob6IpC4PQVjACAAAAAAO/QFjczoznH95Iu0YEVMsU1GA1yxSGL8bcwSweNzAtkAAzk4AH0AAAAFZAAgAAAAAGUrIdKxOihwNmo6B+aG+Ag1qa0+iqdksHOjQj+Oy9bZBXMAIAAAAABwa5dbI2KmzBDNBTQBEkjZv4sPaeRkRNejcjdVymRFKQVjACAAAAAAUtdJucZoQvvPJiy65RonQrxBCcvtfHslpbgLbtWirCYAAzk5AH0AAAAFZAAgAAAAAOx89xV/hRk64/CkM9N2EMK6aldII0c8smdcsZ46NbP8BXMAIAAAAADBF6tfQ+7q9kTuLyuyrSnDgmrdmrXkdhl980i1KHuGHgVjACAAAAAAVc5njbLX1afpsuG662U/OBo4LanEk06lKbQtd95fswgAAzEwMAB9AAAABWQAIAAAAADJDdC9aEFl4Y8J/awHbnXGHjfP+VXQilPHJg7ewaJI7AVzACAAAAAAE+tqRl6EcBMXvbr4GDiNIYObTsYpa1n6BJk9EjIJVicFYwAgAAAAAITXOaLyGsYLUvOemQXxvnAHBwM/t3sMwfQus2aGoII+AAMxMDEAfQAAAAVkACAAAAAA3F9rjEKhpoHuTULVGgfUsGGwJs3bISrXkFP1v6KoQLgFcwAgAAAAAIBf0tXw96Z/Ds0XSIHX/zk3MzUR/7WZR/J6FpxRWChtBWMAIAAAAABzRshE3O7JzVjSGQSabvFXpen8sGUCAyr8hIIevROrjgADMTAyAH0AAAAFZAAgAAAAAOYIYoWkX7dGuyKfi3XssUlc7u/gWzqrR9KMkikKVdmSBXMAIAAAAABVF2OYjRTGi9Tw8XCAwZWLpX35Yl271TlNWp6N/nROhAVjACAAAAAAyGLbmHhe8eTQC8L2eDRcezUWULLZ9E8DPic7Mfp8/+4AAzEwMwB9AAAABWQAIAAAAACMtPm12YtdEAvqu6Eji1yuRXnu1RJP6h0l7pH3lSH4MwVzACAAAAAAENyCFfyUAh1veQBGx+cxiB7Sasrj41jzCGflZkB5cRMFYwAgAAAAAA92s3DQ3aIVOrRzusqU+xdK7cnQGCDJiyLF+su3F1ZDAAMxMDQAfQAAAAVkACAAAAAAvlI4lDcs6GB1cnm/Tzo014CXWqidCdyE5t2lknWQd4QFcwAgAAAAAD60SpNc4O2KT7J0llKdSpcX1/Xxs97N715a1HsTFkmBBWMAIAAAAABR3y/ZE3VZqBhqfUttX4bzW9wkpDoexfYZIG2fMwftcQADMTA1AH0AAAAFZAAgAAAAAKl8zcHJRDjSjJeV/WvMxulW1zrTFtaeBy/aKKhadc6UBXMAIAAAAADBdWQl5SBIvtZZLIHszePwkO14W1mQ0izUk2Ov21cPNAVjACAAAAAAHvQLJ2X4ncjVv1BsOd/jbVouRPwf4222WlGSzPyAfnsAAzEwNgB9AAAABWQAIAAAAABb6LXDWqCp1beQgQjj8I3sRTtFhlrmiBi+h/+ikmrvugVzACAAAAAA9stpgTecT7uTyaGNs3K9Bp0A7R0QaIAOfscyMXHBPX8FYwAgAAAAANTp7TwMatTSeLH3GvsrB3IOvJo+0B8HRGUFcRzJVesdAAMxMDcAfQAAAAVkACAAAAAA97NTT+81PhDhgptNtp4epzA0tP4iNb9j1AWkiiiKGM8FcwAgAAAAAKPbHg7ise16vxmdPCzksA/2Mn/qST0L9Xe8vnQugVkcBWMAIAAAAACVl3ds8xRdGFPAtZ+WtC4ojC1q5OzB6dSJoOLaJsdhhgADMTA4AH0AAAAFZAAgAAAAAMqpayM2XotEFmm0gwQd9rIzApy0X+7HfOhNk6VU7F5lBXMAIAAAAACJR9+q5T9qFHXFNgGbZnPubG8rkO6cwWhzITQTmd6VgwVjACAAAAAAEEOCUo+ihRGl1kuHlabWBWUFyJPAXSXzpQB4od57cMEAAzEwOQB9AAAABWQAIAAAAADQnslvt6Hm2kJPmqsTVYQHE/wWeZ4bE1XSkt7TKy0r1gVzACAAAAAA8URTA4ZMrhHPvlp53TH6FDCzS+0+61qHm5XK6UiOrKEFYwAgAAAAANSmECEsZgXBpoAzAIwaU3H0K/6HIutCn+ELRGBFzykkAAMxMTAAfQAAAAVkACAAAAAAhujlgFPFczsdCGXtQ/002Ck8YWQHHzvWvUHrkbjv4rwFcwAgAAAAALbV0lLGcSGfE7mDM3n/fgEvi+ifjl7WZ5b3aqjDNvx9BWMAIAAAAACKzGyyRT/3KSmGlKMHpswxj7pNu3rCmOETZ2DUhQsCBgADMTExAH0AAAAFZAAgAAAAABm/6pF96j26Jm7z5KkY1y33zcAEXLx2n0DwC03bs/ixBXMAIAAAAAD01OMvTZI/mqMgxIhA5nLs068mW+GKl3OW3ilf2D8+LgVjACAAAAAAnPa8PTfcaSSAdzNAC54IMTicbShbtt/cSnFHz7u7g8wAAzExMgB9AAAABWQAIAAAAACfw9/te4GkHZAapC9sDMHHHZgmlTrccyJDPFciOMSOcwVzACAAAAAAIIC1ZpHObvmMwUfqDRPl4C1aeuHwujM1G/yJbvybMNAFYwAgAAAAAKeagivKdHk7wAFEPHyDhsm9mDNWH5UPB+oIIlmfbzSOAAMxMTMAfQAAAAVkACAAAAAAkxHJRbnShpPOylLoDdNShfILeA1hChKFQY9qQyZ5VmsFcwAgAAAAAKidrY+rC3hTY+YWu2a7fuMH2RD/XaiTIBW1hrxNCQOJBWMAIAAAAADH8s64PC+lo8Ul4oujBlb/irtJSwu12V0HbvNzj/9qUQADMTE0AH0AAAAFZAAgAAAAAByuYl8dBvfaZ0LO/81JW4hYypeNmvLMaxsIdvqMPrWoBXMAIAAAAABNddwobOUJzm9HOUD8BMZJqkNCUCqstHZkC76FIdNg9AVjACAAAAAA4SEPY+qEZ2dCu/9485IEVybiM3Z7szXYM+izxklNm14AAzExNQB9AAAABWQAIAAAAABxMy7X5hf7AXGDz3Y/POu1ZpkMlNcSvSP92NOO/Gs7wAVzACAAAAAAHJshWo2T5wU2zvqCyJzcJQKQaHFHpCpMc9oWBXkpUPoFYwAgAAAAANeYm2aAT9f6T9uwQyH4sw2DUQyKTpo/CHt+Bb67ao4TAAMxMTYAfQAAAAVkACAAAAAAlqbslixl7Zw3bRlibZbe/WmKw23k8uKeIzPKYEtbIy0FcwAgAAAAAHEKwpUxkxOfef5HYvulXPmdbzTivwdwrSYIHDeNRcpcBWMAIAAAAAASUKCgxGu2U42qfKiyMimLPiZBMurf5v8lQJUSrhAWIAADMTE3AH0AAAAFZAAgAAAAAMXrXx0saZ+5gORmwM2FLuZG6iuO2YS+1IGPoAtDKoKBBXMAIAAAAADIQsxCr8CfFKaBcx8kIeSywnGh7JHjKRJ9vJd9x79y7wVjACAAAAAAPzOTJ088k9QdGUpZ1ubUdkw3pTGkNF8bui4a9wqeo08AAzExOAB9AAAABWQAIAAAAAAm83FA9yDUpwkbKTihe7m53u+DivS9BU2b4vQMtCVQ2AVzACAAAAAAz3m1UB/AbZPa4QSKFDnUgHaT78+6iGOFAtouiBorEgEFYwAgAAAAAHuM0gIDED47c9rdQ4MVtVzpVfn5zTF7eokO4lew4EM+AAMxMTkAfQAAAAVkACAAAAAAJRzYK0PUwr9RPG2/7yID0WgcTJPB2Xjccp5LAPDYunkFcwAgAAAAAIIh24h3DrltAzNFhF+MEmPrZtzr1PhCofhChZqfCW+jBWMAIAAAAAD6LppOeSiLHFI7EOBAz9pZPKU7LWbRWIINTlqw4zmJ5gADMTIwAH0AAAAFZAAgAAAAAHSaHWs/dnmI9sc7nB50VB2Bzs0kHapMHCQdyVEYY30TBXMAIAAAAACkV22lhEjWv/9/DubfHBAcwJggKI5mIbSK5L2nyqloqQVjACAAAAAA1I5pLbnlFf/EfJlhE0RFIioDjzKrWNh4TO8H/ksrbMcAAzEyMQB9AAAABWQAIAAAAAAC/I4TQRtCl12YZmdGz17X4GqSQgfwCPgRBwdHmdwu+QVzACAAAAAAx8f3z2ut/RAZhleari4vCEE+tNIn4ikjoUwzitfQ588FYwAgAAAAADX1qVlsvYru6YDBMtmlQoIBhLSbVykBtft8wn/A+ohJAAMxMjIAfQAAAAVkACAAAAAADGB5yU2XT0fse/MPWgvBvZikVxrl5pf3S5K1hceKWooFcwAgAAAAAIxTmlLHMjNaVDEfJbXvRez0SEPWFREBJCT6qTHsrljoBWMAIAAAAAD+RiUk9IWfaDWXtzLp2NX/vCCeU9amFnQgpJjy56dQXQADMTIzAH0AAAAFZAAgAAAAABmO7QD9vxWMmFjIHz13lyOeV6vHT6mYCsWxF7hb/yOjBXMAIAAAAACT9lmgkiqzuWG24afuzYiCeK9gmJqacmxAruIukd0xEAVjACAAAAAAyiZxtuOZQNSit4wFMJS3jnqLbDzyJ0OtDTKs6r0EO0cAAzEyNAB9AAAABWQAIAAAAAAfPUoy7QyZKhIIURso+mkP9qr1izbjETqF5s22GwjCjAVzACAAAAAAvLMsIDQ/go4VUxeh50UHmsvMvfx51cwyONnRD2odvC0FYwAgAAAAAMwY/KvSIOLoSHOq4TQ1V9ZSCPZc34SdPooPL4gYXa7UAAMxMjUAfQAAAAVkACAAAAAAE3ZqUar0Bq2zWbARE0bAv98jBlK9UJ73/xcwdMWWlSkFcwAgAAAAAK4M+MmC+9sFiFsumMyJZQKxWmmJiuG9H7IzKw083xxkBWMAIAAAAACpywsi9vc979ApgH3BDzph7Y4VIAkQhji97sswzdLAYwADMTI2AH0AAAAFZAAgAAAAAMo8FJiOq63cAmyk2O7eI7GcbQh/1j4RrMTqly3rexftBXMAIAAAAADjVmpd0WiRGTw/gAqEgGolt2EI7Csv14vKdmYoMD0aAgVjACAAAAAAEPmYjDZnMZdkaYlLZLlRgist9kCrSQe0mSuKxPwwYbQAAzEyNwB9AAAABWQAIAAAAACu5IGaIx7A3Jvly/kzlCsSA4s3iJwuIl8jEdRH0k93NwVzACAAAAAA9NRUyxYE+t0Xyosyt6vIfMFW/vBoYg6sR+jBNs4JAxIFYwAgAAAAAKMbsr4JCCodHmkBvfhOe7BJigKiJik95wLeu3PjLSpXAAMxMjgAfQAAAAVkACAAAAAALkRy0GJInXYLA+cgjs6Myb0a+Gu9hgXhHvhLNoGWfckFcwAgAAAAANbALyt9zCSvwnLaWCd2/y2eoB7qkWTvv1Ldu8r40JPuBWMAIAAAAABi0dME7AUSoeMv4yIgnGMgrX9G+Uk6HV+I9/9zzABcZwADMTI5AH0AAAAFZAAgAAAAAGoUK/DSWhT8LZhszSUqDbTrp8cSA7rdqmADKL+MILtTBXMAIAAAAABHnEE9bVa6lvhfhEMkkV2kzSSxH/sMW/FIJuw3CzWs6wVjACAAAAAAagOaxIb7co+HxAXhfI9ahHLUNqmGuN/4JxeR9sm6PdkAAzEzMAB9AAAABWQAIAAAAAC922ZDQE3h2fQKibGMZ9hV0WNlmrPYYSdtaSyYxsWYqgVzACAAAAAAagMovciKK6WVjIc2cCj8nK5O/gVOFFVeVAJpRp89tmQFYwAgAAAAAAMsWoODZkTvKSNWzloogbp/AiFFspENp49RwKjWpMLHAAMxMzEAfQAAAAVkACAAAAAAtK+JqX3K/z2txjAU15DgX4y90DS2YLfIJFolCOkJJJwFcwAgAAAAAMnR5V7gfX7MNqqUdL5AkWlkhyFXaBRVNej+Rcn8lrQkBWMAIAAAAADOmDHuXwsu9uwKYYSN5XIb/TG3K5DB5aci/X1ATKTaSwADMTMyAH0AAAAFZAAgAAAAAAeGrIMK/bac6kPczxbvRYqKMkcpeI2FjdMpD91FDWIvBXMAIAAAAAAix62z1LeS8yvSXCl5gHSIomjyx76fF3S1lp9k900hygVjACAAAAAAUK1Gzlm0meDhYPL7ZgKaCD0qz7Zb5rtnPn1EEW3UZxoAAzEzMwB9AAAABWQAIAAAAACphf298InM0Us4HT8o1W1MGw0D/02vd7Jh+U0h7qaFaQVzACAAAAAAFXtk7YpqsOJxsqGWSIL+YcBE96G3Zz9D31gPqDW94y8FYwAgAAAAAEA5mnfyccI1SjyYftq3qCuORs/NxzzqUA+fxn3FRjVsAAMxMzQAfQAAAAVkACAAAAAATLDS2cuDVM3yDMuWNgk2iGKBTzPpfJMbvxVOSY39ZfcFcwAgAAAAAPT5wRi2cLHIUflXzm6EQB/m7xdThP80ir1VV/JBBqvxBWMAIAAAAABTj7i+CxxNVkbnAkSYga/v2X3/NH3nD/FRiOo2FPkzvAADMTM1AH0AAAAFZAAgAAAAAJNjExiZVX7jfFGfYpQu16qxLN0YPqVU/5CQ/Y67YSinBXMAIAAAAABMpm2+6KrkRUlXzQoMPHrQmIO6dkQz66tYdfTeA3dKqQVjACAAAAAATkNGECGUFzo34ItVnTEmxbe8dbde8QzMgEEdjMhRLz8AAzEzNgB9AAAABWQAIAAAAABlFkYtLCx901X6QVVMkSn6Z7k30UF4xHaA0OZJJ9bdyQVzACAAAAAATez+F9GHcGzTp7jjv4feboUNb8JCkIp4EqcPFisnq7MFYwAgAAAAAOHfn3eVRhSWn61AfZuAnCGleSLluJ3sF9IFGUWrCsBaAAMxMzcAfQAAAAVkACAAAAAA8NJKN0IxZnruhswGQkiruv8Ih0EMwDcSZx/Xasup9dkFcwAgAAAAAKaJZRxzA+Igeydvuk6cSwUHXcrmT4PjhuPu//FslpdnBWMAIAAAAAC3Cs/mKg+sbU/BC9QWFglOau6FWAP1sJqqSZOEOdzh+gADMTM4AH0AAAAFZAAgAAAAABHxHdEClz7hbSSgE58+dWLlSMJnoPz+jFxp4bB1GmLQBXMAIAAAAAD3nSvT6aGD+A110J/NwEfp0nPutlmuB5B+wA3CC3noGAVjACAAAAAA56lpvjMjFzR6NMGiSF1es1X01dwINWqaxyq3UUL8XJ0AAzEzOQB9AAAABWQAIAAAAAC/o8qW/ifk3KuJ01VFkyNLgQafxB5/bGs2G5VyyVafOwVzACAAAAAA1bMqAFGDHSl6BYNLbxApvkAv2K1/oafywiX0MDz1dGUFYwAgAAAAAMjJrrd6E5DyUuXkWZsAqo020Qvom6xqAluQLod5SmtdAAMxNDAAfQAAAAVkACAAAAAAY2V8I1bz3a1AxTtmED6UhdhA09huFkuuEX8R+d/WDPUFcwAgAAAAAPTVoNRiI76tcRKqd+JBBVyy4+YcKST42p0QX2BtmQ2VBWMAIAAAAAD0kQXxOWWOfIzqhJk5Rnvp/h5VXcGxxLZ8HMvVVH++YQADMTQxAH0AAAAFZAAgAAAAAO2kSsW0WGN9AOtK4xK2SHrGhWiaAbMEKT4iZkRpaDN/BXMAIAAAAABKGzQcPM8LT2dwOggxoWjv/1imYWabbG/G4kBw8OWaxAVjACAAAAAAT3sD7PTrAPtOtiSRrG4IKmSNr/uWgX8TvIyZpZzj4wkAAzE0MgB9AAAABWQAIAAAAAAiCwzNEEaH/mDam68IdDftnhthyUFdb+ZCNSBQ91WlHQVzACAAAAAA7tHyHcxCzmbJeFYZyPm4mEgkTGKOvwY4MX82OvH0Jn8FYwAgAAAAAP4A3nd470CwvaGzyJLfzbrw7ePJV35V3Cw6tuiPFlsCAAMxNDMAfQAAAAVkACAAAAAAGuCHVNJSuoVkpPOnS5s89GuA+BLi2IPBUr2Bg1sWEPIFcwAgAAAAAEl1gncS5/xO7bQ/KQSstRV3rOT2SW6nV92ZANeG2SR6BWMAIAAAAADi6/zM3xSBXEfpRpvEdeycRDl08SmwwLey72qgZaY7FwADMTQ0AH0AAAAFZAAgAAAAAMfxz7gEaoCdPvXrubDhCZUS0ARLZc1svgbXgMDlVBPgBXMAIAAAAAB6a5dDA3fuT5Vz2KvAcbUEFX/+B7Nw2p1QqbPoQ5TTuAVjACAAAAAAB4Kp5ewWpFMJ2T+QCrfw2eJV70L4M+GM8khV1JwqOqQAAzE0NQB9AAAABWQAIAAAAACnYsqF/VzmjIImC9+dqrHO1TM6lJ6fRwM0mM6Wf6paOwVzACAAAAAA5tgZzch8uDCR1ky3SllVaKVpxAlbrhvlNDTazZZRZOAFYwAgAAAAALDJDWkmExE4L8hf2CDNLuF+dgivUGXb7ZvBp90EALm+AAMxNDYAfQAAAAVkACAAAAAAEC0sIVmadtW4YMuRXH7RpAhXclsd+3bmqGXCMeaT014FcwAgAAAAABPpXh0uzpsJJB+IRUNajmMB9WGwswfpw5T9xk3Xj6ANBWMAIAAAAABauth7HsI90juToIGY6149TbrxP8Emoa+5Izilj9zeWQADMTQ3AH0AAAAFZAAgAAAAABaIB3n49Xm9cOafSrQsE0WCcYp8rMIO/qVwIlMF5YLRBXMAIAAAAAC9EyWJV3xOu9bzgdJ/yX+ko7qLf1u3AxNMataW2C9EzQVjACAAAAAAy7IAYSgbqP7CyvurHMoEq1eO08TIPNO/XcS1L5RKXHkAAzE0OAB9AAAABWQAIAAAAAA5rZItA/cocRnngYqcJ3nBXQ+l688aKz3EQyLbYYunPAVzACAAAAAAwKyA+L7TgxztPClLrIMk2JXR+w7c04N3ZOqPgjvrIvsFYwAgAAAAABaAwqPDgkTaz4888/3PeF64+vtHbLtAZgFgEurWT5kbAAMxNDkAfQAAAAVkACAAAAAA3htn7oHJ0YYpIrs+Mzyh85Ys67HwAdv5LQl1mCdoMWkFcwAgAAAAAEHjCtNNLenHuSIYux6ezAHsXDaj2DlTF67ToDhDDe6HBWMAIAAAAAAVwUZIMifuPtmiFSEctLt+bOFo1T4fgGiWNsZAP0ddeQADMTUwAH0AAAAFZAAgAAAAAEnt18Km/nqggfIJWxzTr9r3hnXNaueG6XO9A5G11LnGBXMAIAAAAAD7QxzGMN/ard5TfFLecE6uusMmXG2+RBsBR+/NCQHUwAVjACAAAAAAIPTDniUskKj5mEzULdlKtIDquwjcqkwunJyhmBazNNcAAzE1MQB9AAAABWQAIAAAAAAVAKK/GoY8AACu/hyMpO4hdLq6JnEyWNzkyci9sbaD/wVzACAAAAAA2HmeqpMlvvBpV2zQTYIRmsc4MFlfHRwLof0ycJgMg/MFYwAgAAAAACHHCAszhxijRN9Es+XRQxXo7JQ6g2qM6f5sIdMy0VKbAAMxNTIAfQAAAAVkACAAAAAAhHyq1GQC/GiMwpYjcsfkNxolJ10ARKjIjfkW1Wipzi0FcwAgAAAAAD/uaGWxTDq87F8XZ6CrFI+RNa8yMqfSZdqK00Kj833BBWMAIAAAAAD1mJD7i29l2xloVHwS1n0CbkyodWeFfZM1KF1r4HqNIgADMTUzAH0AAAAFZAAgAAAAABAJJxHoZD+MQBWqm9UM9Dd3z5ZohIZGWRaRVRsMptKQBXMAIAAAAADrE/ca+gqj/SH4oao4wE4qn2ovoTydzcMbDbrfnUs3zAVjACAAAAAAAIUpF9mApVCMCckE7S+K9RL6TPtlTTYuzUsj9E6oIpUAAzE1NAB9AAAABWQAIAAAAAAv01wz7VG9mTepjXQi6Zma+7b/OVBaKVkWNbgDLr1mFgVzACAAAAAA0I5sxz8r6wkCp5Tgvr+iL4p6MxSOq5d3e1kZG+0b7NkFYwAgAAAAAKwOaUTAOw6Y1F5vFrbDf8yVua5Fm6hKhkdIrzejuaatAAMxNTUAfQAAAAVkACAAAAAAWXecRwxSon68xaa9THXnRDw5ZfzARKnvvjTjtbae6T0FcwAgAAAAAPh0UfUMEo7eILCMv2tiJQe1bF9qtXq7GJtC6H5Va4fIBWMAIAAAAAASanx14WR2rmomuZ2x/nuD8j75pYzz+ZAt/v7uRcoOEQADMTU2AH0AAAAFZAAgAAAAAEY5WL8/LpX36iAB1wlQrMO/xHVjoO9BePVzbUlBYo+bBXMAIAAAAABoKcpadDXUARedDvTmzUzWPe1jTuvD0z9oIcZmKuiSXwVjACAAAAAAWhdUWP186dYAIDx6RtC0o/lzwYNvYBXm4RWFMbcU3YkAAzE1NwB9AAAABWQAIAAAAADnJnWqsfx0xqNnqfFGCxIplVu8mXjaHTViJT9+y2RuTgVzACAAAAAAWAaSCwIXDwdYxWf2NZTly/iKVfG/KDjHUcA1BokN5sMFYwAgAAAAAPS3GDNsHgCuTgEWTHiEStoY0VMzlopZ8L7wPUquK7ayAAMxNTgAfQAAAAVkACAAAAAAsPG7LaIpJvcwqcbtfFUpIjj+vpNj70Zjaw3eV9T+QYsFcwAgAAAAAJQ71zi0NlCyY8ZQs3IasJ4gB1PmWx57HpnlCf3+hmhqBWMAIAAAAABmDfQAQeyeutCB+wSn4RmErwYEkYpyBeCYA+3iutFzAQADMTU5AH0AAAAFZAAgAAAAAAGiSqKaQDakMi1W87rFAhkogfRAevnwQ41onWNUJKtuBXMAIAAAAAASgiDpXfGh7E47KkOD8MAcX8+BnDShlnU5JAGdnPdqOAVjACAAAAAAucFM6KF1YHEhH9vn16ox+VSqNEykMGaJzhLPsLI7qk8AAzE2MAB9AAAABWQAIAAAAAB7L4EnhjKA5xJD3ORhH2wOA1BvpnQ+7IjRYi+jjVEaJAVzACAAAAAAuhBIm0nL3FJnVJId+7CKDASEo+l2E89Z9/5aWSITK4AFYwAgAAAAALxpb5qDek038/wz4E//KIrwwo0voaqwlB7cTXSQ44EHAAMxNjEAfQAAAAVkACAAAAAAaROn1NaDZFOGEWw724dsXBAm6bgmL5i0cki6QZQNrOoFcwAgAAAAANVT8R6UvhrAlyqYlxtmnvkR4uYK/hlvyQmBu/LP6/3ZBWMAIAAAAAC1ONheOA57vNof7iZM3b82xk1abqT0bppzjQnHCmnUIwADMTYyAH0AAAAFZAAgAAAAADqSR5e0/Th59LrauDA7OnGD1Xr3H3NokfVxzDWOFaN7BXMAIAAAAACt30faNwTWRbvmykDpiDYUOCwA6QDbBBYBFWS7rdOB4AVjACAAAAAAQZlvU/gzOwW2rcr5+W43Q6EkfX8oX1TpRJWEAZAULx4AAzE2MwB9AAAABWQAIAAAAADmtb1ZgpZjSeodPG/hIVlsnS8hoRRwRbrTVx89VwL62AVzACAAAAAAi38e1g6sEyVfSDkzZbaZXGxKI/zKNbMasOl2LYoWrq8FYwAgAAAAAFWkHkxPA28FjeUjeRILC0tX8OJcanhPP68QxM5s1kcMAAMxNjQAfQAAAAVkACAAAAAAf/f7AWVgBxoKjr7YsEQ4w/fqSvuQWV2HMiA3rQ7ur0sFcwAgAAAAADkkeJozP6FFhUdRIN74H4UhIHue+eVbOs1NvbdWYFQrBWMAIAAAAADS3qKdAEzPtGMFpsisEhnOfF0zfxmLXjHKKaNCzX4PhQADMTY1AH0AAAAFZAAgAAAAAI2WEOymtuFpdKi4ctanPLnlQud+yMKKb8p/nfKmIy56BXMAIAAAAADVKrJmhjr1rfF3p+T+tl7UFd1B7+BfJRk0e7a4im7ozgVjACAAAAAAxeqPjcgDwae+AgEXg3hU118dHoy6rcHd6jAJrGik+EcAAzE2NgB9AAAABWQAIAAAAAAuHU9Qd79hjyvKOujGanSGDIQlxzsql8JytTZhEnPw+AVzACAAAAAAjF2gV/4+sOHVgDd/oR5wDi9zL7NGpGD+NsEpGXy/a4QFYwAgAAAAAIFYquIAyUn3D2peptolq3s8oIgSLEIctqBYx6qEeQTxAAMxNjcAfQAAAAVkACAAAAAA7dQDkt3iyWYCT94d7yqUtPPwp4qkC0ddu+HFdHgVKEkFcwAgAAAAANuYvtvZBTEq4Rm9+5eb7VuFopowkrAuv86PGP8Q8/QvBWMAIAAAAABBO8poBKW/RWjdHBARy3Rzghz3frQIZEPE/nSKWYlVPgADMTY4AH0AAAAFZAAgAAAAAAwnZSDhL4tNGYxlHPhKYB8s28dY5ScSwiKZm3UhT8U3BXMAIAAAAABDoY6dhivufTURQExyC9Gx3ocpl09bgbbQLChj3qVGbgVjACAAAAAA4JWOs1XAUgX3rGBBGEDDIC9/i9khJ+FgIcwfvj/SnS0AAzE2OQB9AAAABWQAIAAAAAC2VuRdaC4ZJmLdNOvD6R2tnvkyARteqXouJmI46V306QVzACAAAAAAMn1Z6B35wFTX9mEYAPM+IiJ5hauEwfD0CyIvBrxHg7IFYwAgAAAAAMRQnV65wbGlT+7Wj/HvGPLKg8NBnhkQ8Hx/MIyWvynTAAMxNzAAfQAAAAVkACAAAAAAVerb7qVNy457rNOHOgDSKyWl5ojun7iWrv1uHPXrIZQFcwAgAAAAAIDcYS9j5z+gx0xdJj09L7876r/vjvKTi/d3bXDE3PhyBWMAIAAAAAANDuaZsI2eS6/qiSdG9pCLjcPdKS96xWUJr84V/1La7gADMTcxAH0AAAAFZAAgAAAAAH/E44yLxKCJjuSmU9A8SEhbmkDOx1PqqtYcZtgOzJdrBXMAIAAAAABgLh9v2HjBbogrRoQ82LS6KjZQnzjxyJH4PH+F3jupSAVjACAAAAAAd93miIlZ/uuhzjVBt0BPjzeewxih6TSzkzbsNhpMjA8AAzE3MgB9AAAABWQAIAAAAAAlNUdDL+f/SSQ5074mrq0JNh7CTXwTbbhsQyDwWeDVMwVzACAAAAAANIH2IlSNG0kUw4qz0budjcWn8mNR9cJlYUqPYdonucAFYwAgAAAAAF9OccClBu1j8BVMDmMtJyCxnhWCbg9FuY/8nhuFGJknAAMxNzMAfQAAAAVkACAAAAAASZ+CvUDtlk/R4HAQ3a+PHrKeY/8ifAfh0oXYFqliu80FcwAgAAAAAJelpzPgM65OZFt/mvGGpwibclQ49wH+1gbUGzd9OindBWMAIAAAAAC63uwNXVbd1VP5If++NprVRsHCbomU9Iq3GxCttNAweAADMTc0AH0AAAAFZAAgAAAAAN9fkoUVbvFV2vMNMAkak4gYfEnzwKI3eDM3pnDK5q3lBXMAIAAAAACnDkgVNVNUlbQ9RhR6Aot2nVy+U4km6+GHPkLr631jEAVjACAAAAAA5d2REAVBZI9rUfeqJ9jhePmRXvrGolUbMTFiC78k/OIAAzE3NQB9AAAABWQAIAAAAAASyK3b1nmNCMptVEGOjwoxYLLS9fYWm/Zxilqea0jpEQVzACAAAAAADDHsGrbqlKGEpxlvfyqOJKQJjwJrzsrB7k3HG0AUJbkFYwAgAAAAACZHzWGY3PEL4uPSKPezGGJkyScYw/Vt3bKmX5inVMaOAAMxNzYAfQAAAAVkACAAAAAAlSP9iK31GlcG9MKGbLmq+VXMslURr+As736rrVNXcsUFcwAgAAAAAAvbj0zfq9zzi8XReheKFbCB+h9IsOLgXPPpI5vrEJNZBWMAIAAAAABb9Y2Ox3MeHCfGnMxpR7nk6hwIJg0J8/tT8fZHwxMCBQADMTc3AH0AAAAFZAAgAAAAAOJNORH8Bev97gVU7y6bznOxJ+E6Qoykur1QP76hG1/7BXMAIAAAAAC+C1PtOOrSZgzBAGhr+dPe/kR0JUw9GTwLVNr61xC1aAVjACAAAAAA4rDk/UlRHudzR12i5Ivcb5Pxwn63JX2Sjf2xrlALL7kAAzE3OAB9AAAABWQAIAAAAAAKcHzLUomavInN6upPkyWhAqYQACP/vdVCIYpiy6U6HgVzACAAAAAATsR4KItY6R2+U7Gg6sJdaEcf58gjd1OulyWovIqfxKcFYwAgAAAAAEac2NX3v0n87D/eUM/a3Gj+5Axs896SCidMyOMaOz6zAAMxNzkAfQAAAAVkACAAAAAAqTOLiMpCdR59tLZzzIPqJvbCNvz2XQL9ust0qYaehtcFcwAgAAAAAArefox/3k5xGOeiw2m6NUdzuGxmPwcu5IFcj+jMwHgHBWMAIAAAAABK08kOnYjDWhLJ70iVhd3XEj0Q0srEQttJTawBm6GuOwADMTgwAH0AAAAFZAAgAAAAAIPSqSeVzSRgNVNmrPYHmUMgykCY27NbdDUNhE5kx/SgBXMAIAAAAAAhX90nNfxyXmZe/+btZ7q6xMX4PFyj0paM1ccJ/5IUUQVjACAAAAAA/9XTbq8UVKJbIeYtfNg8pdDrpDqbCwVr5Rq3Tb4dFwwAAzE4MQB9AAAABWQAIAAAAACLn5DxiqAosHGXIAY96FwFKjeqrzXWf3VJIQMwx1fl4gVzACAAAAAAindvU27nveutopdvuHmzdENBbeGFtI3Qcsr07jxmvm8FYwAgAAAAAJ3GMHBeIaCyFV5zBgcUMpjfIDcdoaTYQSyQN2shVJMWAAMxODIAfQAAAAVkACAAAAAA7i2S6rHRSPBwZEn59yxaS7HiYBOmObIkeyCcFU42kf8FcwAgAAAAAGb3RSEyBmgarkTvyLWtOLJcPwCKbCRkESG4RZjVmY4iBWMAIAAAAABKIdcHRSJ3dnevw5Pj0BfULPqmDOA7KBDREMGv5rXUEAADMTgzAH0AAAAFZAAgAAAAAFz+M+H/Z94mdPW5oP51B4HWptp1rxcMWAjnlHvWJDWrBXMAIAAAAACBFEOQyL7ZHu4Cq33QvXkmKuH5ibG/Md3RaED9CtG5HwVjACAAAAAABf2EvIMumZohp2E/sHWEJW4VMU3ez95hwWU9pjKGRCYAAzE4NAB9AAAABWQAIAAAAAAE7c2x3Z3aM1XGfLNk/XQ9jCazNRbGhVm7H8c2NjS5ywVzACAAAAAARJ9h8fdcwA19velF3L/Wcvi2rCzewlKZ2nA0p8bT9uwFYwAgAAAAAPl1vH6JaJGZSJ8au0NK58X4gqleryub8slUhA0HRtzmAAMxODUAfQAAAAVkACAAAAAAVoRt9B9dNVvIMGN+ea5TzRzQC+lqSZ8dd/170zU5o9cFcwAgAAAAAEwM95XZin5mv2yhCI8+ugtKuvRVmNgzzIQN0yi1+9aIBWMAIAAAAAA+rgph1avVrhyHl/6G+HtASJSiJkv7u2UPYj+25fwtLAADMTg2AH0AAAAFZAAgAAAAAKRDkjyWv/etlYT4GyoXrmBED2FgZHnhc+l9Wsl06cH2BXMAIAAAAABohlpm3K850Vndf3NmNE0hHqDlNbSR8/IvMidQ3LnIZAVjACAAAAAAKw6WqWDrI7ZfyndtrftL3uh7BaF/FQxhkh8b0Zl547gAAzE4NwB9AAAABWQAIAAAAAC3DFh5oklLCNLY90bgWm68dFXz65JpAZSp1K99MBTPAQVzACAAAAAAQgZecmxEUZVHoptEQClDwAf8smI3WynQ/i+JBP0g+kQFYwAgAAAAADmUpVNJUwrydIllFvI/aau1703nk6tF2sfzkgFlWsKCAAMxODgAfQAAAAVkACAAAAAAQ5WKvWSB503qeNlOI2Tpjd5blheNr6OBO8pfJfPNstcFcwAgAAAAAKwHgQLSDJ5NwLBQbY5OnblQIsVDpGV7q3RCbFLD1U4/BWMAIAAAAADKOab9GTEiaybo9o4J72Pb9LiQU/CuKoQm6Jtlmo/IeQADMTg5AH0AAAAFZAAgAAAAAGfhFY3RGRm5ZgWRQef1tXxHBq5Y6fXaLAR4yJhrTBplBXMAIAAAAACKEF0ApLoB6lP2UqTFsTQYNc9OdDrs/vziPGzttGVLKQVjACAAAAAAvJcnqBNCZgHe+Q7Cqk+yiAaIuSJIRItXEsAlKy9PES4AAzE5MAB9AAAABWQAIAAAAAAIM73gPcgzgotYHLeMa2zAU4mFsr7CbILUZWfnuKSwagVzACAAAAAAJCSu98uV8xv88f2BIOWzt6p+6EjQStMBdkGPUkgN79cFYwAgAAAAABQqDP10DcaqEY7vxFS7U/9xcadDASONow1+xmVsPZ11AAMxOTEAfQAAAAVkACAAAAAAkWmHCUsiMy1pwZTHxVPBzPTrWFBUDqHNrVqcyyt7nO8FcwAgAAAAAMv2CebFRG/br7USELR98sIdgE9OQCRBGV5JZCO+uPMgBWMAIAAAAABxLEFq6nFVI3WaYhggo1d6oeDsBSm4wCW0T6D0yE/puwADMTkyAH0AAAAFZAAgAAAAAGInUYv0lP/rK7McM8taEHXRefk8Q2AunrvWqdfSV7UaBXMAIAAAAACE+WPxJ3gan7iRTbIxXXx+bKVcaf8kP4JD8DcwU0aL7wVjACAAAAAAiThzC5jAZ5D6xuCIsUD1aLy7KxmT2Fb8ybTkLxvj17MAAzE5MwB9AAAABWQAIAAAAACmHlg2ud3cplXlTsNTpvNnY6Qm1Fce0m899COamoDjaQVzACAAAAAArtJQeJIlepBWRU2aYar7+YGYVQ7dfDc1oxgTmA8r9q0FYwAgAAAAADZpcxtBFv18EdmhxsoxlDs253tNB5zqVY/h+nFD1isIAAMxOTQAfQAAAAVkACAAAAAApzcWSAbZWV/Rq+ylRNqqlJqNVR4fhXrz4633/MQOQgcFcwAgAAAAAN/jz/bsEleiuCl+li83EWlG6UMHA8CyaOMRKCkXkSCPBWMAIAAAAADSTHgALJugYsjIvSK1o0UwG6VizNokXQ/tnPNjOpGB1AAABWUAIAAAAADrmnP3kS2GpCl+gdL2da90KHTkBX46iQ/sZRoj7uPz7BJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAAA==", + "subType": "06" + } + } + } + } + } + ] + cursor: {} + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: aggregate + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimal": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rbf3AeBEv4wWFAKknqDxRW5cLNkFvbIs6iJjc6LShQY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0l86Ag5OszXpa78SlOUV3K9nff5iC1p0mRXtLg9M1s4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Hn6yuxFHodeyu7ISlhYrbSf9pTiH4TDEvbYLWjTwFO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zdf4y2etKBuIpkEU1zMwoCkCsdisfXZCh8QPamm+drY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rOQ9oMdiK5xxGH+jPzOvwVqdGGnF3+HkJXxn81s6hp4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "61aKKsE3+BJHHWYvs3xSIBvlRmKswmaOo5rygQJguUg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KuDb/GIzqDM8wv7m7m8AECiWJbae5EKKtJRugZx7kR0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Q+t8t2TmNUiCIorVr9F3AlVnX+Mpt2ZYvN+s8UGict8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJRZIpKxUgHyL83kW8cvfjkxN3z6WoNnUg+SQw+LK+k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnUsYjip8SvW0+m9mR5WWTkpK+p6uwJ6yBUAlBnFKMk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PArHlz+yPRYDycAP/PgnI/AkP8Wgmfg++Vf4UG1Bf0E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wnIh53Q3jeK8jEBe1n8kJLa89/H0BxO26ZU8SRIAs9Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4F8U59gzBLGhq58PEWQk2nch+R0Va7eTUoxMneReUIA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ihKagIW3uT1dm22ROr/g5QaCpxZVj2+Fs/YSdM2Noco=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EJtUOOwjkrPUi9mavYAi+Gom9Y2DuFll7aDwo4mq0M0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dIkr8dbaVRQFskAVT6B286BbcBBt1pZPEOcTZqk4ZcI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aYVAcZYkH/Tieoa1XOjE/zCy5AJcVTHjS0NG2QB7muA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sBidL6y8TenseetpioIAAtn0lK/7C8MoW4JXpVYi3z8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0Dd2klU/t4R86c2WJcJDAd57k/N7OjvYSO5Vf8KH8sw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I3jZ92WEVmZmgaIkLbuWhBxl7EM6bEjiEttgBJunArA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aGHoQMlgJoGvArjfIbc3nnkoc8SWBxcrN7hSmjMRzos=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "bpiWPnF/KVBQr5F6MEwc5ZZayzIRvQOLDAm4ntwOi8g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tI7QVKbE6avWgDD9h4QKyFlnTxFCwd2iLySKakxNR/I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XGsge0CnoaXgE3rcpKm8AEeku5QVfokS3kcI+JKV1lk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JQxlryW2Q5WOwfrjAnaZxDvC83Dg6sjRVP5zegf2WiM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YFuHKJOfoqp1iGVxoFjx7bLYgVdsN4GuUFxEgO9HJ5s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Z6vUdiCR18ylKomf08uxcQHeRtmyav7/Ecvzz4av3k4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SPGo1Ib5AiP/tSllL7Z5PAypvnKdwJLzt8imfIMSEJQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "m94Nh6PFFQFLIib9Cu5LAKavhXnagSHG6F5EF8lD96I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pfEkQI98mB+gm1+JbmVurPAODMFPJ4E8DnqfVyUWbSo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DNj3OVRLbr43s0vd+rgWghOL3FqeO/60npdojC8Ry/M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kAYIQrjHVu49W8FTxyxJeiLVRWWjC9fPcBn+Hx1F+Ss=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aCSO7UVOpoQvu/iridarxkxV1SVxU1i9HVSYXUAeXk4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Gh6hTP/yj1IKlXQ+Q69KTfMlGZjEcXoRLGbQHNFo/1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/gDgIFQ4tAlJk3GN48IS5Qa5IPmErwGk8CHxAbp6gs0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PICyimwPjxpusyKxNssOOwUotAUbygpyEtORsVGXT8g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4lu+cBHyAUvuxC6JUNyHLzHsCogGSWFFnUCkDwfQdgI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pSndkmoNUJwXjgkbkgOrT5f9nSvuoMEZOkwAN9ElRaE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tyW+D4i26QihNM5MuBM+wnt5AdWGSJaJ4X5ydc9iWTU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9Syjr8RoxUgPKr+O5rsCu07AvcebA4P8IVKyS1NVLWc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "67tPfDYnK2tmrioI51fOBG0ygajcV0pLo5+Zm/rEW7U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "y0EiPRxYTuS1eVTIaPQUQBBxwkyxNckbePvKgChwd0M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NWd+2veAaeXQgR3vCvzlI4R1WW67D5YsVLdoXfdb8qg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PY5RQqKQsL2GqBBSPNOEVpojNFRX/NijCghIpxD6CZk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lcvwTyEjFlssCJtdjRpdN6oY+C7bxZY+WA+QAqzj9zg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWE7XRNylvTwO/9Fv56dNqUaQWMmESNS/GNIwgBaEI0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ijwlrUeS8nRYqK1F8kiCYF0mNDolEZS+/lJO1Lg93C8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8KzV+qYGYuIjoNj8eEpnTuHrMYuhzphl80rS6wrODuU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wDyTLjSEFF895hSQsHvmoEQVS6KIkZOtq1c9dVogm9I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SGrtPuMYCjUrfKF0Pq/thdaQzmGBMUvlwN3ORIu9tHU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KySHON3hIoUk4xWcwTqk6IL0kgjzjxgMBObVIkCGvk4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hBIdS9j0XJPeT4ot73ngELkpUoSixvRBvdOL9z48jY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Tx6um0q9HjS5ZvlFhvukpI6ORnyrXMWVW1OoxvgqII0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zFKlyfX5H81+d4A4J3FKn4T5JfG+OWtR06ddyX4Mxas=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cGgCDuPV7MeMMYEDpgOupqyNP4BQ4H7rBnd2QygumgM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IPaUoy98v11EoglTpJ4kBlEawoZ8y7BPwzjLYBpkvHQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Pfo4Am6tOWAyZNn8G9W5HWWGC3ZWmX0igI/RRB870Ro=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fnTSjd7bC1Udoq6iM7UDnHAC/lsIXSHp/Gy332qw+/I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fApBgVRrTDyEumkeWs5p3ag9KB48SbU4Si0dl7Ns9rc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QxudfBItgoCnUj5NXVnSmWH3HK76YtKkMmzn4lyyUYY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sSOvwhKa29Wq94bZ5jGIiJQGbG1uBrKSBfOYBz/oZeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FdaMgwwJ0NKsqmPZLC5oE+/0D74Dfpvig3LaI5yW5Fs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sRWBy12IERN43BSZIrnBfC9+zFBUdvjTlkqIH81NGt4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/4tIRpxKhoOwnXAiFn1Z7Xmric4USOIfKvTYQXk3QTc=", + "subType": "00" + } + } + ] + } + - + { + "_id": { + "$numberInt": "1" + }, + "encryptedDecimal": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RGTjNVEsNJb+DG7DpPOam8rQWD5HZAMpRyiTQaw7tk8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I93Md7QNPGmEEGYU1+VVCqBPBEvXdqHPtTJtMOn06Yk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "GecBFQ1PemlECWZWCl7f74vmsL6eB6mzQ9n6tK6FYfs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QpjhZl+O1ORifgtCZuWAdcP6OKL7IZ2cA46v8FJcV28=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RlQWwhU+uVv0a+9IB5cUkEfvHBvOw3B1Sx6WfPWMqes=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ubb81XTC7U+4tcNzf1oYvOY6gR5hC2Izqx54f4GuJ0E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6M4Q5NMQ9TqNnjzGOxIkiUIY8TEL0I3XD1QnhefQUqU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BtInzk9t2FFMCEY6AQ7zN8jwrrZEs2irSv6q0Q4NaIw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6vxXfETu9cuBIpRBo3jUUU04mJIH/aAhLX8K6VI5Xv0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wXPCdS+q23zi1bkPnaVG2j0PsVtxdeSLJ//h6J1x8RU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KY3KkfBAsN2l80wbpj41G0gwBR5KmmFnZcagg7D3ENk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tI8NFAxXCX4VOnY5X73K6KI/Yspd3aR94KV39MhJlAw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "nFxH0UC3mATKA6Vboz+QX/hAjj19kF/SH6H5Cne7qC0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q8hYqIYaIi7nOdG/7qQZYnz8Bsacfi66M1nVku4SH08=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4saA92R4arp4anvD9xFtze+sNcQqTEhPHyl1h70A8NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DbIziOBRRyeQS6RtBR09E37LV+CTKrEjGoRMLSpG6eE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Fv80Plp/7w2gnVqrwawLd6qhJ10G4NCDm3re67cNq4Y=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "T/T2oiQCBBES4YN7EodzPRdabZSFlYIClHBym+bQUZE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ZQgHD3l46Ujqtbnj1VbbeM29C9wJzOhz+yZ/7XdSrxk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ltlFKzWvyZvHxDFOYDd/XXJ6kUiJj0ln2HTCEz2o4Z4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "flW8A7bltC1u8bzx0WJtxosGJdOVsJFfbx33jxnpFGg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SXO+92QbMKwUSG2t27ciunV1c3VvFkUuDmSczpRe008=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+KioGs1GM+xRBzFE67ePTWj04KMSE5/Y6qUF7nJ5kvU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L3xNVbh6YH+RzqABN+5Jgb7T234Efpn766DmUvxIxgg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hPF+60mBYPjh21dEmPlBhKgyc9S2qLtTkypYvnqP2Fc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EletRsETy2HcjaPIm2c8CkT7ch/P3pJJDC8hasepcSU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "r5bMXUaNKqLPxZ+TG9HYTG4aSDgcpim27rN8rQFkM0w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0Q7Erdr8+/S0wUEDDIqlS5XjBVWvhZY65K0uUDb6+Ns=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xEcnhXy35hbXNVBPOOt3TUHbxvKfQ48KjA9b6/rbMqQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "T8bEpiQNgsEudXvyKE9SZlSvbpV/LUaslsdqgSFltyo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hIoiaF2YjnxDbODfhFEB+JGZ5nf8suD3Shck5bwQ3N0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qnA6qzejeRJ0rsZaZ0zOvKAaXyxt5lpscKQNYFZNl4k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "anAKCL2DN/le2VaP0n2ucYSEH/DaaEH/8Sa4OqTZsRA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JCZlBJaFm618oWYSnT9Jr1MtwFVw4BZjOzO+5yWgR90=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yxyk4n9762WzcDVGnTn4jCqUnSMIVCrLDIjCX1QVj34=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fDI6fdKvDJwim5/CQwWZEzcrXE3LHgy7FTtffcC7tXE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Vex+gcz5T+WkzsVZQrkqUR2ryyZbnaOGuWpYvjN0zCw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8TLEXz+Gbbp6llHpZXVjLsdlYY9f6hrKpHVpyfDe0RY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7fTyt5BrunypS65TfOzFW2E2qdIuT4SLeDeGlbQoJCs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8fKGrkqN0/KuSjyXgDBmRauDKrSa//JBKRWHEB9xBf4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s4codmG7uN4ss6P357jL21lazEe90M9GOK5WrOknSV0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RkSpua8XF+NUdxVDU90EbLUTTyZFX3tt3atBTroFaRk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "LnTCuCDyAHK5B9KXzjtwGmWB+qergQk2OCjnIx9MI2A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cBFh0virAX4pVXf/udIGI2951i0+0aZAdJcBVGtYnT4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "G54X6myQXWZ5fw/G31en3QbdgfXzL9+hFTtJpnWMqDI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EdsiiuezcsFJFnYIyGjCOhnqMj1BOwTB5EFxN+ERUkg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dVH9MXLtk0WTwGQ3xmrhOqfropMUkDW3o6paNPGl3NU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sB3HqXKWY3pKbuEH8BTbfNIGfbY+7/ZbOc3XC+JRNNI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WHyDk62Xhqbo4/iie2aLIM4x2uuAjv6102dJSHI58oM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pNUFuHpeNRDUZ/NrtII2c6sNc9eGR1lIUlIyXKERA+0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UPa+pdCqnN0bfAptdzldQOSd01gidrDKy8KhWrpSKAI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "l+7dOAlo+HUffMqFYXL6pgUFeTbwOM9CjKQLxEoLtc4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SRnDXV/rN6C8xwMutv9E1luv3DOUio3VkgPr8Cpm7Ew=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QcH6gl+gX7xZ7OWhUNQMbndJy0Piz49pDo6RsnLkVSA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "t+uL4DnfsI/Zll/KXWW1cOKX3Hu8WIkm3pt9efCVSAQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "myutHDctku/+Uug/nD8gRbYvmx/IovtoAAC2/fz2oHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6C+cjD0e0nSCP6cPqQYbNG7SlOd6Mfvi8hyfm7Ng+D8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zg01JSoOj9oBKT0S1ldJucXzY5AKgreS+h2xJreWTOs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7qQ80/FjodHl1m1py/Oii0/9C/xWbLdhaRXQ+kkCP10=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YwWMNH07vL6c5Nhg+MRnVByhzUunu8y0VLM9z/XvR5U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Dle8bU98+fudAbc14SToZFkwvV3tcYVsjDug0NWljpc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "J+eKL1vPJmlzltvhI6Li5Fz/TJmi3Ng+ehRTcs46API=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zB3XzfFygLwC3WHkj0up+VbEd25KKoce1vOpG/5bwK4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vnVnmOnL+z2pqwE+A6cVKS0Iwy4F4/2IiElJca9bUQM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+lG5r/Fpqry3BtFuvY67+RntmHAMDoLVOSGc6ZoXPb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L5MXQertqc6uj7ADe8aWKbd1sYHPCE7P1VYVg9Zc3VI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "imKONuZgopt0bhM3GMX2WVPwQYMTobuUUEdhcLfHs4c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "eOkU1J1uVbiVFWBerbXsSIVcF2nqiicTkFy4x7kFHB8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gI0uDhXeoH/UatDQKEf4qo8FHzWZDhb/wuWTqbq/ID4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cOkd5Aa3btYhtojE/smsF/PJnULqQ4NNqTkU6KXTFmo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "AWNJMs1MTe294oFipp8Y6P0CjpkZ4qCZoClQF3XcHq8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6gJtlzXOFhGYrVbTuRMmvMlDTwXdNtR9aGBlHZPwIMw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "LEmwVGA/xsEG7UrcOoYLFu6KCXgijzFznenknuDacm8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "mIRFPTXRrGaPtp/Ydij2jgkRe4uoUvAKxW2d8b9zYL0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "B+Uv2u48WALOO0L311z+eryjYQzKJVMfdHMZPhOAFmY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "INXXp0wDyVCq+NtfIrrC2ciETmyW/dWB/48/u4yLEZ4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "se7DGo8XrlrQDLEcco1tZrQt9kDe+0RTyl2bw/quG4w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vr0m2+Zk9lbN6UgWCyn8xJWJOokU3IDYab5U5q1+CgQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XI+eJ8Gy2JktG1gICgoj1qpsfy1tKmH0kglWbaQH6DA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A+UCuNnuAUqnQzspA6TVqUPRmtZmpSex5HFw7THRxs0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xaH2Ehfljd19uo0Fvb3iwkdaiWEVQd2YPoitgEPkhSM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "S/iZBJGcc8+qZxyMtab65MMBoSglybwk3x58Nb86gnY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "w14ZE5qqY5YgkS4Zcs9YNbrQbY1XfGOOHNn9bOYnFVQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0MhGd/jEF1vjkKGp+ZMn9SjLK54jkp9W4Hg+Sp/oxaI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "92QZ73e/NRTYgCm4aifaKth6aAsKnLLccBc0zx/qUTY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WOjzemCgFJOiGIp81RSVh/tFlzSTj9eFWcBnsiv2Ycs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DrsP9CmfKPjw5yLL8bnSeAxfNzAwlb+Z8OqCiKgBY7o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lMogqg8veBv6mri3/drMe9afJiKMvevkmGcw9BedfLo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "TxqwNcY8Tg2MPpNdkPBwvfpuTttSYRHU26DGECKYQ9o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "l0u1b4b4vYACWIwfnB7PZac4oDEgjQZCzHruNPTgAIY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "iVSGQ+cCfhbWIrY/v/WBORK92elu9gfRKyGhr6r/k00=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yK1forG50diEXte8ECzjfpHeYsPyuQ/dgxbxn/nzY5k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gIfTLCD3VwnOwkC0zPXWTqaITxX6ZplA69PO2a6zolc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "O/Zxlgh3WqpzJ7+Sd8XWMVID4/GXJUUWaSqfgDUi3b0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ZQ6yv368zwahUqSUYH/StL0Qgz/TwS1CzlMjVDvCciI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "m2rPEYkjwyiKdonMrKlcF7hya4lFOAUwEePJ3SgrNx8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Mq0yl5iVKlq71bT/dT/fXOWf2n90bTnXFnOdGDN0JOc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6qDGMXipPLC2O6EAAMjO2F9xx4rdqZso4IkPpH2304U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jvQHRQQa2RIszE2LX2Hv2LbRhYawJ6qmtRt8HZzFQXg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ovJXQrkZlpeHRciKyE/WWNm5O389gRgzx1W+Dw596X4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "a4kgRNvYctGYqyQv9qScL/WkljTYVylJ9pE9KDULlxU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qV4Q48vPiCJMTjljotzYKI/zfExWpkKOSHGcAjGyDig=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jtI7zbBF+QW/aYYTkn90zzyHLXLgmy7l1bzgMb2oqic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q0KmJl9txPdn962UNvnfe6UFhdk9YaFZuTm33F+csso=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ULNdEqeZJgtmNOhN/Y9INzsE9AnxWYwOMn+pIbRXIFs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "R4oz9+wkdjpKe5tE1jpG7IURAnfvS5fLP4LrD5cZfTE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qG5Z7VhwSu/HT/YFTgDzyAAzJKq51xPw2HeEV5btYC4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OM/1DmIIZ5Qyhtq8TGkHTBEMVKjAnKRZMRXYtTG8ctc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2R5vZbljLXnDFA99YfGuRB7pAdPJVKsT25zLNMC0fUk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OMbavF2EmdAz1fHkLV3ctFEUDfriKhoT2gidwHZ9z1o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MWT4Zrw3/vVvTYMa1Is5Pjr3wEwnBfnEAPPUAHKQhNU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tBkRPfG9yxfKocQx5pAJX0oEHKPL0Tgtr+0UYe09InE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lqxpnDR/H0YgH7RcfKoNoaaRhe1SIazIeMbQ1fu9y3Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "utT1UdR22PWOTrOkZauztX613lAplV4eh/ejTRb7ZSk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "S+Y2yFyKi/a6FXhih4yGo29X8I8OT6/zwEoX6NMKT4o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QSjVppg29x6oS5yBg8OFjrFt0tuTpWCuKxfIy0k8YnE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "y3r6/Xsfvsl3HksXlVYkJgHUqpQGfICxg3x9f8Zw1qM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BSltHzEwDjFN4du9rDHAPvl22atlcTioEtt+gC5L1tk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0arGXjSN0006UnXbrWsGqhvBair569DeFDUME3Df3rA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s/DumaMad08S+PBUUcrS+v42K0z8HgcdiQtrFAEu2Qs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EzJ8Y8N0OQBTlnvrK82PdevDNZZO4E6CNgYVu8Cj6Ks=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VA4vr8jBPI5QdiPrULzzZjBMIUbG3V7Slg5zm0bFcKc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YAOvEB2ZLtq9LQiFViBHWaxxWVVonC2rNYj9tN9s3L0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hgaHMo9aAGS+nBwvqnTjZO+YkiQPY1c1XcIYeaYKHyI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YvaoLt3ZpH0atB0tNzwMjpoxRYJXl0DqSjisMJiGVBE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EMmW6CptFsiLoPOi5/uAJQ2FmeLg6mCpuVLLrRWk7Mc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1jQsNMarSnarlYmXEuoFokeBMg/090qUD9wqo1Zn8Gs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hupXNKhRpJxpyDAAP1TgJ5JMZh9lhbMk6s7D7dMS3C8=", + "subType": "00" + } + } + ] + } + \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Decimal-Correctness.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-Decimal-Correctness.json new file mode 100644 index 00000000000..89b7bd3118b --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Decimal-Correctness.json @@ -0,0 +1,1158 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimal", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "Find with $gt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimal": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimal": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimal": { + "$gt": { + "$numberDecimal": "0.0" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDecimal": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $gte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimal": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimal": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimal": { + "$gte": { + "$numberDecimal": "0.0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedDecimal": { + "$numberDecimal": "0.0" + } + }, + { + "_id": 1, + "encryptedDecimal": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $gt with no results", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimal": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimal": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimal": { + "$gt": { + "$numberDecimal": "1.0" + } + } + } + }, + "result": [] + } + ] + }, + { + "description": "Find with $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimal": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimal": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimal": { + "$lt": { + "$numberDecimal": "1.0" + } + } + } + }, + "result": [ + { + "_id": 0, + "encryptedDecimal": { + "$numberDecimal": "0.0" + } + } + ] + } + ] + }, + { + "description": "Find with $lte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimal": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimal": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimal": { + "$lte": { + "$numberDecimal": "1.0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedDecimal": { + "$numberDecimal": "0.0" + } + }, + { + "_id": 1, + "encryptedDecimal": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $gt and $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimal": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimal": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimal": { + "$gt": { + "$numberDecimal": "0.0" + }, + "$lt": { + "$numberDecimal": "2.0" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDecimal": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with equality", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimal": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimal": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimal": { + "$numberDecimal": "0.0" + } + } + }, + "result": [ + { + "_id": 0, + "encryptedDecimal": { + "$numberDecimal": "0.0" + } + } + ] + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimal": { + "$numberDecimal": "1.0" + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDecimal": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $in", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimal": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimal": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimal": { + "$in": [ + { + "$numberDecimal": "0.0" + } + ] + } + } + }, + "result": [ + { + "_id": 0, + "encryptedDecimal": { + "$numberDecimal": "0.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimal": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimal": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimal": { + "$gte": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDecimal": { + "$numberDecimal": "0.0" + } + }, + { + "_id": 1, + "encryptedDecimal": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gt with no results", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimal": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimal": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimal": { + "$gt": { + "$numberDecimal": "1.0" + } + } + } + } + ] + }, + "result": [] + } + ] + }, + { + "description": "Aggregate with $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimal": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimal": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimal": { + "$lt": { + "$numberDecimal": "1.0" + } + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDecimal": { + "$numberDecimal": "0.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $lte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimal": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimal": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimal": { + "$lte": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDecimal": { + "$numberDecimal": "0.0" + } + }, + { + "_id": 1, + "encryptedDecimal": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gt and $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimal": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimal": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimal": { + "$gt": { + "$numberDecimal": "0.0" + }, + "$lt": { + "$numberDecimal": "2.0" + } + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedDecimal": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with equality", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimal": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimal": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimal": { + "$numberDecimal": "0.0" + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDecimal": { + "$numberDecimal": "0.0" + } + } + ] + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimal": { + "$numberDecimal": "1.0" + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedDecimal": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $in", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimal": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimal": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimal": { + "$in": [ + { + "$numberDecimal": "0.0" + } + ] + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDecimal": { + "$numberDecimal": "0.0" + } + } + ] + } + ] + }, + { + "description": "Wrong type: Insert Int", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimal": { + "$numberInt": "0" + } + } + }, + "result": { + "errorContains": "cannot encrypt element" + } + } + ] + }, + { + "description": "Wrong type: Find Int", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimal": { + "$gte": { + "$numberInt": "0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": { + "errorContains": "field type is not supported" + } + } + ] + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Decimal-Correctness.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-Decimal-Correctness.yml new file mode 100644 index 00000000000..03df38175f2 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Decimal-Correctness.yml @@ -0,0 +1,291 @@ +# Test correctness results. +# Does not include command monitoring expectations or outcome assertions to make tests more readable. +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + # Tests for Decimal (without precision) must only run against a replica set. Decimal (without precision) queries are expected to take a long time and may exceed the default mongos timeout. + topology: [ "replicaset" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDecimal', 'bsonType': 'decimal', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "Find with $gt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDecimal: { $numberDecimal: "0.0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDecimal: { $numberDecimal: "1.0" } } + - name: find + arguments: + filter: { encryptedDecimal: { $gt: { $numberDecimal: "0.0" } }} + result: [*doc1] + + - description: "Find with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimal: { $gte: { $numberDecimal: "0.0" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimal: { $gt: { $numberDecimal: "1.0" } }} + result: [] + + - description: "Find with $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimal: { $lt: { $numberDecimal: "1.0" } }} + result: [*doc0] + + - description: "Find with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimal: { $lte: { $numberDecimal: "1.0" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $gt and $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimal: { $gt: { $numberDecimal: "0.0" }, $lt: { $numberDecimal: "2.0"} }} + result: [*doc1] + + - description: "Find with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimal: { $numberDecimal: "0.0" } } + result: [*doc0] + - name: find + arguments: + filter: { encryptedDecimal: { $numberDecimal: "1.0" } } + result: [*doc1] + + - description: "Find with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimal: { $in: [ {$numberDecimal: "0.0"} ] } } + result: [*doc0] + + - description: "Aggregate with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimal: { $gte: { $numberDecimal: "0.0" } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimal: { $gt: { $numberDecimal: "1.0" } }} } + result: [] + + - description: "Aggregate with $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimal: { $lt: { $numberDecimal: "1.0" } }} } + result: [*doc0] + + - description: "Aggregate with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimal: { $lte: { $numberDecimal: "1.0" } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $gt and $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimal: { $gt: { $numberDecimal: "0.0" }, $lt: { $numberDecimal: "2.0"} }} } + result: [*doc1] + + - description: "Aggregate with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimal: { $numberDecimal: "0.0" } } } + result: [*doc0] + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimal: { $numberDecimal: "1.0" } } } + result: [*doc1] + + - description: "Aggregate with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimal: { $in: [ {$numberDecimal: "0.0"} ] } } } + result: [*doc0] + + - description: "Wrong type: Insert Int" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: { _id: 0, encryptedDecimal: { $numberInt: "0" }} } + result: + # Expect an error from mongocryptd. + errorContains: "cannot encrypt element" + + - description: "Wrong type: Find Int" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: find + arguments: + filter: { encryptedDecimal: { $gte: { $numberInt: "0" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: + # expect an error from libmongocrypt. + errorContains: "field type is not supported" \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Decimal-Delete.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-Decimal-Delete.json new file mode 100644 index 00000000000..0463be1c69f --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Decimal-Delete.json @@ -0,0 +1,1133 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimal", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Decimal. Delete.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimal": { + "$numberDecimal": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimal": { + "$numberDecimal": "1" + } + } + } + }, + { + "name": "deleteOne", + "arguments": { + "filter": { + "encryptedDecimal": { + "$gt": { + "$numberDecimal": "0" + } + } + } + }, + "result": { + "deletedCount": 1 + } + } + ], + "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": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDecimal": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimal", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDecimal": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimal", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "delete": "default", + "deletes": [ + { + "q": { + "encryptedDecimal": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ], + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimal", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + }, + "deleteTokens": { + "default.default": { + "encryptedDecimal": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + } + }, + "command_name": "delete" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimal": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rbf3AeBEv4wWFAKknqDxRW5cLNkFvbIs6iJjc6LShQY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0l86Ag5OszXpa78SlOUV3K9nff5iC1p0mRXtLg9M1s4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Hn6yuxFHodeyu7ISlhYrbSf9pTiH4TDEvbYLWjTwFO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zdf4y2etKBuIpkEU1zMwoCkCsdisfXZCh8QPamm+drY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rOQ9oMdiK5xxGH+jPzOvwVqdGGnF3+HkJXxn81s6hp4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "61aKKsE3+BJHHWYvs3xSIBvlRmKswmaOo5rygQJguUg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KuDb/GIzqDM8wv7m7m8AECiWJbae5EKKtJRugZx7kR0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Q+t8t2TmNUiCIorVr9F3AlVnX+Mpt2ZYvN+s8UGict8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJRZIpKxUgHyL83kW8cvfjkxN3z6WoNnUg+SQw+LK+k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnUsYjip8SvW0+m9mR5WWTkpK+p6uwJ6yBUAlBnFKMk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PArHlz+yPRYDycAP/PgnI/AkP8Wgmfg++Vf4UG1Bf0E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wnIh53Q3jeK8jEBe1n8kJLa89/H0BxO26ZU8SRIAs9Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4F8U59gzBLGhq58PEWQk2nch+R0Va7eTUoxMneReUIA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ihKagIW3uT1dm22ROr/g5QaCpxZVj2+Fs/YSdM2Noco=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EJtUOOwjkrPUi9mavYAi+Gom9Y2DuFll7aDwo4mq0M0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dIkr8dbaVRQFskAVT6B286BbcBBt1pZPEOcTZqk4ZcI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aYVAcZYkH/Tieoa1XOjE/zCy5AJcVTHjS0NG2QB7muA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sBidL6y8TenseetpioIAAtn0lK/7C8MoW4JXpVYi3z8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0Dd2klU/t4R86c2WJcJDAd57k/N7OjvYSO5Vf8KH8sw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I3jZ92WEVmZmgaIkLbuWhBxl7EM6bEjiEttgBJunArA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aGHoQMlgJoGvArjfIbc3nnkoc8SWBxcrN7hSmjMRzos=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "bpiWPnF/KVBQr5F6MEwc5ZZayzIRvQOLDAm4ntwOi8g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tI7QVKbE6avWgDD9h4QKyFlnTxFCwd2iLySKakxNR/I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XGsge0CnoaXgE3rcpKm8AEeku5QVfokS3kcI+JKV1lk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JQxlryW2Q5WOwfrjAnaZxDvC83Dg6sjRVP5zegf2WiM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YFuHKJOfoqp1iGVxoFjx7bLYgVdsN4GuUFxEgO9HJ5s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Z6vUdiCR18ylKomf08uxcQHeRtmyav7/Ecvzz4av3k4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SPGo1Ib5AiP/tSllL7Z5PAypvnKdwJLzt8imfIMSEJQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "m94Nh6PFFQFLIib9Cu5LAKavhXnagSHG6F5EF8lD96I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pfEkQI98mB+gm1+JbmVurPAODMFPJ4E8DnqfVyUWbSo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DNj3OVRLbr43s0vd+rgWghOL3FqeO/60npdojC8Ry/M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kAYIQrjHVu49W8FTxyxJeiLVRWWjC9fPcBn+Hx1F+Ss=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aCSO7UVOpoQvu/iridarxkxV1SVxU1i9HVSYXUAeXk4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Gh6hTP/yj1IKlXQ+Q69KTfMlGZjEcXoRLGbQHNFo/1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/gDgIFQ4tAlJk3GN48IS5Qa5IPmErwGk8CHxAbp6gs0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PICyimwPjxpusyKxNssOOwUotAUbygpyEtORsVGXT8g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4lu+cBHyAUvuxC6JUNyHLzHsCogGSWFFnUCkDwfQdgI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pSndkmoNUJwXjgkbkgOrT5f9nSvuoMEZOkwAN9ElRaE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tyW+D4i26QihNM5MuBM+wnt5AdWGSJaJ4X5ydc9iWTU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9Syjr8RoxUgPKr+O5rsCu07AvcebA4P8IVKyS1NVLWc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "67tPfDYnK2tmrioI51fOBG0ygajcV0pLo5+Zm/rEW7U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "y0EiPRxYTuS1eVTIaPQUQBBxwkyxNckbePvKgChwd0M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NWd+2veAaeXQgR3vCvzlI4R1WW67D5YsVLdoXfdb8qg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PY5RQqKQsL2GqBBSPNOEVpojNFRX/NijCghIpxD6CZk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lcvwTyEjFlssCJtdjRpdN6oY+C7bxZY+WA+QAqzj9zg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWE7XRNylvTwO/9Fv56dNqUaQWMmESNS/GNIwgBaEI0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ijwlrUeS8nRYqK1F8kiCYF0mNDolEZS+/lJO1Lg93C8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8KzV+qYGYuIjoNj8eEpnTuHrMYuhzphl80rS6wrODuU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wDyTLjSEFF895hSQsHvmoEQVS6KIkZOtq1c9dVogm9I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SGrtPuMYCjUrfKF0Pq/thdaQzmGBMUvlwN3ORIu9tHU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KySHON3hIoUk4xWcwTqk6IL0kgjzjxgMBObVIkCGvk4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hBIdS9j0XJPeT4ot73ngELkpUoSixvRBvdOL9z48jY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Tx6um0q9HjS5ZvlFhvukpI6ORnyrXMWVW1OoxvgqII0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zFKlyfX5H81+d4A4J3FKn4T5JfG+OWtR06ddyX4Mxas=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cGgCDuPV7MeMMYEDpgOupqyNP4BQ4H7rBnd2QygumgM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IPaUoy98v11EoglTpJ4kBlEawoZ8y7BPwzjLYBpkvHQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Pfo4Am6tOWAyZNn8G9W5HWWGC3ZWmX0igI/RRB870Ro=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fnTSjd7bC1Udoq6iM7UDnHAC/lsIXSHp/Gy332qw+/I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fApBgVRrTDyEumkeWs5p3ag9KB48SbU4Si0dl7Ns9rc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QxudfBItgoCnUj5NXVnSmWH3HK76YtKkMmzn4lyyUYY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sSOvwhKa29Wq94bZ5jGIiJQGbG1uBrKSBfOYBz/oZeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FdaMgwwJ0NKsqmPZLC5oE+/0D74Dfpvig3LaI5yW5Fs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sRWBy12IERN43BSZIrnBfC9+zFBUdvjTlkqIH81NGt4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/4tIRpxKhoOwnXAiFn1Z7Xmric4USOIfKvTYQXk3QTc=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Decimal-Delete.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-Decimal-Delete.yml new file mode 100644 index 00000000000..fa2daa78e61 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Decimal-Delete.yml @@ -0,0 +1,908 @@ +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + # Tests for Decimal (without precision) must only run against a replica set. Decimal (without precision) queries are expected to take a long time and may exceed the default mongos timeout. + topology: [ "replicaset" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDecimal', 'bsonType': 'decimal', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "FLE2 Range Decimal. Delete." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDecimal: { $numberDecimal: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDecimal: { $numberDecimal: "1" } } + - name: deleteOne + arguments: + filter: { "encryptedDecimal": { $gt: {$numberDecimal: "0" }} } + result: + deletedCount: 1 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDecimal": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDecimal": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + delete: *collection_name + deletes: [ + { + "q": { + "encryptedDecimal": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ] + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + "deleteTokens": { + "default.default": { + "encryptedDecimal": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + command_name: delete + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimal": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rbf3AeBEv4wWFAKknqDxRW5cLNkFvbIs6iJjc6LShQY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0l86Ag5OszXpa78SlOUV3K9nff5iC1p0mRXtLg9M1s4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Hn6yuxFHodeyu7ISlhYrbSf9pTiH4TDEvbYLWjTwFO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zdf4y2etKBuIpkEU1zMwoCkCsdisfXZCh8QPamm+drY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rOQ9oMdiK5xxGH+jPzOvwVqdGGnF3+HkJXxn81s6hp4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "61aKKsE3+BJHHWYvs3xSIBvlRmKswmaOo5rygQJguUg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KuDb/GIzqDM8wv7m7m8AECiWJbae5EKKtJRugZx7kR0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Q+t8t2TmNUiCIorVr9F3AlVnX+Mpt2ZYvN+s8UGict8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJRZIpKxUgHyL83kW8cvfjkxN3z6WoNnUg+SQw+LK+k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnUsYjip8SvW0+m9mR5WWTkpK+p6uwJ6yBUAlBnFKMk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PArHlz+yPRYDycAP/PgnI/AkP8Wgmfg++Vf4UG1Bf0E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wnIh53Q3jeK8jEBe1n8kJLa89/H0BxO26ZU8SRIAs9Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4F8U59gzBLGhq58PEWQk2nch+R0Va7eTUoxMneReUIA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ihKagIW3uT1dm22ROr/g5QaCpxZVj2+Fs/YSdM2Noco=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EJtUOOwjkrPUi9mavYAi+Gom9Y2DuFll7aDwo4mq0M0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dIkr8dbaVRQFskAVT6B286BbcBBt1pZPEOcTZqk4ZcI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aYVAcZYkH/Tieoa1XOjE/zCy5AJcVTHjS0NG2QB7muA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sBidL6y8TenseetpioIAAtn0lK/7C8MoW4JXpVYi3z8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0Dd2klU/t4R86c2WJcJDAd57k/N7OjvYSO5Vf8KH8sw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I3jZ92WEVmZmgaIkLbuWhBxl7EM6bEjiEttgBJunArA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aGHoQMlgJoGvArjfIbc3nnkoc8SWBxcrN7hSmjMRzos=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "bpiWPnF/KVBQr5F6MEwc5ZZayzIRvQOLDAm4ntwOi8g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tI7QVKbE6avWgDD9h4QKyFlnTxFCwd2iLySKakxNR/I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XGsge0CnoaXgE3rcpKm8AEeku5QVfokS3kcI+JKV1lk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JQxlryW2Q5WOwfrjAnaZxDvC83Dg6sjRVP5zegf2WiM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YFuHKJOfoqp1iGVxoFjx7bLYgVdsN4GuUFxEgO9HJ5s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Z6vUdiCR18ylKomf08uxcQHeRtmyav7/Ecvzz4av3k4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SPGo1Ib5AiP/tSllL7Z5PAypvnKdwJLzt8imfIMSEJQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "m94Nh6PFFQFLIib9Cu5LAKavhXnagSHG6F5EF8lD96I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pfEkQI98mB+gm1+JbmVurPAODMFPJ4E8DnqfVyUWbSo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DNj3OVRLbr43s0vd+rgWghOL3FqeO/60npdojC8Ry/M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kAYIQrjHVu49W8FTxyxJeiLVRWWjC9fPcBn+Hx1F+Ss=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aCSO7UVOpoQvu/iridarxkxV1SVxU1i9HVSYXUAeXk4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Gh6hTP/yj1IKlXQ+Q69KTfMlGZjEcXoRLGbQHNFo/1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/gDgIFQ4tAlJk3GN48IS5Qa5IPmErwGk8CHxAbp6gs0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PICyimwPjxpusyKxNssOOwUotAUbygpyEtORsVGXT8g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4lu+cBHyAUvuxC6JUNyHLzHsCogGSWFFnUCkDwfQdgI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pSndkmoNUJwXjgkbkgOrT5f9nSvuoMEZOkwAN9ElRaE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tyW+D4i26QihNM5MuBM+wnt5AdWGSJaJ4X5ydc9iWTU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9Syjr8RoxUgPKr+O5rsCu07AvcebA4P8IVKyS1NVLWc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "67tPfDYnK2tmrioI51fOBG0ygajcV0pLo5+Zm/rEW7U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "y0EiPRxYTuS1eVTIaPQUQBBxwkyxNckbePvKgChwd0M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NWd+2veAaeXQgR3vCvzlI4R1WW67D5YsVLdoXfdb8qg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PY5RQqKQsL2GqBBSPNOEVpojNFRX/NijCghIpxD6CZk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lcvwTyEjFlssCJtdjRpdN6oY+C7bxZY+WA+QAqzj9zg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWE7XRNylvTwO/9Fv56dNqUaQWMmESNS/GNIwgBaEI0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ijwlrUeS8nRYqK1F8kiCYF0mNDolEZS+/lJO1Lg93C8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8KzV+qYGYuIjoNj8eEpnTuHrMYuhzphl80rS6wrODuU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wDyTLjSEFF895hSQsHvmoEQVS6KIkZOtq1c9dVogm9I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SGrtPuMYCjUrfKF0Pq/thdaQzmGBMUvlwN3ORIu9tHU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KySHON3hIoUk4xWcwTqk6IL0kgjzjxgMBObVIkCGvk4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hBIdS9j0XJPeT4ot73ngELkpUoSixvRBvdOL9z48jY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Tx6um0q9HjS5ZvlFhvukpI6ORnyrXMWVW1OoxvgqII0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zFKlyfX5H81+d4A4J3FKn4T5JfG+OWtR06ddyX4Mxas=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cGgCDuPV7MeMMYEDpgOupqyNP4BQ4H7rBnd2QygumgM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IPaUoy98v11EoglTpJ4kBlEawoZ8y7BPwzjLYBpkvHQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Pfo4Am6tOWAyZNn8G9W5HWWGC3ZWmX0igI/RRB870Ro=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fnTSjd7bC1Udoq6iM7UDnHAC/lsIXSHp/Gy332qw+/I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fApBgVRrTDyEumkeWs5p3ag9KB48SbU4Si0dl7Ns9rc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QxudfBItgoCnUj5NXVnSmWH3HK76YtKkMmzn4lyyUYY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sSOvwhKa29Wq94bZ5jGIiJQGbG1uBrKSBfOYBz/oZeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FdaMgwwJ0NKsqmPZLC5oE+/0D74Dfpvig3LaI5yW5Fs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sRWBy12IERN43BSZIrnBfC9+zFBUdvjTlkqIH81NGt4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/4tIRpxKhoOwnXAiFn1Z7Xmric4USOIfKvTYQXk3QTc=", + "subType": "00" + } + } + ] + } \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Decimal-FindOneAndUpdate.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-Decimal-FindOneAndUpdate.json new file mode 100644 index 00000000000..d0e29677714 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Decimal-FindOneAndUpdate.json @@ -0,0 +1,1930 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimal", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Decimal. FindOneAndUpdate.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimal": { + "$numberDecimal": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimal": { + "$numberDecimal": "1" + } + } + } + }, + { + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "encryptedDecimal": { + "$gt": { + "$numberDecimal": "0" + } + } + }, + "update": { + "$set": { + "encryptedDecimal": { + "$numberDecimal": "2" + } + } + }, + "returnDocument": "Before" + }, + "result": { + "_id": 1, + "encryptedDecimal": { + "$numberDecimal": "1" + } + } + } + ], + "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": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDecimal": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimal", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDecimal": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimal", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "findAndModify": "default", + "query": { + "encryptedDecimal": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "update": { + "$set": { + "encryptedDecimal": { + "$$type": "binData" + } + } + }, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimal", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + }, + "deleteTokens": { + "default.default": { + "encryptedDecimal": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + } + }, + "command_name": "findAndModify" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimal": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rbf3AeBEv4wWFAKknqDxRW5cLNkFvbIs6iJjc6LShQY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0l86Ag5OszXpa78SlOUV3K9nff5iC1p0mRXtLg9M1s4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Hn6yuxFHodeyu7ISlhYrbSf9pTiH4TDEvbYLWjTwFO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zdf4y2etKBuIpkEU1zMwoCkCsdisfXZCh8QPamm+drY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rOQ9oMdiK5xxGH+jPzOvwVqdGGnF3+HkJXxn81s6hp4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "61aKKsE3+BJHHWYvs3xSIBvlRmKswmaOo5rygQJguUg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KuDb/GIzqDM8wv7m7m8AECiWJbae5EKKtJRugZx7kR0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Q+t8t2TmNUiCIorVr9F3AlVnX+Mpt2ZYvN+s8UGict8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJRZIpKxUgHyL83kW8cvfjkxN3z6WoNnUg+SQw+LK+k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnUsYjip8SvW0+m9mR5WWTkpK+p6uwJ6yBUAlBnFKMk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PArHlz+yPRYDycAP/PgnI/AkP8Wgmfg++Vf4UG1Bf0E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wnIh53Q3jeK8jEBe1n8kJLa89/H0BxO26ZU8SRIAs9Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4F8U59gzBLGhq58PEWQk2nch+R0Va7eTUoxMneReUIA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ihKagIW3uT1dm22ROr/g5QaCpxZVj2+Fs/YSdM2Noco=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EJtUOOwjkrPUi9mavYAi+Gom9Y2DuFll7aDwo4mq0M0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dIkr8dbaVRQFskAVT6B286BbcBBt1pZPEOcTZqk4ZcI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aYVAcZYkH/Tieoa1XOjE/zCy5AJcVTHjS0NG2QB7muA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sBidL6y8TenseetpioIAAtn0lK/7C8MoW4JXpVYi3z8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0Dd2klU/t4R86c2WJcJDAd57k/N7OjvYSO5Vf8KH8sw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I3jZ92WEVmZmgaIkLbuWhBxl7EM6bEjiEttgBJunArA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aGHoQMlgJoGvArjfIbc3nnkoc8SWBxcrN7hSmjMRzos=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "bpiWPnF/KVBQr5F6MEwc5ZZayzIRvQOLDAm4ntwOi8g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tI7QVKbE6avWgDD9h4QKyFlnTxFCwd2iLySKakxNR/I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XGsge0CnoaXgE3rcpKm8AEeku5QVfokS3kcI+JKV1lk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JQxlryW2Q5WOwfrjAnaZxDvC83Dg6sjRVP5zegf2WiM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YFuHKJOfoqp1iGVxoFjx7bLYgVdsN4GuUFxEgO9HJ5s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Z6vUdiCR18ylKomf08uxcQHeRtmyav7/Ecvzz4av3k4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SPGo1Ib5AiP/tSllL7Z5PAypvnKdwJLzt8imfIMSEJQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "m94Nh6PFFQFLIib9Cu5LAKavhXnagSHG6F5EF8lD96I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pfEkQI98mB+gm1+JbmVurPAODMFPJ4E8DnqfVyUWbSo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DNj3OVRLbr43s0vd+rgWghOL3FqeO/60npdojC8Ry/M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kAYIQrjHVu49W8FTxyxJeiLVRWWjC9fPcBn+Hx1F+Ss=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aCSO7UVOpoQvu/iridarxkxV1SVxU1i9HVSYXUAeXk4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Gh6hTP/yj1IKlXQ+Q69KTfMlGZjEcXoRLGbQHNFo/1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/gDgIFQ4tAlJk3GN48IS5Qa5IPmErwGk8CHxAbp6gs0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PICyimwPjxpusyKxNssOOwUotAUbygpyEtORsVGXT8g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4lu+cBHyAUvuxC6JUNyHLzHsCogGSWFFnUCkDwfQdgI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pSndkmoNUJwXjgkbkgOrT5f9nSvuoMEZOkwAN9ElRaE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tyW+D4i26QihNM5MuBM+wnt5AdWGSJaJ4X5ydc9iWTU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9Syjr8RoxUgPKr+O5rsCu07AvcebA4P8IVKyS1NVLWc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "67tPfDYnK2tmrioI51fOBG0ygajcV0pLo5+Zm/rEW7U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "y0EiPRxYTuS1eVTIaPQUQBBxwkyxNckbePvKgChwd0M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NWd+2veAaeXQgR3vCvzlI4R1WW67D5YsVLdoXfdb8qg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PY5RQqKQsL2GqBBSPNOEVpojNFRX/NijCghIpxD6CZk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lcvwTyEjFlssCJtdjRpdN6oY+C7bxZY+WA+QAqzj9zg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWE7XRNylvTwO/9Fv56dNqUaQWMmESNS/GNIwgBaEI0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ijwlrUeS8nRYqK1F8kiCYF0mNDolEZS+/lJO1Lg93C8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8KzV+qYGYuIjoNj8eEpnTuHrMYuhzphl80rS6wrODuU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wDyTLjSEFF895hSQsHvmoEQVS6KIkZOtq1c9dVogm9I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SGrtPuMYCjUrfKF0Pq/thdaQzmGBMUvlwN3ORIu9tHU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KySHON3hIoUk4xWcwTqk6IL0kgjzjxgMBObVIkCGvk4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hBIdS9j0XJPeT4ot73ngELkpUoSixvRBvdOL9z48jY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Tx6um0q9HjS5ZvlFhvukpI6ORnyrXMWVW1OoxvgqII0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zFKlyfX5H81+d4A4J3FKn4T5JfG+OWtR06ddyX4Mxas=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cGgCDuPV7MeMMYEDpgOupqyNP4BQ4H7rBnd2QygumgM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IPaUoy98v11EoglTpJ4kBlEawoZ8y7BPwzjLYBpkvHQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Pfo4Am6tOWAyZNn8G9W5HWWGC3ZWmX0igI/RRB870Ro=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fnTSjd7bC1Udoq6iM7UDnHAC/lsIXSHp/Gy332qw+/I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fApBgVRrTDyEumkeWs5p3ag9KB48SbU4Si0dl7Ns9rc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QxudfBItgoCnUj5NXVnSmWH3HK76YtKkMmzn4lyyUYY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sSOvwhKa29Wq94bZ5jGIiJQGbG1uBrKSBfOYBz/oZeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FdaMgwwJ0NKsqmPZLC5oE+/0D74Dfpvig3LaI5yW5Fs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sRWBy12IERN43BSZIrnBfC9+zFBUdvjTlkqIH81NGt4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/4tIRpxKhoOwnXAiFn1Z7Xmric4USOIfKvTYQXk3QTc=", + "subType": "00" + } + } + ] + }, + { + "_id": { + "$numberInt": "1" + }, + "encryptedDecimal": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Mr/laWHUijZT5VT3x2a7crb7wgd/UXOGz8jr8BVqBpM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wXVD/HSbBljko0jJcaxJ1nrzs2+pchLQqYR3vywS8SU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VDCpBYsJIxTfcI6Zgf7FTmKMxUffQv+Ys8zt5dlK76I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zYDslUwOUVNwTYkETfjceH/PU3bac9X3UuQyYJ19qK0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rAOmHSz18Jx107xpbv9fYcPOmh/KPAqge0PAtuhIRnc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BFOB1OGVUen7VsOuS0g8Ti7oDsTt2Yj/k/7ta8YAdGM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2fckE5SPs0GU+akDkUEM6mm0EtcV3WDE/sQsnTtodlk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "mi9+aNjuwIvaMpSHENvKzKRAmX9cYguo2mXLvOoftHQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "K6TWn4VcWWkz/gkUkLmbtwkG7SNeABICmLDnoYJFlLU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Z+2/cEtGU0Fq7QJFNGA/0y4aWAsw0ncG6X0LYRqwS3c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rrSIf+lgcNZFbbUkS9BmE045jRWBpcBJXHzfMVEFuzE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KlHL3Kyje1/LMIfgbCqw1SolxffJvvgsYBV5y77wxuA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hzJ1YBoETmYeCh352dBmG8d8Wse/bUcqojTWpWQlgsc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lSdcllDXx8MA+s0GULjDA1lQkcV0L8/aHtZ6dM2pZ2c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "HGr7JLTTA7ksAnlmjSIwwdBVvgr3fv46/FTdiCPYpos=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "mMr25v1VwOEVZ8xaNUTHJCcsYqV+kwK6RzGYilxPtJ4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "129hJbziPJzNo0IoTU3bECdge0FtaPW8dm4dyNVNwYU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "doiLJ96qoo+v7NqIAZLq6BI5axV8Id8gT5vyJ1ZZ0PM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cW/Lcul3xYmfyvI/0x/+ybN78aQmBK1XIGs1EEU09N8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1aVIwzu9N5EJV9yEES+/g6hOTH7cA2NTcLIc59cu0wU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kw5tyl7Ew0r1wFyrN1mB9FiVW2hK2BxxxUuJDNWjyjQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ADAY2YBrm6RJBDY/eLLcfNxmSJku+mefz74gH66oyco=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8gkqB1LojzPrstpFG7RHYmWxXpIlPDTqWnNsXH7XDRU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "TESfVQMDQjfTZmHmUeYUE2XrokJ6CcrsKx/GmypGjOw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qFM+HFVQ539S0Ouynd1fBHoemFxtU9PRxE5+Dq7Ljy4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jPiFgUZteSmOg4wf3bsEKCZzcnxmMoILsgp/GaZD+dM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YaWUgJhYgPNN7TkFK16H8SsQS226JguaVhOIQxZwQNQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x90/Qk3AgyaFsvWf2KUCu5XF3j76WFSjt/GrnG01060=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ZGWybWL/xlEdMYRFCZDUoz10sywTf7U/7wufsb78lH0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8l4ganN66jIcdxfHAdYLaym/mdzUUQ8TViw3MDRySPc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c8p5XEGTqxqvRGVlR+nkxw9uUdoqDqTB0jlYQ361qMA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1ZGFLlpQBcU3zIUg8MmgWwFKVz/SaA7eSYFrfe3Hb70=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "34529174M77rHr3Ftn9r8jU4a5ztYtyVhMn1wryZSkU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YkQ4pxFWzc49MS0vZM6S8mNo4wAwo21rePBeF3C+9mI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MhOf4mYY00KKVhptOcXf0bXB7WfuuM801MRJg4vXPgc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7pbbD8ihNIYIBJ3tAUPGzHpFPpIeCTAk5L88qCB0/9w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "C9Q5PoNJTQo6pmNzXEEXUEqH22//UUWY1gqILcIywec=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "AqGVk1QjDNDLYWGRBX/nv9QdGR2SEgXZEhF0EWBAiSE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/sGI3VCbJUKATULJmhTayPOeVW+5MjWSvVCqS77sRbU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yOtbL0ih7gsuoxVtRrACMz+4N5uo7jIR7zzmtih2Beo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uA6dkb2Iyg9Su8UNDvZzkPx33kPZtWr/CCuEY+XgzUM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1DoSFPdHIplqZk+DyWAmEPckWwXw/GdB25NLmzeEZhk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OfDVS0T3ZuIXI/LNbTp6C9UbPIWLKiMy6Wx+9tqNl+g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "3PZjHXbmG6GtPz+iapKtQ3yY4PoFFgjIy+fV2xQv1YU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kaoLN0BoBWsmqE7kKkJQejATmLShd8qffcAmlhsxsGY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vpiw9KgQdegGmp7IJnSGX2miujRLU0xzs0ITTqbPW7c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NuXFf7xGUefYjIUTuMxNUTCfVHrF8oL0AT7dPv5Plk4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8Tz53LxtfEBJ9eR+d2690kwNsqPV6XyKo2PlqZCbUrc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "e6zsOmHSyV8tyQtSX6BSwui6wK9v1xG3giY/IILJQ2w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2fedFMCxa2DzmIpfbDKGXhQg0PPwbUv6vIWdwwlvhms=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yEJKMFnWXTC8tJUfzCInzQRByNEPjHxpw4L4m8No91Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YbFuWwOiFuQyOzIJXDbOkCWC2DyrG+248TBuVCa1pXU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "w7IkwGdrguwDrar5+w0Z3va5wXyZ4VXJkDMISyRjPGo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YmJUoILTRJPhyIyWyXJTsQ6KSZHHbEpwPVup6Ldm/Ko=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FvMjcwVZJmfh6FP/yBg2wgskK+KHD8YVUY6WtrE8xbg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "h4HCtD4HyYz0nci49IVAa10Z4NJD/FHnRMV4sRX6qro=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "nC7BpXCmym+a0Is2kReM9cYN2M1Eh5rVo8fjms14Oiw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1qtVWaeVo649ZZZtN8gXbwLgMWGLhz8beODbvru0I7Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Ej+mC0QFyMNIiSjR939S+iGBm7dm+1xObu5IcF/OpbU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UQ8LbUG3cMegbr9yKfKanAPQE1EfPkFciVDrNqZ5GHY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4iI3mXIDjnX+ralk1HhJY43mZx2uTJM7hsv9MQzTX7E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0WQCcs3rvsasgohERHHCaBM4Iy6yomS4qJ5To3/yYiw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qDCTVPoue1/DOAGNAlUstdA9Sid8MgEY4e5EzHcVHRk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9F9Mus0UnlzHb8E8ImxgXtz6SU98YXD0JqswOKw/Bzs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pctHpHKVBBcsahQ6TNh6/1V1ZrqOtKSAPtATV6BJqh0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vfR3C/4cPkVdxtNaqtF/v635ONbhTf5WbwJM6s4EXNE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ejP43xUBIex6szDcqExAFpx1IE/Ksi5ywJ84GKDFRrs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jbP4AWYd3S2f3ejmMG7dS5IbrFol48UUoT+ve3JLN6U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CiDifI7958sUjNqJUBQULeyF7x0Up3loPWvYKw9uAuw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "e2dQFsiHqd2BFHNhlSxocjd+cPs4wkcUW/CnCz4KNuM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PJFckVmzBipqaEqsuP2mkjhJE4qhw36NhfQ9DcOHyEU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "S3MeuJhET/B8VcfZYDR9fvX0nscDj416jdDekhmK11s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CGVHZRXpuNtQviDB2Kj03Q8uvs4w3RwTgV847R7GwPw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yUGgmgyLrxbEpDVy89XN3c2cmFpZXWWmuJ/35zVZ+Jw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "inb6Q97mL1a9onfNTT8v9wsoi/fz7KXKq3p8j90AU9c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CCyYx/4npq9xGO1lsCo8ZJhFO9/tN7DB+/DTE778rYg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "LNnYw4fwbiAZu0kBdAHPEm/OFnreS+oArdB5O/l/I98=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "P006SxmUS/RjiQJVYPdMFnNo3827GIEmSzagggkg05Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "oyvwY+WsnYV6UHuPki1o0ILJ2jN4uyXf9yaUNtZJyBA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "36Lk3RHWh1wmtCWC/Yj6jNIo17U5y6SofAgQjzjVxD8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vOOo8FqeHnuO9mqOYjIb4vgwIwVyXZ5Y+bY5d9tGFUM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "bJiDJjwQRNxqxlGjRm5lLziFhcfTDCnQ/qU1V85qcRg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2Qgrm1n0wUELAQnpkEiIHB856yv76q8jLbpiucetcm0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "5ciPOYxTK0WDwwYyfs7yiVymwtYQXDELLxmM4JLl4/o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "31dC2WUSIOKQc4jwT6PikfeYTwi80mTlh7P31T5KNQU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YluTV2Mu53EGCKLcWfHZb0BM/IPW2xJdG3vYlDMEsM4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dh/8lGo2Ek6KukSwutH6Q35iy8TgV0FN0SJqe0ZVHN8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EVw6HpIs3BKen2qY2gz4y5dw1JpXilfh07msZfQqJpc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FYolLla9L8EZMROEdWetozroU40Dnmwwx2jIMrr7c1A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8M6k4QIutSIj6CM41vvkQtuFsaGrjoR9SZJVSLbfGKQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9LM0VoddDNHway442MqY+Z7vohB2UHau/cddshhzf40=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "66i8Ytco4Yq/FMl6pIRZazz3CZlu8fO2OI6Pne0pvHU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2a/HgX+MjZxjXtSvHgF1yEpHMJBkl8Caee8XrJtn0WM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "frhBM662c4ZVG7mWP8K/HhRjd01lydW/cPcHnDjifqc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6k1T7Q1t668PBqv6fwpVnT1HWh7Am5LtbKvwPJKcpGU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UlJ5Edfusp8S/Pyhw6KTglIejmbr1HO0zUeHn/qFETA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jsxsB+1ECB3assUdoC333do9tYH+LglHmVSJHy4N8Hg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2nzIQxGYF7j3bGsIesECEOqhObKs/9ywknPHeJ3yges=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xJYKtuWrX90JrJVoYtnwP7Ce59XQGFYoalxpNfBXEH0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NLI5lriBTleGCELcHBtNnmnvwSRkHHaLOX4cKboMgTw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hUOQV0RmE5aJdJww1AR9rirJG4zOYPo+6cCkgn/BGvQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "h4G2Of76AgxcUziBwCyH+ayMOpdBWzg4yFrTfehSC2c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VuamM75RzGfQpj2/Y1jSVuQLrhy6OAwlZxjuQLB/9Ss=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kn9+hLq7hvw02xr9vrplOCDXKBTuFhfbX7d5v/l85Pg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fAiGqKyLZpGngBYFbtYUYt8LUrJ49vYafiboifTDjxs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BxRILymgfVJCczqjUIWXcfrfSgrrYkxTM5VTg0HkZLY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CrFY/PzfPU2zsFkGLu/dI6mEeizZzCR+uYgjZBAHro0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "AEbrIuwvXLTtYgMjOqnGQ8y8axUn5Ukrn7UZRSyfQVw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ouWeVH3PEFg+dKWlXc6BmqirJOaVWjJbMzZbCsce4dA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+hd6xFB+EG+kVP7WH4uMd1CLaWMnt5xJRaY/Guuga9Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zmpGalfAOL3gmcUMJYcLYIRT/2VDO/1Dw4KdYZoNcng=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2PbHAoM/46J2UIZ/vyksKzmVVfxA7YUyIxWeL/N/vBk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7fD9x+zk5MVFesb59Klqiwwmve7P5ON/5COURXj5smE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tlrNQ4jaq051iaWonuv1sSrYhKkL1LtNZuHsvATha3s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fBodm28iClNpvlRyVq0dOdXQ08S7/N3aDwid+PdWvRo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "O+/nnRqT3Zv7yMMGug8GhKHaWy6u7BfRGtZoj0sdN1c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "5AZZ/RTMY4Photnm/cpXZr/HnFRi3eljacMsipkJLHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "oFVyo/kgoMxBIk2VE52ySSimeyU+Gr0EfCwapXnTpKA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Z8v59DfcnviA0mzvnUk+URVO0UuqAWvtarEgJva/n1c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "P64GOntZ+zBJEHkigoh9FSxSO+rJTqR20z5aiGQ9an4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xMbSuDPfWuO/Dm7wuVl06GnzG9uzTlJJX9vFy7boGlY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kXPB19mRClxdH2UsHwlttS6lLU2uHvzuZgZz7kC45jU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NDVjVYXAw4k0w4tFzvs7QDq39aaU3HQor4I2XMKKnCk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uKw/+ErVfpTO1dGUfd3T/eWfZW3nUxXCdBGdjvHtZ88=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "av0uxEzWkizYWm0QUM/MN1hLibnxPvCWJKwjOV4yVQY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ERwUC47dvgOBzIsEESMIioLYbFOxOe8PtJTnmDkKuHM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2gseKlG5Le12fS/vj4eaED4lturF16kAgJ1TpW3HxEE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7Cvg0Y3j/5i2F1TeXxlMmU7xwif5dCmwkZAOrVC5K2Y=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Decimal-FindOneAndUpdate.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-Decimal-FindOneAndUpdate.yml new file mode 100644 index 00000000000..3b772cd6d78 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Decimal-FindOneAndUpdate.yml @@ -0,0 +1,1688 @@ +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + # Tests for Decimal (without precision) must only run against a replica set. Decimal (without precision) queries are expected to take a long time and may exceed the default mongos timeout. + topology: [ "replicaset" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDecimal', 'bsonType': 'decimal', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "FLE2 Range Decimal. FindOneAndUpdate." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDecimal: { $numberDecimal: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDecimal: { $numberDecimal: "1" } } + - name: findOneAndUpdate + arguments: + filter: { encryptedDecimal: { $gt: {$numberDecimal: "0"}} } + update: { "$set": { "encryptedDecimal": {$numberDecimal: "2"}}} + returnDocument: Before + result: *doc1 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDecimal": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDecimal": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + findAndModify: *collection_name + query: { + "encryptedDecimal": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + } + update: { "$set": {"encryptedDecimal": { $$type: "binData" }} } + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + "deleteTokens": { + "default.default": { + "encryptedDecimal": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + command_name: findAndModify + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimal": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rbf3AeBEv4wWFAKknqDxRW5cLNkFvbIs6iJjc6LShQY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0l86Ag5OszXpa78SlOUV3K9nff5iC1p0mRXtLg9M1s4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Hn6yuxFHodeyu7ISlhYrbSf9pTiH4TDEvbYLWjTwFO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zdf4y2etKBuIpkEU1zMwoCkCsdisfXZCh8QPamm+drY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rOQ9oMdiK5xxGH+jPzOvwVqdGGnF3+HkJXxn81s6hp4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "61aKKsE3+BJHHWYvs3xSIBvlRmKswmaOo5rygQJguUg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KuDb/GIzqDM8wv7m7m8AECiWJbae5EKKtJRugZx7kR0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Q+t8t2TmNUiCIorVr9F3AlVnX+Mpt2ZYvN+s8UGict8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJRZIpKxUgHyL83kW8cvfjkxN3z6WoNnUg+SQw+LK+k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnUsYjip8SvW0+m9mR5WWTkpK+p6uwJ6yBUAlBnFKMk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PArHlz+yPRYDycAP/PgnI/AkP8Wgmfg++Vf4UG1Bf0E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wnIh53Q3jeK8jEBe1n8kJLa89/H0BxO26ZU8SRIAs9Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4F8U59gzBLGhq58PEWQk2nch+R0Va7eTUoxMneReUIA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ihKagIW3uT1dm22ROr/g5QaCpxZVj2+Fs/YSdM2Noco=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EJtUOOwjkrPUi9mavYAi+Gom9Y2DuFll7aDwo4mq0M0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dIkr8dbaVRQFskAVT6B286BbcBBt1pZPEOcTZqk4ZcI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aYVAcZYkH/Tieoa1XOjE/zCy5AJcVTHjS0NG2QB7muA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sBidL6y8TenseetpioIAAtn0lK/7C8MoW4JXpVYi3z8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0Dd2klU/t4R86c2WJcJDAd57k/N7OjvYSO5Vf8KH8sw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I3jZ92WEVmZmgaIkLbuWhBxl7EM6bEjiEttgBJunArA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aGHoQMlgJoGvArjfIbc3nnkoc8SWBxcrN7hSmjMRzos=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "bpiWPnF/KVBQr5F6MEwc5ZZayzIRvQOLDAm4ntwOi8g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tI7QVKbE6avWgDD9h4QKyFlnTxFCwd2iLySKakxNR/I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XGsge0CnoaXgE3rcpKm8AEeku5QVfokS3kcI+JKV1lk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JQxlryW2Q5WOwfrjAnaZxDvC83Dg6sjRVP5zegf2WiM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YFuHKJOfoqp1iGVxoFjx7bLYgVdsN4GuUFxEgO9HJ5s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Z6vUdiCR18ylKomf08uxcQHeRtmyav7/Ecvzz4av3k4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SPGo1Ib5AiP/tSllL7Z5PAypvnKdwJLzt8imfIMSEJQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "m94Nh6PFFQFLIib9Cu5LAKavhXnagSHG6F5EF8lD96I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pfEkQI98mB+gm1+JbmVurPAODMFPJ4E8DnqfVyUWbSo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DNj3OVRLbr43s0vd+rgWghOL3FqeO/60npdojC8Ry/M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kAYIQrjHVu49W8FTxyxJeiLVRWWjC9fPcBn+Hx1F+Ss=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aCSO7UVOpoQvu/iridarxkxV1SVxU1i9HVSYXUAeXk4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Gh6hTP/yj1IKlXQ+Q69KTfMlGZjEcXoRLGbQHNFo/1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/gDgIFQ4tAlJk3GN48IS5Qa5IPmErwGk8CHxAbp6gs0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PICyimwPjxpusyKxNssOOwUotAUbygpyEtORsVGXT8g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4lu+cBHyAUvuxC6JUNyHLzHsCogGSWFFnUCkDwfQdgI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pSndkmoNUJwXjgkbkgOrT5f9nSvuoMEZOkwAN9ElRaE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tyW+D4i26QihNM5MuBM+wnt5AdWGSJaJ4X5ydc9iWTU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9Syjr8RoxUgPKr+O5rsCu07AvcebA4P8IVKyS1NVLWc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "67tPfDYnK2tmrioI51fOBG0ygajcV0pLo5+Zm/rEW7U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "y0EiPRxYTuS1eVTIaPQUQBBxwkyxNckbePvKgChwd0M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NWd+2veAaeXQgR3vCvzlI4R1WW67D5YsVLdoXfdb8qg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PY5RQqKQsL2GqBBSPNOEVpojNFRX/NijCghIpxD6CZk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lcvwTyEjFlssCJtdjRpdN6oY+C7bxZY+WA+QAqzj9zg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWE7XRNylvTwO/9Fv56dNqUaQWMmESNS/GNIwgBaEI0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ijwlrUeS8nRYqK1F8kiCYF0mNDolEZS+/lJO1Lg93C8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8KzV+qYGYuIjoNj8eEpnTuHrMYuhzphl80rS6wrODuU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wDyTLjSEFF895hSQsHvmoEQVS6KIkZOtq1c9dVogm9I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SGrtPuMYCjUrfKF0Pq/thdaQzmGBMUvlwN3ORIu9tHU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KySHON3hIoUk4xWcwTqk6IL0kgjzjxgMBObVIkCGvk4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hBIdS9j0XJPeT4ot73ngELkpUoSixvRBvdOL9z48jY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Tx6um0q9HjS5ZvlFhvukpI6ORnyrXMWVW1OoxvgqII0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zFKlyfX5H81+d4A4J3FKn4T5JfG+OWtR06ddyX4Mxas=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cGgCDuPV7MeMMYEDpgOupqyNP4BQ4H7rBnd2QygumgM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IPaUoy98v11EoglTpJ4kBlEawoZ8y7BPwzjLYBpkvHQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Pfo4Am6tOWAyZNn8G9W5HWWGC3ZWmX0igI/RRB870Ro=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fnTSjd7bC1Udoq6iM7UDnHAC/lsIXSHp/Gy332qw+/I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fApBgVRrTDyEumkeWs5p3ag9KB48SbU4Si0dl7Ns9rc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QxudfBItgoCnUj5NXVnSmWH3HK76YtKkMmzn4lyyUYY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sSOvwhKa29Wq94bZ5jGIiJQGbG1uBrKSBfOYBz/oZeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FdaMgwwJ0NKsqmPZLC5oE+/0D74Dfpvig3LaI5yW5Fs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sRWBy12IERN43BSZIrnBfC9+zFBUdvjTlkqIH81NGt4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/4tIRpxKhoOwnXAiFn1Z7Xmric4USOIfKvTYQXk3QTc=", + "subType": "00" + } + } + ] + } + - + { + "_id": { + "$numberInt": "1" + }, + "encryptedDecimal": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Mr/laWHUijZT5VT3x2a7crb7wgd/UXOGz8jr8BVqBpM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wXVD/HSbBljko0jJcaxJ1nrzs2+pchLQqYR3vywS8SU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VDCpBYsJIxTfcI6Zgf7FTmKMxUffQv+Ys8zt5dlK76I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zYDslUwOUVNwTYkETfjceH/PU3bac9X3UuQyYJ19qK0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rAOmHSz18Jx107xpbv9fYcPOmh/KPAqge0PAtuhIRnc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BFOB1OGVUen7VsOuS0g8Ti7oDsTt2Yj/k/7ta8YAdGM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2fckE5SPs0GU+akDkUEM6mm0EtcV3WDE/sQsnTtodlk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "mi9+aNjuwIvaMpSHENvKzKRAmX9cYguo2mXLvOoftHQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "K6TWn4VcWWkz/gkUkLmbtwkG7SNeABICmLDnoYJFlLU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Z+2/cEtGU0Fq7QJFNGA/0y4aWAsw0ncG6X0LYRqwS3c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rrSIf+lgcNZFbbUkS9BmE045jRWBpcBJXHzfMVEFuzE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KlHL3Kyje1/LMIfgbCqw1SolxffJvvgsYBV5y77wxuA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hzJ1YBoETmYeCh352dBmG8d8Wse/bUcqojTWpWQlgsc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lSdcllDXx8MA+s0GULjDA1lQkcV0L8/aHtZ6dM2pZ2c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "HGr7JLTTA7ksAnlmjSIwwdBVvgr3fv46/FTdiCPYpos=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "mMr25v1VwOEVZ8xaNUTHJCcsYqV+kwK6RzGYilxPtJ4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "129hJbziPJzNo0IoTU3bECdge0FtaPW8dm4dyNVNwYU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "doiLJ96qoo+v7NqIAZLq6BI5axV8Id8gT5vyJ1ZZ0PM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cW/Lcul3xYmfyvI/0x/+ybN78aQmBK1XIGs1EEU09N8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1aVIwzu9N5EJV9yEES+/g6hOTH7cA2NTcLIc59cu0wU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kw5tyl7Ew0r1wFyrN1mB9FiVW2hK2BxxxUuJDNWjyjQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ADAY2YBrm6RJBDY/eLLcfNxmSJku+mefz74gH66oyco=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8gkqB1LojzPrstpFG7RHYmWxXpIlPDTqWnNsXH7XDRU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "TESfVQMDQjfTZmHmUeYUE2XrokJ6CcrsKx/GmypGjOw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qFM+HFVQ539S0Ouynd1fBHoemFxtU9PRxE5+Dq7Ljy4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jPiFgUZteSmOg4wf3bsEKCZzcnxmMoILsgp/GaZD+dM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YaWUgJhYgPNN7TkFK16H8SsQS226JguaVhOIQxZwQNQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x90/Qk3AgyaFsvWf2KUCu5XF3j76WFSjt/GrnG01060=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ZGWybWL/xlEdMYRFCZDUoz10sywTf7U/7wufsb78lH0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8l4ganN66jIcdxfHAdYLaym/mdzUUQ8TViw3MDRySPc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c8p5XEGTqxqvRGVlR+nkxw9uUdoqDqTB0jlYQ361qMA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1ZGFLlpQBcU3zIUg8MmgWwFKVz/SaA7eSYFrfe3Hb70=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "34529174M77rHr3Ftn9r8jU4a5ztYtyVhMn1wryZSkU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YkQ4pxFWzc49MS0vZM6S8mNo4wAwo21rePBeF3C+9mI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MhOf4mYY00KKVhptOcXf0bXB7WfuuM801MRJg4vXPgc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7pbbD8ihNIYIBJ3tAUPGzHpFPpIeCTAk5L88qCB0/9w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "C9Q5PoNJTQo6pmNzXEEXUEqH22//UUWY1gqILcIywec=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "AqGVk1QjDNDLYWGRBX/nv9QdGR2SEgXZEhF0EWBAiSE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/sGI3VCbJUKATULJmhTayPOeVW+5MjWSvVCqS77sRbU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yOtbL0ih7gsuoxVtRrACMz+4N5uo7jIR7zzmtih2Beo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uA6dkb2Iyg9Su8UNDvZzkPx33kPZtWr/CCuEY+XgzUM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1DoSFPdHIplqZk+DyWAmEPckWwXw/GdB25NLmzeEZhk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OfDVS0T3ZuIXI/LNbTp6C9UbPIWLKiMy6Wx+9tqNl+g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "3PZjHXbmG6GtPz+iapKtQ3yY4PoFFgjIy+fV2xQv1YU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kaoLN0BoBWsmqE7kKkJQejATmLShd8qffcAmlhsxsGY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vpiw9KgQdegGmp7IJnSGX2miujRLU0xzs0ITTqbPW7c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NuXFf7xGUefYjIUTuMxNUTCfVHrF8oL0AT7dPv5Plk4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8Tz53LxtfEBJ9eR+d2690kwNsqPV6XyKo2PlqZCbUrc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "e6zsOmHSyV8tyQtSX6BSwui6wK9v1xG3giY/IILJQ2w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2fedFMCxa2DzmIpfbDKGXhQg0PPwbUv6vIWdwwlvhms=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yEJKMFnWXTC8tJUfzCInzQRByNEPjHxpw4L4m8No91Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YbFuWwOiFuQyOzIJXDbOkCWC2DyrG+248TBuVCa1pXU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "w7IkwGdrguwDrar5+w0Z3va5wXyZ4VXJkDMISyRjPGo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YmJUoILTRJPhyIyWyXJTsQ6KSZHHbEpwPVup6Ldm/Ko=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FvMjcwVZJmfh6FP/yBg2wgskK+KHD8YVUY6WtrE8xbg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "h4HCtD4HyYz0nci49IVAa10Z4NJD/FHnRMV4sRX6qro=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "nC7BpXCmym+a0Is2kReM9cYN2M1Eh5rVo8fjms14Oiw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1qtVWaeVo649ZZZtN8gXbwLgMWGLhz8beODbvru0I7Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Ej+mC0QFyMNIiSjR939S+iGBm7dm+1xObu5IcF/OpbU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UQ8LbUG3cMegbr9yKfKanAPQE1EfPkFciVDrNqZ5GHY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4iI3mXIDjnX+ralk1HhJY43mZx2uTJM7hsv9MQzTX7E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0WQCcs3rvsasgohERHHCaBM4Iy6yomS4qJ5To3/yYiw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qDCTVPoue1/DOAGNAlUstdA9Sid8MgEY4e5EzHcVHRk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9F9Mus0UnlzHb8E8ImxgXtz6SU98YXD0JqswOKw/Bzs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pctHpHKVBBcsahQ6TNh6/1V1ZrqOtKSAPtATV6BJqh0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vfR3C/4cPkVdxtNaqtF/v635ONbhTf5WbwJM6s4EXNE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ejP43xUBIex6szDcqExAFpx1IE/Ksi5ywJ84GKDFRrs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jbP4AWYd3S2f3ejmMG7dS5IbrFol48UUoT+ve3JLN6U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CiDifI7958sUjNqJUBQULeyF7x0Up3loPWvYKw9uAuw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "e2dQFsiHqd2BFHNhlSxocjd+cPs4wkcUW/CnCz4KNuM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PJFckVmzBipqaEqsuP2mkjhJE4qhw36NhfQ9DcOHyEU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "S3MeuJhET/B8VcfZYDR9fvX0nscDj416jdDekhmK11s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CGVHZRXpuNtQviDB2Kj03Q8uvs4w3RwTgV847R7GwPw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yUGgmgyLrxbEpDVy89XN3c2cmFpZXWWmuJ/35zVZ+Jw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "inb6Q97mL1a9onfNTT8v9wsoi/fz7KXKq3p8j90AU9c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CCyYx/4npq9xGO1lsCo8ZJhFO9/tN7DB+/DTE778rYg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "LNnYw4fwbiAZu0kBdAHPEm/OFnreS+oArdB5O/l/I98=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "P006SxmUS/RjiQJVYPdMFnNo3827GIEmSzagggkg05Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "oyvwY+WsnYV6UHuPki1o0ILJ2jN4uyXf9yaUNtZJyBA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "36Lk3RHWh1wmtCWC/Yj6jNIo17U5y6SofAgQjzjVxD8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vOOo8FqeHnuO9mqOYjIb4vgwIwVyXZ5Y+bY5d9tGFUM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "bJiDJjwQRNxqxlGjRm5lLziFhcfTDCnQ/qU1V85qcRg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2Qgrm1n0wUELAQnpkEiIHB856yv76q8jLbpiucetcm0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "5ciPOYxTK0WDwwYyfs7yiVymwtYQXDELLxmM4JLl4/o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "31dC2WUSIOKQc4jwT6PikfeYTwi80mTlh7P31T5KNQU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YluTV2Mu53EGCKLcWfHZb0BM/IPW2xJdG3vYlDMEsM4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dh/8lGo2Ek6KukSwutH6Q35iy8TgV0FN0SJqe0ZVHN8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EVw6HpIs3BKen2qY2gz4y5dw1JpXilfh07msZfQqJpc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FYolLla9L8EZMROEdWetozroU40Dnmwwx2jIMrr7c1A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8M6k4QIutSIj6CM41vvkQtuFsaGrjoR9SZJVSLbfGKQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9LM0VoddDNHway442MqY+Z7vohB2UHau/cddshhzf40=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "66i8Ytco4Yq/FMl6pIRZazz3CZlu8fO2OI6Pne0pvHU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2a/HgX+MjZxjXtSvHgF1yEpHMJBkl8Caee8XrJtn0WM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "frhBM662c4ZVG7mWP8K/HhRjd01lydW/cPcHnDjifqc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6k1T7Q1t668PBqv6fwpVnT1HWh7Am5LtbKvwPJKcpGU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UlJ5Edfusp8S/Pyhw6KTglIejmbr1HO0zUeHn/qFETA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jsxsB+1ECB3assUdoC333do9tYH+LglHmVSJHy4N8Hg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2nzIQxGYF7j3bGsIesECEOqhObKs/9ywknPHeJ3yges=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xJYKtuWrX90JrJVoYtnwP7Ce59XQGFYoalxpNfBXEH0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NLI5lriBTleGCELcHBtNnmnvwSRkHHaLOX4cKboMgTw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hUOQV0RmE5aJdJww1AR9rirJG4zOYPo+6cCkgn/BGvQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "h4G2Of76AgxcUziBwCyH+ayMOpdBWzg4yFrTfehSC2c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VuamM75RzGfQpj2/Y1jSVuQLrhy6OAwlZxjuQLB/9Ss=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kn9+hLq7hvw02xr9vrplOCDXKBTuFhfbX7d5v/l85Pg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fAiGqKyLZpGngBYFbtYUYt8LUrJ49vYafiboifTDjxs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BxRILymgfVJCczqjUIWXcfrfSgrrYkxTM5VTg0HkZLY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CrFY/PzfPU2zsFkGLu/dI6mEeizZzCR+uYgjZBAHro0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "AEbrIuwvXLTtYgMjOqnGQ8y8axUn5Ukrn7UZRSyfQVw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ouWeVH3PEFg+dKWlXc6BmqirJOaVWjJbMzZbCsce4dA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+hd6xFB+EG+kVP7WH4uMd1CLaWMnt5xJRaY/Guuga9Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zmpGalfAOL3gmcUMJYcLYIRT/2VDO/1Dw4KdYZoNcng=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2PbHAoM/46J2UIZ/vyksKzmVVfxA7YUyIxWeL/N/vBk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7fD9x+zk5MVFesb59Klqiwwmve7P5ON/5COURXj5smE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tlrNQ4jaq051iaWonuv1sSrYhKkL1LtNZuHsvATha3s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fBodm28iClNpvlRyVq0dOdXQ08S7/N3aDwid+PdWvRo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "O+/nnRqT3Zv7yMMGug8GhKHaWy6u7BfRGtZoj0sdN1c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "5AZZ/RTMY4Photnm/cpXZr/HnFRi3eljacMsipkJLHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "oFVyo/kgoMxBIk2VE52ySSimeyU+Gr0EfCwapXnTpKA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Z8v59DfcnviA0mzvnUk+URVO0UuqAWvtarEgJva/n1c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "P64GOntZ+zBJEHkigoh9FSxSO+rJTqR20z5aiGQ9an4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xMbSuDPfWuO/Dm7wuVl06GnzG9uzTlJJX9vFy7boGlY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kXPB19mRClxdH2UsHwlttS6lLU2uHvzuZgZz7kC45jU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NDVjVYXAw4k0w4tFzvs7QDq39aaU3HQor4I2XMKKnCk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uKw/+ErVfpTO1dGUfd3T/eWfZW3nUxXCdBGdjvHtZ88=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "av0uxEzWkizYWm0QUM/MN1hLibnxPvCWJKwjOV4yVQY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ERwUC47dvgOBzIsEESMIioLYbFOxOe8PtJTnmDkKuHM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2gseKlG5Le12fS/vj4eaED4lturF16kAgJ1TpW3HxEE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7Cvg0Y3j/5i2F1TeXxlMmU7xwif5dCmwkZAOrVC5K2Y=", + "subType": "00" + } + } + ] + } \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Decimal-InsertFind.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-Decimal-InsertFind.json new file mode 100644 index 00000000000..cea03e23fe8 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Decimal-InsertFind.json @@ -0,0 +1,1899 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimal", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Decimal. Insert and Find.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimal": { + "$numberDecimal": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimal": { + "$numberDecimal": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimal": { + "$gt": { + "$numberDecimal": "0" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDecimal": { + "$numberDecimal": "1" + } + } + ] + } + ], + "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": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDecimal": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimal", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDecimal": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimal", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "encryptedDecimal": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimal", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimal": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rbf3AeBEv4wWFAKknqDxRW5cLNkFvbIs6iJjc6LShQY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0l86Ag5OszXpa78SlOUV3K9nff5iC1p0mRXtLg9M1s4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Hn6yuxFHodeyu7ISlhYrbSf9pTiH4TDEvbYLWjTwFO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zdf4y2etKBuIpkEU1zMwoCkCsdisfXZCh8QPamm+drY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rOQ9oMdiK5xxGH+jPzOvwVqdGGnF3+HkJXxn81s6hp4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "61aKKsE3+BJHHWYvs3xSIBvlRmKswmaOo5rygQJguUg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KuDb/GIzqDM8wv7m7m8AECiWJbae5EKKtJRugZx7kR0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Q+t8t2TmNUiCIorVr9F3AlVnX+Mpt2ZYvN+s8UGict8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJRZIpKxUgHyL83kW8cvfjkxN3z6WoNnUg+SQw+LK+k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnUsYjip8SvW0+m9mR5WWTkpK+p6uwJ6yBUAlBnFKMk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PArHlz+yPRYDycAP/PgnI/AkP8Wgmfg++Vf4UG1Bf0E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wnIh53Q3jeK8jEBe1n8kJLa89/H0BxO26ZU8SRIAs9Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4F8U59gzBLGhq58PEWQk2nch+R0Va7eTUoxMneReUIA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ihKagIW3uT1dm22ROr/g5QaCpxZVj2+Fs/YSdM2Noco=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EJtUOOwjkrPUi9mavYAi+Gom9Y2DuFll7aDwo4mq0M0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dIkr8dbaVRQFskAVT6B286BbcBBt1pZPEOcTZqk4ZcI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aYVAcZYkH/Tieoa1XOjE/zCy5AJcVTHjS0NG2QB7muA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sBidL6y8TenseetpioIAAtn0lK/7C8MoW4JXpVYi3z8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0Dd2klU/t4R86c2WJcJDAd57k/N7OjvYSO5Vf8KH8sw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I3jZ92WEVmZmgaIkLbuWhBxl7EM6bEjiEttgBJunArA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aGHoQMlgJoGvArjfIbc3nnkoc8SWBxcrN7hSmjMRzos=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "bpiWPnF/KVBQr5F6MEwc5ZZayzIRvQOLDAm4ntwOi8g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tI7QVKbE6avWgDD9h4QKyFlnTxFCwd2iLySKakxNR/I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XGsge0CnoaXgE3rcpKm8AEeku5QVfokS3kcI+JKV1lk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JQxlryW2Q5WOwfrjAnaZxDvC83Dg6sjRVP5zegf2WiM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YFuHKJOfoqp1iGVxoFjx7bLYgVdsN4GuUFxEgO9HJ5s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Z6vUdiCR18ylKomf08uxcQHeRtmyav7/Ecvzz4av3k4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SPGo1Ib5AiP/tSllL7Z5PAypvnKdwJLzt8imfIMSEJQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "m94Nh6PFFQFLIib9Cu5LAKavhXnagSHG6F5EF8lD96I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pfEkQI98mB+gm1+JbmVurPAODMFPJ4E8DnqfVyUWbSo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DNj3OVRLbr43s0vd+rgWghOL3FqeO/60npdojC8Ry/M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kAYIQrjHVu49W8FTxyxJeiLVRWWjC9fPcBn+Hx1F+Ss=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aCSO7UVOpoQvu/iridarxkxV1SVxU1i9HVSYXUAeXk4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Gh6hTP/yj1IKlXQ+Q69KTfMlGZjEcXoRLGbQHNFo/1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/gDgIFQ4tAlJk3GN48IS5Qa5IPmErwGk8CHxAbp6gs0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PICyimwPjxpusyKxNssOOwUotAUbygpyEtORsVGXT8g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4lu+cBHyAUvuxC6JUNyHLzHsCogGSWFFnUCkDwfQdgI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pSndkmoNUJwXjgkbkgOrT5f9nSvuoMEZOkwAN9ElRaE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tyW+D4i26QihNM5MuBM+wnt5AdWGSJaJ4X5ydc9iWTU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9Syjr8RoxUgPKr+O5rsCu07AvcebA4P8IVKyS1NVLWc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "67tPfDYnK2tmrioI51fOBG0ygajcV0pLo5+Zm/rEW7U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "y0EiPRxYTuS1eVTIaPQUQBBxwkyxNckbePvKgChwd0M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NWd+2veAaeXQgR3vCvzlI4R1WW67D5YsVLdoXfdb8qg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PY5RQqKQsL2GqBBSPNOEVpojNFRX/NijCghIpxD6CZk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lcvwTyEjFlssCJtdjRpdN6oY+C7bxZY+WA+QAqzj9zg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWE7XRNylvTwO/9Fv56dNqUaQWMmESNS/GNIwgBaEI0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ijwlrUeS8nRYqK1F8kiCYF0mNDolEZS+/lJO1Lg93C8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8KzV+qYGYuIjoNj8eEpnTuHrMYuhzphl80rS6wrODuU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wDyTLjSEFF895hSQsHvmoEQVS6KIkZOtq1c9dVogm9I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SGrtPuMYCjUrfKF0Pq/thdaQzmGBMUvlwN3ORIu9tHU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KySHON3hIoUk4xWcwTqk6IL0kgjzjxgMBObVIkCGvk4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hBIdS9j0XJPeT4ot73ngELkpUoSixvRBvdOL9z48jY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Tx6um0q9HjS5ZvlFhvukpI6ORnyrXMWVW1OoxvgqII0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zFKlyfX5H81+d4A4J3FKn4T5JfG+OWtR06ddyX4Mxas=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cGgCDuPV7MeMMYEDpgOupqyNP4BQ4H7rBnd2QygumgM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IPaUoy98v11EoglTpJ4kBlEawoZ8y7BPwzjLYBpkvHQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Pfo4Am6tOWAyZNn8G9W5HWWGC3ZWmX0igI/RRB870Ro=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fnTSjd7bC1Udoq6iM7UDnHAC/lsIXSHp/Gy332qw+/I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fApBgVRrTDyEumkeWs5p3ag9KB48SbU4Si0dl7Ns9rc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QxudfBItgoCnUj5NXVnSmWH3HK76YtKkMmzn4lyyUYY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sSOvwhKa29Wq94bZ5jGIiJQGbG1uBrKSBfOYBz/oZeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FdaMgwwJ0NKsqmPZLC5oE+/0D74Dfpvig3LaI5yW5Fs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sRWBy12IERN43BSZIrnBfC9+zFBUdvjTlkqIH81NGt4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/4tIRpxKhoOwnXAiFn1Z7Xmric4USOIfKvTYQXk3QTc=", + "subType": "00" + } + } + ] + }, + { + "_id": { + "$numberInt": "1" + }, + "encryptedDecimal": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RGTjNVEsNJb+DG7DpPOam8rQWD5HZAMpRyiTQaw7tk8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I93Md7QNPGmEEGYU1+VVCqBPBEvXdqHPtTJtMOn06Yk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "GecBFQ1PemlECWZWCl7f74vmsL6eB6mzQ9n6tK6FYfs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QpjhZl+O1ORifgtCZuWAdcP6OKL7IZ2cA46v8FJcV28=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RlQWwhU+uVv0a+9IB5cUkEfvHBvOw3B1Sx6WfPWMqes=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ubb81XTC7U+4tcNzf1oYvOY6gR5hC2Izqx54f4GuJ0E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6M4Q5NMQ9TqNnjzGOxIkiUIY8TEL0I3XD1QnhefQUqU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BtInzk9t2FFMCEY6AQ7zN8jwrrZEs2irSv6q0Q4NaIw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6vxXfETu9cuBIpRBo3jUUU04mJIH/aAhLX8K6VI5Xv0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wXPCdS+q23zi1bkPnaVG2j0PsVtxdeSLJ//h6J1x8RU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KY3KkfBAsN2l80wbpj41G0gwBR5KmmFnZcagg7D3ENk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tI8NFAxXCX4VOnY5X73K6KI/Yspd3aR94KV39MhJlAw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "nFxH0UC3mATKA6Vboz+QX/hAjj19kF/SH6H5Cne7qC0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q8hYqIYaIi7nOdG/7qQZYnz8Bsacfi66M1nVku4SH08=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4saA92R4arp4anvD9xFtze+sNcQqTEhPHyl1h70A8NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DbIziOBRRyeQS6RtBR09E37LV+CTKrEjGoRMLSpG6eE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Fv80Plp/7w2gnVqrwawLd6qhJ10G4NCDm3re67cNq4Y=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "T/T2oiQCBBES4YN7EodzPRdabZSFlYIClHBym+bQUZE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ZQgHD3l46Ujqtbnj1VbbeM29C9wJzOhz+yZ/7XdSrxk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ltlFKzWvyZvHxDFOYDd/XXJ6kUiJj0ln2HTCEz2o4Z4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "flW8A7bltC1u8bzx0WJtxosGJdOVsJFfbx33jxnpFGg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SXO+92QbMKwUSG2t27ciunV1c3VvFkUuDmSczpRe008=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+KioGs1GM+xRBzFE67ePTWj04KMSE5/Y6qUF7nJ5kvU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L3xNVbh6YH+RzqABN+5Jgb7T234Efpn766DmUvxIxgg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hPF+60mBYPjh21dEmPlBhKgyc9S2qLtTkypYvnqP2Fc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EletRsETy2HcjaPIm2c8CkT7ch/P3pJJDC8hasepcSU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "r5bMXUaNKqLPxZ+TG9HYTG4aSDgcpim27rN8rQFkM0w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0Q7Erdr8+/S0wUEDDIqlS5XjBVWvhZY65K0uUDb6+Ns=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xEcnhXy35hbXNVBPOOt3TUHbxvKfQ48KjA9b6/rbMqQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "T8bEpiQNgsEudXvyKE9SZlSvbpV/LUaslsdqgSFltyo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hIoiaF2YjnxDbODfhFEB+JGZ5nf8suD3Shck5bwQ3N0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qnA6qzejeRJ0rsZaZ0zOvKAaXyxt5lpscKQNYFZNl4k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "anAKCL2DN/le2VaP0n2ucYSEH/DaaEH/8Sa4OqTZsRA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JCZlBJaFm618oWYSnT9Jr1MtwFVw4BZjOzO+5yWgR90=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yxyk4n9762WzcDVGnTn4jCqUnSMIVCrLDIjCX1QVj34=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fDI6fdKvDJwim5/CQwWZEzcrXE3LHgy7FTtffcC7tXE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Vex+gcz5T+WkzsVZQrkqUR2ryyZbnaOGuWpYvjN0zCw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8TLEXz+Gbbp6llHpZXVjLsdlYY9f6hrKpHVpyfDe0RY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7fTyt5BrunypS65TfOzFW2E2qdIuT4SLeDeGlbQoJCs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8fKGrkqN0/KuSjyXgDBmRauDKrSa//JBKRWHEB9xBf4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s4codmG7uN4ss6P357jL21lazEe90M9GOK5WrOknSV0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RkSpua8XF+NUdxVDU90EbLUTTyZFX3tt3atBTroFaRk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "LnTCuCDyAHK5B9KXzjtwGmWB+qergQk2OCjnIx9MI2A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cBFh0virAX4pVXf/udIGI2951i0+0aZAdJcBVGtYnT4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "G54X6myQXWZ5fw/G31en3QbdgfXzL9+hFTtJpnWMqDI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EdsiiuezcsFJFnYIyGjCOhnqMj1BOwTB5EFxN+ERUkg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dVH9MXLtk0WTwGQ3xmrhOqfropMUkDW3o6paNPGl3NU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sB3HqXKWY3pKbuEH8BTbfNIGfbY+7/ZbOc3XC+JRNNI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WHyDk62Xhqbo4/iie2aLIM4x2uuAjv6102dJSHI58oM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pNUFuHpeNRDUZ/NrtII2c6sNc9eGR1lIUlIyXKERA+0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UPa+pdCqnN0bfAptdzldQOSd01gidrDKy8KhWrpSKAI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "l+7dOAlo+HUffMqFYXL6pgUFeTbwOM9CjKQLxEoLtc4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SRnDXV/rN6C8xwMutv9E1luv3DOUio3VkgPr8Cpm7Ew=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QcH6gl+gX7xZ7OWhUNQMbndJy0Piz49pDo6RsnLkVSA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "t+uL4DnfsI/Zll/KXWW1cOKX3Hu8WIkm3pt9efCVSAQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "myutHDctku/+Uug/nD8gRbYvmx/IovtoAAC2/fz2oHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6C+cjD0e0nSCP6cPqQYbNG7SlOd6Mfvi8hyfm7Ng+D8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zg01JSoOj9oBKT0S1ldJucXzY5AKgreS+h2xJreWTOs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7qQ80/FjodHl1m1py/Oii0/9C/xWbLdhaRXQ+kkCP10=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YwWMNH07vL6c5Nhg+MRnVByhzUunu8y0VLM9z/XvR5U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Dle8bU98+fudAbc14SToZFkwvV3tcYVsjDug0NWljpc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "J+eKL1vPJmlzltvhI6Li5Fz/TJmi3Ng+ehRTcs46API=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zB3XzfFygLwC3WHkj0up+VbEd25KKoce1vOpG/5bwK4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vnVnmOnL+z2pqwE+A6cVKS0Iwy4F4/2IiElJca9bUQM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+lG5r/Fpqry3BtFuvY67+RntmHAMDoLVOSGc6ZoXPb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L5MXQertqc6uj7ADe8aWKbd1sYHPCE7P1VYVg9Zc3VI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "imKONuZgopt0bhM3GMX2WVPwQYMTobuUUEdhcLfHs4c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "eOkU1J1uVbiVFWBerbXsSIVcF2nqiicTkFy4x7kFHB8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gI0uDhXeoH/UatDQKEf4qo8FHzWZDhb/wuWTqbq/ID4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cOkd5Aa3btYhtojE/smsF/PJnULqQ4NNqTkU6KXTFmo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "AWNJMs1MTe294oFipp8Y6P0CjpkZ4qCZoClQF3XcHq8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6gJtlzXOFhGYrVbTuRMmvMlDTwXdNtR9aGBlHZPwIMw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "LEmwVGA/xsEG7UrcOoYLFu6KCXgijzFznenknuDacm8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "mIRFPTXRrGaPtp/Ydij2jgkRe4uoUvAKxW2d8b9zYL0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "B+Uv2u48WALOO0L311z+eryjYQzKJVMfdHMZPhOAFmY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "INXXp0wDyVCq+NtfIrrC2ciETmyW/dWB/48/u4yLEZ4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "se7DGo8XrlrQDLEcco1tZrQt9kDe+0RTyl2bw/quG4w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vr0m2+Zk9lbN6UgWCyn8xJWJOokU3IDYab5U5q1+CgQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XI+eJ8Gy2JktG1gICgoj1qpsfy1tKmH0kglWbaQH6DA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A+UCuNnuAUqnQzspA6TVqUPRmtZmpSex5HFw7THRxs0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xaH2Ehfljd19uo0Fvb3iwkdaiWEVQd2YPoitgEPkhSM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "S/iZBJGcc8+qZxyMtab65MMBoSglybwk3x58Nb86gnY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "w14ZE5qqY5YgkS4Zcs9YNbrQbY1XfGOOHNn9bOYnFVQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0MhGd/jEF1vjkKGp+ZMn9SjLK54jkp9W4Hg+Sp/oxaI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "92QZ73e/NRTYgCm4aifaKth6aAsKnLLccBc0zx/qUTY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WOjzemCgFJOiGIp81RSVh/tFlzSTj9eFWcBnsiv2Ycs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DrsP9CmfKPjw5yLL8bnSeAxfNzAwlb+Z8OqCiKgBY7o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lMogqg8veBv6mri3/drMe9afJiKMvevkmGcw9BedfLo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "TxqwNcY8Tg2MPpNdkPBwvfpuTttSYRHU26DGECKYQ9o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "l0u1b4b4vYACWIwfnB7PZac4oDEgjQZCzHruNPTgAIY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "iVSGQ+cCfhbWIrY/v/WBORK92elu9gfRKyGhr6r/k00=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yK1forG50diEXte8ECzjfpHeYsPyuQ/dgxbxn/nzY5k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gIfTLCD3VwnOwkC0zPXWTqaITxX6ZplA69PO2a6zolc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "O/Zxlgh3WqpzJ7+Sd8XWMVID4/GXJUUWaSqfgDUi3b0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ZQ6yv368zwahUqSUYH/StL0Qgz/TwS1CzlMjVDvCciI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "m2rPEYkjwyiKdonMrKlcF7hya4lFOAUwEePJ3SgrNx8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Mq0yl5iVKlq71bT/dT/fXOWf2n90bTnXFnOdGDN0JOc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6qDGMXipPLC2O6EAAMjO2F9xx4rdqZso4IkPpH2304U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jvQHRQQa2RIszE2LX2Hv2LbRhYawJ6qmtRt8HZzFQXg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ovJXQrkZlpeHRciKyE/WWNm5O389gRgzx1W+Dw596X4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "a4kgRNvYctGYqyQv9qScL/WkljTYVylJ9pE9KDULlxU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qV4Q48vPiCJMTjljotzYKI/zfExWpkKOSHGcAjGyDig=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jtI7zbBF+QW/aYYTkn90zzyHLXLgmy7l1bzgMb2oqic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q0KmJl9txPdn962UNvnfe6UFhdk9YaFZuTm33F+csso=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ULNdEqeZJgtmNOhN/Y9INzsE9AnxWYwOMn+pIbRXIFs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "R4oz9+wkdjpKe5tE1jpG7IURAnfvS5fLP4LrD5cZfTE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qG5Z7VhwSu/HT/YFTgDzyAAzJKq51xPw2HeEV5btYC4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OM/1DmIIZ5Qyhtq8TGkHTBEMVKjAnKRZMRXYtTG8ctc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2R5vZbljLXnDFA99YfGuRB7pAdPJVKsT25zLNMC0fUk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OMbavF2EmdAz1fHkLV3ctFEUDfriKhoT2gidwHZ9z1o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MWT4Zrw3/vVvTYMa1Is5Pjr3wEwnBfnEAPPUAHKQhNU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tBkRPfG9yxfKocQx5pAJX0oEHKPL0Tgtr+0UYe09InE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lqxpnDR/H0YgH7RcfKoNoaaRhe1SIazIeMbQ1fu9y3Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "utT1UdR22PWOTrOkZauztX613lAplV4eh/ejTRb7ZSk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "S+Y2yFyKi/a6FXhih4yGo29X8I8OT6/zwEoX6NMKT4o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QSjVppg29x6oS5yBg8OFjrFt0tuTpWCuKxfIy0k8YnE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "y3r6/Xsfvsl3HksXlVYkJgHUqpQGfICxg3x9f8Zw1qM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BSltHzEwDjFN4du9rDHAPvl22atlcTioEtt+gC5L1tk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0arGXjSN0006UnXbrWsGqhvBair569DeFDUME3Df3rA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s/DumaMad08S+PBUUcrS+v42K0z8HgcdiQtrFAEu2Qs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EzJ8Y8N0OQBTlnvrK82PdevDNZZO4E6CNgYVu8Cj6Ks=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VA4vr8jBPI5QdiPrULzzZjBMIUbG3V7Slg5zm0bFcKc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YAOvEB2ZLtq9LQiFViBHWaxxWVVonC2rNYj9tN9s3L0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hgaHMo9aAGS+nBwvqnTjZO+YkiQPY1c1XcIYeaYKHyI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YvaoLt3ZpH0atB0tNzwMjpoxRYJXl0DqSjisMJiGVBE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EMmW6CptFsiLoPOi5/uAJQ2FmeLg6mCpuVLLrRWk7Mc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1jQsNMarSnarlYmXEuoFokeBMg/090qUD9wqo1Zn8Gs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hupXNKhRpJxpyDAAP1TgJ5JMZh9lhbMk6s7D7dMS3C8=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Decimal-InsertFind.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-Decimal-InsertFind.yml new file mode 100644 index 00000000000..48d44264a84 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Decimal-InsertFind.yml @@ -0,0 +1,1666 @@ +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + # Tests for Decimal (without precision) must only run against a replica set. Decimal (without precision) queries are expected to take a long time and may exceed the default mongos timeout. + topology: [ "replicaset" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDecimal', 'bsonType': 'decimal', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "FLE2 Range Decimal. Insert and Find." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDecimal: { $numberDecimal: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDecimal: { $numberDecimal: "1" } } + - name: find + arguments: + filter: { encryptedDecimal: { $gt: { $numberDecimal: "0" } } } + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDecimal": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDecimal": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: + "encryptedDecimal": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimal": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rbf3AeBEv4wWFAKknqDxRW5cLNkFvbIs6iJjc6LShQY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0l86Ag5OszXpa78SlOUV3K9nff5iC1p0mRXtLg9M1s4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Hn6yuxFHodeyu7ISlhYrbSf9pTiH4TDEvbYLWjTwFO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zdf4y2etKBuIpkEU1zMwoCkCsdisfXZCh8QPamm+drY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rOQ9oMdiK5xxGH+jPzOvwVqdGGnF3+HkJXxn81s6hp4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "61aKKsE3+BJHHWYvs3xSIBvlRmKswmaOo5rygQJguUg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KuDb/GIzqDM8wv7m7m8AECiWJbae5EKKtJRugZx7kR0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Q+t8t2TmNUiCIorVr9F3AlVnX+Mpt2ZYvN+s8UGict8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJRZIpKxUgHyL83kW8cvfjkxN3z6WoNnUg+SQw+LK+k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnUsYjip8SvW0+m9mR5WWTkpK+p6uwJ6yBUAlBnFKMk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PArHlz+yPRYDycAP/PgnI/AkP8Wgmfg++Vf4UG1Bf0E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wnIh53Q3jeK8jEBe1n8kJLa89/H0BxO26ZU8SRIAs9Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4F8U59gzBLGhq58PEWQk2nch+R0Va7eTUoxMneReUIA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ihKagIW3uT1dm22ROr/g5QaCpxZVj2+Fs/YSdM2Noco=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EJtUOOwjkrPUi9mavYAi+Gom9Y2DuFll7aDwo4mq0M0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dIkr8dbaVRQFskAVT6B286BbcBBt1pZPEOcTZqk4ZcI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aYVAcZYkH/Tieoa1XOjE/zCy5AJcVTHjS0NG2QB7muA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sBidL6y8TenseetpioIAAtn0lK/7C8MoW4JXpVYi3z8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0Dd2klU/t4R86c2WJcJDAd57k/N7OjvYSO5Vf8KH8sw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I3jZ92WEVmZmgaIkLbuWhBxl7EM6bEjiEttgBJunArA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aGHoQMlgJoGvArjfIbc3nnkoc8SWBxcrN7hSmjMRzos=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "bpiWPnF/KVBQr5F6MEwc5ZZayzIRvQOLDAm4ntwOi8g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tI7QVKbE6avWgDD9h4QKyFlnTxFCwd2iLySKakxNR/I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XGsge0CnoaXgE3rcpKm8AEeku5QVfokS3kcI+JKV1lk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JQxlryW2Q5WOwfrjAnaZxDvC83Dg6sjRVP5zegf2WiM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YFuHKJOfoqp1iGVxoFjx7bLYgVdsN4GuUFxEgO9HJ5s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Z6vUdiCR18ylKomf08uxcQHeRtmyav7/Ecvzz4av3k4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SPGo1Ib5AiP/tSllL7Z5PAypvnKdwJLzt8imfIMSEJQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "m94Nh6PFFQFLIib9Cu5LAKavhXnagSHG6F5EF8lD96I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pfEkQI98mB+gm1+JbmVurPAODMFPJ4E8DnqfVyUWbSo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DNj3OVRLbr43s0vd+rgWghOL3FqeO/60npdojC8Ry/M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kAYIQrjHVu49W8FTxyxJeiLVRWWjC9fPcBn+Hx1F+Ss=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aCSO7UVOpoQvu/iridarxkxV1SVxU1i9HVSYXUAeXk4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Gh6hTP/yj1IKlXQ+Q69KTfMlGZjEcXoRLGbQHNFo/1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/gDgIFQ4tAlJk3GN48IS5Qa5IPmErwGk8CHxAbp6gs0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PICyimwPjxpusyKxNssOOwUotAUbygpyEtORsVGXT8g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4lu+cBHyAUvuxC6JUNyHLzHsCogGSWFFnUCkDwfQdgI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pSndkmoNUJwXjgkbkgOrT5f9nSvuoMEZOkwAN9ElRaE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tyW+D4i26QihNM5MuBM+wnt5AdWGSJaJ4X5ydc9iWTU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9Syjr8RoxUgPKr+O5rsCu07AvcebA4P8IVKyS1NVLWc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "67tPfDYnK2tmrioI51fOBG0ygajcV0pLo5+Zm/rEW7U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "y0EiPRxYTuS1eVTIaPQUQBBxwkyxNckbePvKgChwd0M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NWd+2veAaeXQgR3vCvzlI4R1WW67D5YsVLdoXfdb8qg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PY5RQqKQsL2GqBBSPNOEVpojNFRX/NijCghIpxD6CZk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lcvwTyEjFlssCJtdjRpdN6oY+C7bxZY+WA+QAqzj9zg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWE7XRNylvTwO/9Fv56dNqUaQWMmESNS/GNIwgBaEI0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ijwlrUeS8nRYqK1F8kiCYF0mNDolEZS+/lJO1Lg93C8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8KzV+qYGYuIjoNj8eEpnTuHrMYuhzphl80rS6wrODuU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wDyTLjSEFF895hSQsHvmoEQVS6KIkZOtq1c9dVogm9I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SGrtPuMYCjUrfKF0Pq/thdaQzmGBMUvlwN3ORIu9tHU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KySHON3hIoUk4xWcwTqk6IL0kgjzjxgMBObVIkCGvk4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hBIdS9j0XJPeT4ot73ngELkpUoSixvRBvdOL9z48jY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Tx6um0q9HjS5ZvlFhvukpI6ORnyrXMWVW1OoxvgqII0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zFKlyfX5H81+d4A4J3FKn4T5JfG+OWtR06ddyX4Mxas=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cGgCDuPV7MeMMYEDpgOupqyNP4BQ4H7rBnd2QygumgM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IPaUoy98v11EoglTpJ4kBlEawoZ8y7BPwzjLYBpkvHQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Pfo4Am6tOWAyZNn8G9W5HWWGC3ZWmX0igI/RRB870Ro=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fnTSjd7bC1Udoq6iM7UDnHAC/lsIXSHp/Gy332qw+/I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fApBgVRrTDyEumkeWs5p3ag9KB48SbU4Si0dl7Ns9rc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QxudfBItgoCnUj5NXVnSmWH3HK76YtKkMmzn4lyyUYY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sSOvwhKa29Wq94bZ5jGIiJQGbG1uBrKSBfOYBz/oZeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FdaMgwwJ0NKsqmPZLC5oE+/0D74Dfpvig3LaI5yW5Fs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sRWBy12IERN43BSZIrnBfC9+zFBUdvjTlkqIH81NGt4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/4tIRpxKhoOwnXAiFn1Z7Xmric4USOIfKvTYQXk3QTc=", + "subType": "00" + } + } + ] + } + - + { + "_id": { + "$numberInt": "1" + }, + "encryptedDecimal": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RGTjNVEsNJb+DG7DpPOam8rQWD5HZAMpRyiTQaw7tk8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I93Md7QNPGmEEGYU1+VVCqBPBEvXdqHPtTJtMOn06Yk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "GecBFQ1PemlECWZWCl7f74vmsL6eB6mzQ9n6tK6FYfs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QpjhZl+O1ORifgtCZuWAdcP6OKL7IZ2cA46v8FJcV28=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RlQWwhU+uVv0a+9IB5cUkEfvHBvOw3B1Sx6WfPWMqes=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ubb81XTC7U+4tcNzf1oYvOY6gR5hC2Izqx54f4GuJ0E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6M4Q5NMQ9TqNnjzGOxIkiUIY8TEL0I3XD1QnhefQUqU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BtInzk9t2FFMCEY6AQ7zN8jwrrZEs2irSv6q0Q4NaIw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6vxXfETu9cuBIpRBo3jUUU04mJIH/aAhLX8K6VI5Xv0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wXPCdS+q23zi1bkPnaVG2j0PsVtxdeSLJ//h6J1x8RU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KY3KkfBAsN2l80wbpj41G0gwBR5KmmFnZcagg7D3ENk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tI8NFAxXCX4VOnY5X73K6KI/Yspd3aR94KV39MhJlAw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "nFxH0UC3mATKA6Vboz+QX/hAjj19kF/SH6H5Cne7qC0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q8hYqIYaIi7nOdG/7qQZYnz8Bsacfi66M1nVku4SH08=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4saA92R4arp4anvD9xFtze+sNcQqTEhPHyl1h70A8NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DbIziOBRRyeQS6RtBR09E37LV+CTKrEjGoRMLSpG6eE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Fv80Plp/7w2gnVqrwawLd6qhJ10G4NCDm3re67cNq4Y=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "T/T2oiQCBBES4YN7EodzPRdabZSFlYIClHBym+bQUZE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ZQgHD3l46Ujqtbnj1VbbeM29C9wJzOhz+yZ/7XdSrxk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ltlFKzWvyZvHxDFOYDd/XXJ6kUiJj0ln2HTCEz2o4Z4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "flW8A7bltC1u8bzx0WJtxosGJdOVsJFfbx33jxnpFGg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SXO+92QbMKwUSG2t27ciunV1c3VvFkUuDmSczpRe008=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+KioGs1GM+xRBzFE67ePTWj04KMSE5/Y6qUF7nJ5kvU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L3xNVbh6YH+RzqABN+5Jgb7T234Efpn766DmUvxIxgg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hPF+60mBYPjh21dEmPlBhKgyc9S2qLtTkypYvnqP2Fc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EletRsETy2HcjaPIm2c8CkT7ch/P3pJJDC8hasepcSU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "r5bMXUaNKqLPxZ+TG9HYTG4aSDgcpim27rN8rQFkM0w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0Q7Erdr8+/S0wUEDDIqlS5XjBVWvhZY65K0uUDb6+Ns=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xEcnhXy35hbXNVBPOOt3TUHbxvKfQ48KjA9b6/rbMqQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "T8bEpiQNgsEudXvyKE9SZlSvbpV/LUaslsdqgSFltyo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hIoiaF2YjnxDbODfhFEB+JGZ5nf8suD3Shck5bwQ3N0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qnA6qzejeRJ0rsZaZ0zOvKAaXyxt5lpscKQNYFZNl4k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "anAKCL2DN/le2VaP0n2ucYSEH/DaaEH/8Sa4OqTZsRA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JCZlBJaFm618oWYSnT9Jr1MtwFVw4BZjOzO+5yWgR90=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yxyk4n9762WzcDVGnTn4jCqUnSMIVCrLDIjCX1QVj34=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fDI6fdKvDJwim5/CQwWZEzcrXE3LHgy7FTtffcC7tXE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Vex+gcz5T+WkzsVZQrkqUR2ryyZbnaOGuWpYvjN0zCw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8TLEXz+Gbbp6llHpZXVjLsdlYY9f6hrKpHVpyfDe0RY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7fTyt5BrunypS65TfOzFW2E2qdIuT4SLeDeGlbQoJCs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8fKGrkqN0/KuSjyXgDBmRauDKrSa//JBKRWHEB9xBf4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s4codmG7uN4ss6P357jL21lazEe90M9GOK5WrOknSV0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RkSpua8XF+NUdxVDU90EbLUTTyZFX3tt3atBTroFaRk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "LnTCuCDyAHK5B9KXzjtwGmWB+qergQk2OCjnIx9MI2A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cBFh0virAX4pVXf/udIGI2951i0+0aZAdJcBVGtYnT4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "G54X6myQXWZ5fw/G31en3QbdgfXzL9+hFTtJpnWMqDI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EdsiiuezcsFJFnYIyGjCOhnqMj1BOwTB5EFxN+ERUkg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dVH9MXLtk0WTwGQ3xmrhOqfropMUkDW3o6paNPGl3NU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sB3HqXKWY3pKbuEH8BTbfNIGfbY+7/ZbOc3XC+JRNNI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WHyDk62Xhqbo4/iie2aLIM4x2uuAjv6102dJSHI58oM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pNUFuHpeNRDUZ/NrtII2c6sNc9eGR1lIUlIyXKERA+0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UPa+pdCqnN0bfAptdzldQOSd01gidrDKy8KhWrpSKAI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "l+7dOAlo+HUffMqFYXL6pgUFeTbwOM9CjKQLxEoLtc4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SRnDXV/rN6C8xwMutv9E1luv3DOUio3VkgPr8Cpm7Ew=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QcH6gl+gX7xZ7OWhUNQMbndJy0Piz49pDo6RsnLkVSA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "t+uL4DnfsI/Zll/KXWW1cOKX3Hu8WIkm3pt9efCVSAQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "myutHDctku/+Uug/nD8gRbYvmx/IovtoAAC2/fz2oHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6C+cjD0e0nSCP6cPqQYbNG7SlOd6Mfvi8hyfm7Ng+D8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zg01JSoOj9oBKT0S1ldJucXzY5AKgreS+h2xJreWTOs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7qQ80/FjodHl1m1py/Oii0/9C/xWbLdhaRXQ+kkCP10=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YwWMNH07vL6c5Nhg+MRnVByhzUunu8y0VLM9z/XvR5U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Dle8bU98+fudAbc14SToZFkwvV3tcYVsjDug0NWljpc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "J+eKL1vPJmlzltvhI6Li5Fz/TJmi3Ng+ehRTcs46API=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zB3XzfFygLwC3WHkj0up+VbEd25KKoce1vOpG/5bwK4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vnVnmOnL+z2pqwE+A6cVKS0Iwy4F4/2IiElJca9bUQM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+lG5r/Fpqry3BtFuvY67+RntmHAMDoLVOSGc6ZoXPb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L5MXQertqc6uj7ADe8aWKbd1sYHPCE7P1VYVg9Zc3VI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "imKONuZgopt0bhM3GMX2WVPwQYMTobuUUEdhcLfHs4c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "eOkU1J1uVbiVFWBerbXsSIVcF2nqiicTkFy4x7kFHB8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gI0uDhXeoH/UatDQKEf4qo8FHzWZDhb/wuWTqbq/ID4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cOkd5Aa3btYhtojE/smsF/PJnULqQ4NNqTkU6KXTFmo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "AWNJMs1MTe294oFipp8Y6P0CjpkZ4qCZoClQF3XcHq8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6gJtlzXOFhGYrVbTuRMmvMlDTwXdNtR9aGBlHZPwIMw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "LEmwVGA/xsEG7UrcOoYLFu6KCXgijzFznenknuDacm8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "mIRFPTXRrGaPtp/Ydij2jgkRe4uoUvAKxW2d8b9zYL0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "B+Uv2u48WALOO0L311z+eryjYQzKJVMfdHMZPhOAFmY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "INXXp0wDyVCq+NtfIrrC2ciETmyW/dWB/48/u4yLEZ4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "se7DGo8XrlrQDLEcco1tZrQt9kDe+0RTyl2bw/quG4w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vr0m2+Zk9lbN6UgWCyn8xJWJOokU3IDYab5U5q1+CgQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XI+eJ8Gy2JktG1gICgoj1qpsfy1tKmH0kglWbaQH6DA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A+UCuNnuAUqnQzspA6TVqUPRmtZmpSex5HFw7THRxs0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xaH2Ehfljd19uo0Fvb3iwkdaiWEVQd2YPoitgEPkhSM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "S/iZBJGcc8+qZxyMtab65MMBoSglybwk3x58Nb86gnY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "w14ZE5qqY5YgkS4Zcs9YNbrQbY1XfGOOHNn9bOYnFVQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0MhGd/jEF1vjkKGp+ZMn9SjLK54jkp9W4Hg+Sp/oxaI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "92QZ73e/NRTYgCm4aifaKth6aAsKnLLccBc0zx/qUTY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WOjzemCgFJOiGIp81RSVh/tFlzSTj9eFWcBnsiv2Ycs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DrsP9CmfKPjw5yLL8bnSeAxfNzAwlb+Z8OqCiKgBY7o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lMogqg8veBv6mri3/drMe9afJiKMvevkmGcw9BedfLo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "TxqwNcY8Tg2MPpNdkPBwvfpuTttSYRHU26DGECKYQ9o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "l0u1b4b4vYACWIwfnB7PZac4oDEgjQZCzHruNPTgAIY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "iVSGQ+cCfhbWIrY/v/WBORK92elu9gfRKyGhr6r/k00=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yK1forG50diEXte8ECzjfpHeYsPyuQ/dgxbxn/nzY5k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gIfTLCD3VwnOwkC0zPXWTqaITxX6ZplA69PO2a6zolc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "O/Zxlgh3WqpzJ7+Sd8XWMVID4/GXJUUWaSqfgDUi3b0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ZQ6yv368zwahUqSUYH/StL0Qgz/TwS1CzlMjVDvCciI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "m2rPEYkjwyiKdonMrKlcF7hya4lFOAUwEePJ3SgrNx8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Mq0yl5iVKlq71bT/dT/fXOWf2n90bTnXFnOdGDN0JOc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6qDGMXipPLC2O6EAAMjO2F9xx4rdqZso4IkPpH2304U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jvQHRQQa2RIszE2LX2Hv2LbRhYawJ6qmtRt8HZzFQXg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ovJXQrkZlpeHRciKyE/WWNm5O389gRgzx1W+Dw596X4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "a4kgRNvYctGYqyQv9qScL/WkljTYVylJ9pE9KDULlxU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qV4Q48vPiCJMTjljotzYKI/zfExWpkKOSHGcAjGyDig=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jtI7zbBF+QW/aYYTkn90zzyHLXLgmy7l1bzgMb2oqic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q0KmJl9txPdn962UNvnfe6UFhdk9YaFZuTm33F+csso=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ULNdEqeZJgtmNOhN/Y9INzsE9AnxWYwOMn+pIbRXIFs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "R4oz9+wkdjpKe5tE1jpG7IURAnfvS5fLP4LrD5cZfTE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qG5Z7VhwSu/HT/YFTgDzyAAzJKq51xPw2HeEV5btYC4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OM/1DmIIZ5Qyhtq8TGkHTBEMVKjAnKRZMRXYtTG8ctc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2R5vZbljLXnDFA99YfGuRB7pAdPJVKsT25zLNMC0fUk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OMbavF2EmdAz1fHkLV3ctFEUDfriKhoT2gidwHZ9z1o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MWT4Zrw3/vVvTYMa1Is5Pjr3wEwnBfnEAPPUAHKQhNU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tBkRPfG9yxfKocQx5pAJX0oEHKPL0Tgtr+0UYe09InE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lqxpnDR/H0YgH7RcfKoNoaaRhe1SIazIeMbQ1fu9y3Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "utT1UdR22PWOTrOkZauztX613lAplV4eh/ejTRb7ZSk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "S+Y2yFyKi/a6FXhih4yGo29X8I8OT6/zwEoX6NMKT4o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QSjVppg29x6oS5yBg8OFjrFt0tuTpWCuKxfIy0k8YnE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "y3r6/Xsfvsl3HksXlVYkJgHUqpQGfICxg3x9f8Zw1qM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BSltHzEwDjFN4du9rDHAPvl22atlcTioEtt+gC5L1tk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0arGXjSN0006UnXbrWsGqhvBair569DeFDUME3Df3rA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s/DumaMad08S+PBUUcrS+v42K0z8HgcdiQtrFAEu2Qs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EzJ8Y8N0OQBTlnvrK82PdevDNZZO4E6CNgYVu8Cj6Ks=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VA4vr8jBPI5QdiPrULzzZjBMIUbG3V7Slg5zm0bFcKc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YAOvEB2ZLtq9LQiFViBHWaxxWVVonC2rNYj9tN9s3L0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hgaHMo9aAGS+nBwvqnTjZO+YkiQPY1c1XcIYeaYKHyI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YvaoLt3ZpH0atB0tNzwMjpoxRYJXl0DqSjisMJiGVBE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EMmW6CptFsiLoPOi5/uAJQ2FmeLg6mCpuVLLrRWk7Mc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1jQsNMarSnarlYmXEuoFokeBMg/090qUD9wqo1Zn8Gs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hupXNKhRpJxpyDAAP1TgJ5JMZh9lhbMk6s7D7dMS3C8=", + "subType": "00" + } + } + ] + } \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Decimal-Update.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-Decimal-Update.json new file mode 100644 index 00000000000..2f8b991cf72 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Decimal-Update.json @@ -0,0 +1,1934 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimal", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Decimal. Update.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimal": { + "$numberDecimal": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimal": { + "$numberDecimal": "1" + } + } + } + }, + { + "name": "updateOne", + "arguments": { + "filter": { + "encryptedDecimal": { + "$gt": { + "$numberDecimal": "0" + } + } + }, + "update": { + "$set": { + "encryptedDecimal": { + "$numberDecimal": "2" + } + } + } + }, + "result": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "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": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDecimal": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimal", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDecimal": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimal", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command_name": "update", + "command": { + "update": "default", + "ordered": true, + "updates": [ + { + "q": { + "encryptedDecimal": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encryptedDecimal": { + "$$type": "binData" + } + } + } + } + ], + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimal", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + }, + "deleteTokens": { + "default.default": { + "encryptedDecimal": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + }, + "$db": "default" + } + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimal": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rbf3AeBEv4wWFAKknqDxRW5cLNkFvbIs6iJjc6LShQY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0l86Ag5OszXpa78SlOUV3K9nff5iC1p0mRXtLg9M1s4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Hn6yuxFHodeyu7ISlhYrbSf9pTiH4TDEvbYLWjTwFO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zdf4y2etKBuIpkEU1zMwoCkCsdisfXZCh8QPamm+drY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rOQ9oMdiK5xxGH+jPzOvwVqdGGnF3+HkJXxn81s6hp4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "61aKKsE3+BJHHWYvs3xSIBvlRmKswmaOo5rygQJguUg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KuDb/GIzqDM8wv7m7m8AECiWJbae5EKKtJRugZx7kR0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Q+t8t2TmNUiCIorVr9F3AlVnX+Mpt2ZYvN+s8UGict8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJRZIpKxUgHyL83kW8cvfjkxN3z6WoNnUg+SQw+LK+k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnUsYjip8SvW0+m9mR5WWTkpK+p6uwJ6yBUAlBnFKMk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PArHlz+yPRYDycAP/PgnI/AkP8Wgmfg++Vf4UG1Bf0E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wnIh53Q3jeK8jEBe1n8kJLa89/H0BxO26ZU8SRIAs9Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4F8U59gzBLGhq58PEWQk2nch+R0Va7eTUoxMneReUIA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ihKagIW3uT1dm22ROr/g5QaCpxZVj2+Fs/YSdM2Noco=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EJtUOOwjkrPUi9mavYAi+Gom9Y2DuFll7aDwo4mq0M0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dIkr8dbaVRQFskAVT6B286BbcBBt1pZPEOcTZqk4ZcI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aYVAcZYkH/Tieoa1XOjE/zCy5AJcVTHjS0NG2QB7muA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sBidL6y8TenseetpioIAAtn0lK/7C8MoW4JXpVYi3z8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0Dd2klU/t4R86c2WJcJDAd57k/N7OjvYSO5Vf8KH8sw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I3jZ92WEVmZmgaIkLbuWhBxl7EM6bEjiEttgBJunArA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aGHoQMlgJoGvArjfIbc3nnkoc8SWBxcrN7hSmjMRzos=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "bpiWPnF/KVBQr5F6MEwc5ZZayzIRvQOLDAm4ntwOi8g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tI7QVKbE6avWgDD9h4QKyFlnTxFCwd2iLySKakxNR/I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XGsge0CnoaXgE3rcpKm8AEeku5QVfokS3kcI+JKV1lk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JQxlryW2Q5WOwfrjAnaZxDvC83Dg6sjRVP5zegf2WiM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YFuHKJOfoqp1iGVxoFjx7bLYgVdsN4GuUFxEgO9HJ5s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Z6vUdiCR18ylKomf08uxcQHeRtmyav7/Ecvzz4av3k4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SPGo1Ib5AiP/tSllL7Z5PAypvnKdwJLzt8imfIMSEJQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "m94Nh6PFFQFLIib9Cu5LAKavhXnagSHG6F5EF8lD96I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pfEkQI98mB+gm1+JbmVurPAODMFPJ4E8DnqfVyUWbSo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DNj3OVRLbr43s0vd+rgWghOL3FqeO/60npdojC8Ry/M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kAYIQrjHVu49W8FTxyxJeiLVRWWjC9fPcBn+Hx1F+Ss=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aCSO7UVOpoQvu/iridarxkxV1SVxU1i9HVSYXUAeXk4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Gh6hTP/yj1IKlXQ+Q69KTfMlGZjEcXoRLGbQHNFo/1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/gDgIFQ4tAlJk3GN48IS5Qa5IPmErwGk8CHxAbp6gs0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PICyimwPjxpusyKxNssOOwUotAUbygpyEtORsVGXT8g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4lu+cBHyAUvuxC6JUNyHLzHsCogGSWFFnUCkDwfQdgI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pSndkmoNUJwXjgkbkgOrT5f9nSvuoMEZOkwAN9ElRaE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tyW+D4i26QihNM5MuBM+wnt5AdWGSJaJ4X5ydc9iWTU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9Syjr8RoxUgPKr+O5rsCu07AvcebA4P8IVKyS1NVLWc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "67tPfDYnK2tmrioI51fOBG0ygajcV0pLo5+Zm/rEW7U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "y0EiPRxYTuS1eVTIaPQUQBBxwkyxNckbePvKgChwd0M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NWd+2veAaeXQgR3vCvzlI4R1WW67D5YsVLdoXfdb8qg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PY5RQqKQsL2GqBBSPNOEVpojNFRX/NijCghIpxD6CZk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lcvwTyEjFlssCJtdjRpdN6oY+C7bxZY+WA+QAqzj9zg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWE7XRNylvTwO/9Fv56dNqUaQWMmESNS/GNIwgBaEI0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ijwlrUeS8nRYqK1F8kiCYF0mNDolEZS+/lJO1Lg93C8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8KzV+qYGYuIjoNj8eEpnTuHrMYuhzphl80rS6wrODuU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wDyTLjSEFF895hSQsHvmoEQVS6KIkZOtq1c9dVogm9I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SGrtPuMYCjUrfKF0Pq/thdaQzmGBMUvlwN3ORIu9tHU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KySHON3hIoUk4xWcwTqk6IL0kgjzjxgMBObVIkCGvk4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hBIdS9j0XJPeT4ot73ngELkpUoSixvRBvdOL9z48jY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Tx6um0q9HjS5ZvlFhvukpI6ORnyrXMWVW1OoxvgqII0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zFKlyfX5H81+d4A4J3FKn4T5JfG+OWtR06ddyX4Mxas=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cGgCDuPV7MeMMYEDpgOupqyNP4BQ4H7rBnd2QygumgM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IPaUoy98v11EoglTpJ4kBlEawoZ8y7BPwzjLYBpkvHQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Pfo4Am6tOWAyZNn8G9W5HWWGC3ZWmX0igI/RRB870Ro=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fnTSjd7bC1Udoq6iM7UDnHAC/lsIXSHp/Gy332qw+/I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fApBgVRrTDyEumkeWs5p3ag9KB48SbU4Si0dl7Ns9rc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QxudfBItgoCnUj5NXVnSmWH3HK76YtKkMmzn4lyyUYY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sSOvwhKa29Wq94bZ5jGIiJQGbG1uBrKSBfOYBz/oZeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FdaMgwwJ0NKsqmPZLC5oE+/0D74Dfpvig3LaI5yW5Fs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sRWBy12IERN43BSZIrnBfC9+zFBUdvjTlkqIH81NGt4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/4tIRpxKhoOwnXAiFn1Z7Xmric4USOIfKvTYQXk3QTc=", + "subType": "00" + } + } + ] + }, + { + "_id": { + "$numberInt": "1" + }, + "encryptedDecimal": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Mr/laWHUijZT5VT3x2a7crb7wgd/UXOGz8jr8BVqBpM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wXVD/HSbBljko0jJcaxJ1nrzs2+pchLQqYR3vywS8SU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VDCpBYsJIxTfcI6Zgf7FTmKMxUffQv+Ys8zt5dlK76I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zYDslUwOUVNwTYkETfjceH/PU3bac9X3UuQyYJ19qK0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rAOmHSz18Jx107xpbv9fYcPOmh/KPAqge0PAtuhIRnc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BFOB1OGVUen7VsOuS0g8Ti7oDsTt2Yj/k/7ta8YAdGM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2fckE5SPs0GU+akDkUEM6mm0EtcV3WDE/sQsnTtodlk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "mi9+aNjuwIvaMpSHENvKzKRAmX9cYguo2mXLvOoftHQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "K6TWn4VcWWkz/gkUkLmbtwkG7SNeABICmLDnoYJFlLU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Z+2/cEtGU0Fq7QJFNGA/0y4aWAsw0ncG6X0LYRqwS3c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rrSIf+lgcNZFbbUkS9BmE045jRWBpcBJXHzfMVEFuzE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KlHL3Kyje1/LMIfgbCqw1SolxffJvvgsYBV5y77wxuA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hzJ1YBoETmYeCh352dBmG8d8Wse/bUcqojTWpWQlgsc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lSdcllDXx8MA+s0GULjDA1lQkcV0L8/aHtZ6dM2pZ2c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "HGr7JLTTA7ksAnlmjSIwwdBVvgr3fv46/FTdiCPYpos=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "mMr25v1VwOEVZ8xaNUTHJCcsYqV+kwK6RzGYilxPtJ4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "129hJbziPJzNo0IoTU3bECdge0FtaPW8dm4dyNVNwYU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "doiLJ96qoo+v7NqIAZLq6BI5axV8Id8gT5vyJ1ZZ0PM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cW/Lcul3xYmfyvI/0x/+ybN78aQmBK1XIGs1EEU09N8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1aVIwzu9N5EJV9yEES+/g6hOTH7cA2NTcLIc59cu0wU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kw5tyl7Ew0r1wFyrN1mB9FiVW2hK2BxxxUuJDNWjyjQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ADAY2YBrm6RJBDY/eLLcfNxmSJku+mefz74gH66oyco=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8gkqB1LojzPrstpFG7RHYmWxXpIlPDTqWnNsXH7XDRU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "TESfVQMDQjfTZmHmUeYUE2XrokJ6CcrsKx/GmypGjOw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qFM+HFVQ539S0Ouynd1fBHoemFxtU9PRxE5+Dq7Ljy4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jPiFgUZteSmOg4wf3bsEKCZzcnxmMoILsgp/GaZD+dM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YaWUgJhYgPNN7TkFK16H8SsQS226JguaVhOIQxZwQNQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x90/Qk3AgyaFsvWf2KUCu5XF3j76WFSjt/GrnG01060=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ZGWybWL/xlEdMYRFCZDUoz10sywTf7U/7wufsb78lH0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8l4ganN66jIcdxfHAdYLaym/mdzUUQ8TViw3MDRySPc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c8p5XEGTqxqvRGVlR+nkxw9uUdoqDqTB0jlYQ361qMA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1ZGFLlpQBcU3zIUg8MmgWwFKVz/SaA7eSYFrfe3Hb70=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "34529174M77rHr3Ftn9r8jU4a5ztYtyVhMn1wryZSkU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YkQ4pxFWzc49MS0vZM6S8mNo4wAwo21rePBeF3C+9mI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MhOf4mYY00KKVhptOcXf0bXB7WfuuM801MRJg4vXPgc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7pbbD8ihNIYIBJ3tAUPGzHpFPpIeCTAk5L88qCB0/9w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "C9Q5PoNJTQo6pmNzXEEXUEqH22//UUWY1gqILcIywec=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "AqGVk1QjDNDLYWGRBX/nv9QdGR2SEgXZEhF0EWBAiSE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/sGI3VCbJUKATULJmhTayPOeVW+5MjWSvVCqS77sRbU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yOtbL0ih7gsuoxVtRrACMz+4N5uo7jIR7zzmtih2Beo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uA6dkb2Iyg9Su8UNDvZzkPx33kPZtWr/CCuEY+XgzUM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1DoSFPdHIplqZk+DyWAmEPckWwXw/GdB25NLmzeEZhk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OfDVS0T3ZuIXI/LNbTp6C9UbPIWLKiMy6Wx+9tqNl+g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "3PZjHXbmG6GtPz+iapKtQ3yY4PoFFgjIy+fV2xQv1YU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kaoLN0BoBWsmqE7kKkJQejATmLShd8qffcAmlhsxsGY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vpiw9KgQdegGmp7IJnSGX2miujRLU0xzs0ITTqbPW7c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NuXFf7xGUefYjIUTuMxNUTCfVHrF8oL0AT7dPv5Plk4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8Tz53LxtfEBJ9eR+d2690kwNsqPV6XyKo2PlqZCbUrc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "e6zsOmHSyV8tyQtSX6BSwui6wK9v1xG3giY/IILJQ2w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2fedFMCxa2DzmIpfbDKGXhQg0PPwbUv6vIWdwwlvhms=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yEJKMFnWXTC8tJUfzCInzQRByNEPjHxpw4L4m8No91Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YbFuWwOiFuQyOzIJXDbOkCWC2DyrG+248TBuVCa1pXU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "w7IkwGdrguwDrar5+w0Z3va5wXyZ4VXJkDMISyRjPGo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YmJUoILTRJPhyIyWyXJTsQ6KSZHHbEpwPVup6Ldm/Ko=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FvMjcwVZJmfh6FP/yBg2wgskK+KHD8YVUY6WtrE8xbg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "h4HCtD4HyYz0nci49IVAa10Z4NJD/FHnRMV4sRX6qro=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "nC7BpXCmym+a0Is2kReM9cYN2M1Eh5rVo8fjms14Oiw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1qtVWaeVo649ZZZtN8gXbwLgMWGLhz8beODbvru0I7Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Ej+mC0QFyMNIiSjR939S+iGBm7dm+1xObu5IcF/OpbU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UQ8LbUG3cMegbr9yKfKanAPQE1EfPkFciVDrNqZ5GHY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4iI3mXIDjnX+ralk1HhJY43mZx2uTJM7hsv9MQzTX7E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0WQCcs3rvsasgohERHHCaBM4Iy6yomS4qJ5To3/yYiw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qDCTVPoue1/DOAGNAlUstdA9Sid8MgEY4e5EzHcVHRk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9F9Mus0UnlzHb8E8ImxgXtz6SU98YXD0JqswOKw/Bzs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pctHpHKVBBcsahQ6TNh6/1V1ZrqOtKSAPtATV6BJqh0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vfR3C/4cPkVdxtNaqtF/v635ONbhTf5WbwJM6s4EXNE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ejP43xUBIex6szDcqExAFpx1IE/Ksi5ywJ84GKDFRrs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jbP4AWYd3S2f3ejmMG7dS5IbrFol48UUoT+ve3JLN6U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CiDifI7958sUjNqJUBQULeyF7x0Up3loPWvYKw9uAuw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "e2dQFsiHqd2BFHNhlSxocjd+cPs4wkcUW/CnCz4KNuM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PJFckVmzBipqaEqsuP2mkjhJE4qhw36NhfQ9DcOHyEU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "S3MeuJhET/B8VcfZYDR9fvX0nscDj416jdDekhmK11s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CGVHZRXpuNtQviDB2Kj03Q8uvs4w3RwTgV847R7GwPw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yUGgmgyLrxbEpDVy89XN3c2cmFpZXWWmuJ/35zVZ+Jw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "inb6Q97mL1a9onfNTT8v9wsoi/fz7KXKq3p8j90AU9c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CCyYx/4npq9xGO1lsCo8ZJhFO9/tN7DB+/DTE778rYg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "LNnYw4fwbiAZu0kBdAHPEm/OFnreS+oArdB5O/l/I98=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "P006SxmUS/RjiQJVYPdMFnNo3827GIEmSzagggkg05Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "oyvwY+WsnYV6UHuPki1o0ILJ2jN4uyXf9yaUNtZJyBA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "36Lk3RHWh1wmtCWC/Yj6jNIo17U5y6SofAgQjzjVxD8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vOOo8FqeHnuO9mqOYjIb4vgwIwVyXZ5Y+bY5d9tGFUM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "bJiDJjwQRNxqxlGjRm5lLziFhcfTDCnQ/qU1V85qcRg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2Qgrm1n0wUELAQnpkEiIHB856yv76q8jLbpiucetcm0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "5ciPOYxTK0WDwwYyfs7yiVymwtYQXDELLxmM4JLl4/o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "31dC2WUSIOKQc4jwT6PikfeYTwi80mTlh7P31T5KNQU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YluTV2Mu53EGCKLcWfHZb0BM/IPW2xJdG3vYlDMEsM4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dh/8lGo2Ek6KukSwutH6Q35iy8TgV0FN0SJqe0ZVHN8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EVw6HpIs3BKen2qY2gz4y5dw1JpXilfh07msZfQqJpc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FYolLla9L8EZMROEdWetozroU40Dnmwwx2jIMrr7c1A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8M6k4QIutSIj6CM41vvkQtuFsaGrjoR9SZJVSLbfGKQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9LM0VoddDNHway442MqY+Z7vohB2UHau/cddshhzf40=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "66i8Ytco4Yq/FMl6pIRZazz3CZlu8fO2OI6Pne0pvHU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2a/HgX+MjZxjXtSvHgF1yEpHMJBkl8Caee8XrJtn0WM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "frhBM662c4ZVG7mWP8K/HhRjd01lydW/cPcHnDjifqc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6k1T7Q1t668PBqv6fwpVnT1HWh7Am5LtbKvwPJKcpGU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UlJ5Edfusp8S/Pyhw6KTglIejmbr1HO0zUeHn/qFETA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jsxsB+1ECB3assUdoC333do9tYH+LglHmVSJHy4N8Hg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2nzIQxGYF7j3bGsIesECEOqhObKs/9ywknPHeJ3yges=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xJYKtuWrX90JrJVoYtnwP7Ce59XQGFYoalxpNfBXEH0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NLI5lriBTleGCELcHBtNnmnvwSRkHHaLOX4cKboMgTw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hUOQV0RmE5aJdJww1AR9rirJG4zOYPo+6cCkgn/BGvQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "h4G2Of76AgxcUziBwCyH+ayMOpdBWzg4yFrTfehSC2c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VuamM75RzGfQpj2/Y1jSVuQLrhy6OAwlZxjuQLB/9Ss=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kn9+hLq7hvw02xr9vrplOCDXKBTuFhfbX7d5v/l85Pg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fAiGqKyLZpGngBYFbtYUYt8LUrJ49vYafiboifTDjxs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BxRILymgfVJCczqjUIWXcfrfSgrrYkxTM5VTg0HkZLY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CrFY/PzfPU2zsFkGLu/dI6mEeizZzCR+uYgjZBAHro0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "AEbrIuwvXLTtYgMjOqnGQ8y8axUn5Ukrn7UZRSyfQVw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ouWeVH3PEFg+dKWlXc6BmqirJOaVWjJbMzZbCsce4dA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+hd6xFB+EG+kVP7WH4uMd1CLaWMnt5xJRaY/Guuga9Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zmpGalfAOL3gmcUMJYcLYIRT/2VDO/1Dw4KdYZoNcng=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2PbHAoM/46J2UIZ/vyksKzmVVfxA7YUyIxWeL/N/vBk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7fD9x+zk5MVFesb59Klqiwwmve7P5ON/5COURXj5smE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tlrNQ4jaq051iaWonuv1sSrYhKkL1LtNZuHsvATha3s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fBodm28iClNpvlRyVq0dOdXQ08S7/N3aDwid+PdWvRo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "O+/nnRqT3Zv7yMMGug8GhKHaWy6u7BfRGtZoj0sdN1c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "5AZZ/RTMY4Photnm/cpXZr/HnFRi3eljacMsipkJLHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "oFVyo/kgoMxBIk2VE52ySSimeyU+Gr0EfCwapXnTpKA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Z8v59DfcnviA0mzvnUk+URVO0UuqAWvtarEgJva/n1c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "P64GOntZ+zBJEHkigoh9FSxSO+rJTqR20z5aiGQ9an4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xMbSuDPfWuO/Dm7wuVl06GnzG9uzTlJJX9vFy7boGlY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kXPB19mRClxdH2UsHwlttS6lLU2uHvzuZgZz7kC45jU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NDVjVYXAw4k0w4tFzvs7QDq39aaU3HQor4I2XMKKnCk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uKw/+ErVfpTO1dGUfd3T/eWfZW3nUxXCdBGdjvHtZ88=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "av0uxEzWkizYWm0QUM/MN1hLibnxPvCWJKwjOV4yVQY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ERwUC47dvgOBzIsEESMIioLYbFOxOe8PtJTnmDkKuHM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2gseKlG5Le12fS/vj4eaED4lturF16kAgJ1TpW3HxEE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7Cvg0Y3j/5i2F1TeXxlMmU7xwif5dCmwkZAOrVC5K2Y=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Decimal-Update.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-Decimal-Update.yml new file mode 100644 index 00000000000..99ed254d945 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Decimal-Update.yml @@ -0,0 +1,1705 @@ +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + # Tests for Decimal (without precision) must only run against a replica set. Decimal (without precision) queries are expected to take a long time and may exceed the default mongos timeout. + topology: [ "replicaset" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDecimal', 'bsonType': 'decimal', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "FLE2 Range Decimal. Update." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDecimal: { $numberDecimal: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDecimal: { $numberDecimal: "1" } } + - name: updateOne + arguments: + filter: { encryptedDecimal: { $gt: { $numberDecimal: "0" } } } + update: { "$set": { "encryptedDecimal": { $numberDecimal: "2" } }} + result: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDecimal": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDecimal": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command_name: update + command: + { + "update": "default", + "ordered": true, + "updates": [ + { + "q": { + "encryptedDecimal": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encryptedDecimal": { $$type: "binData" } + } + } + } + ], + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": *encrypted_fields + }, + "deleteTokens": { + "default.default": { + "encryptedDecimal": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + }, + "$db": "default" + } + + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimal": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rbf3AeBEv4wWFAKknqDxRW5cLNkFvbIs6iJjc6LShQY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0l86Ag5OszXpa78SlOUV3K9nff5iC1p0mRXtLg9M1s4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Hn6yuxFHodeyu7ISlhYrbSf9pTiH4TDEvbYLWjTwFO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zdf4y2etKBuIpkEU1zMwoCkCsdisfXZCh8QPamm+drY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rOQ9oMdiK5xxGH+jPzOvwVqdGGnF3+HkJXxn81s6hp4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "61aKKsE3+BJHHWYvs3xSIBvlRmKswmaOo5rygQJguUg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KuDb/GIzqDM8wv7m7m8AECiWJbae5EKKtJRugZx7kR0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Q+t8t2TmNUiCIorVr9F3AlVnX+Mpt2ZYvN+s8UGict8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJRZIpKxUgHyL83kW8cvfjkxN3z6WoNnUg+SQw+LK+k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnUsYjip8SvW0+m9mR5WWTkpK+p6uwJ6yBUAlBnFKMk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PArHlz+yPRYDycAP/PgnI/AkP8Wgmfg++Vf4UG1Bf0E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wnIh53Q3jeK8jEBe1n8kJLa89/H0BxO26ZU8SRIAs9Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4F8U59gzBLGhq58PEWQk2nch+R0Va7eTUoxMneReUIA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ihKagIW3uT1dm22ROr/g5QaCpxZVj2+Fs/YSdM2Noco=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EJtUOOwjkrPUi9mavYAi+Gom9Y2DuFll7aDwo4mq0M0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dIkr8dbaVRQFskAVT6B286BbcBBt1pZPEOcTZqk4ZcI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aYVAcZYkH/Tieoa1XOjE/zCy5AJcVTHjS0NG2QB7muA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sBidL6y8TenseetpioIAAtn0lK/7C8MoW4JXpVYi3z8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0Dd2klU/t4R86c2WJcJDAd57k/N7OjvYSO5Vf8KH8sw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I3jZ92WEVmZmgaIkLbuWhBxl7EM6bEjiEttgBJunArA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aGHoQMlgJoGvArjfIbc3nnkoc8SWBxcrN7hSmjMRzos=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "bpiWPnF/KVBQr5F6MEwc5ZZayzIRvQOLDAm4ntwOi8g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tI7QVKbE6avWgDD9h4QKyFlnTxFCwd2iLySKakxNR/I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XGsge0CnoaXgE3rcpKm8AEeku5QVfokS3kcI+JKV1lk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JQxlryW2Q5WOwfrjAnaZxDvC83Dg6sjRVP5zegf2WiM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YFuHKJOfoqp1iGVxoFjx7bLYgVdsN4GuUFxEgO9HJ5s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Z6vUdiCR18ylKomf08uxcQHeRtmyav7/Ecvzz4av3k4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SPGo1Ib5AiP/tSllL7Z5PAypvnKdwJLzt8imfIMSEJQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "m94Nh6PFFQFLIib9Cu5LAKavhXnagSHG6F5EF8lD96I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pfEkQI98mB+gm1+JbmVurPAODMFPJ4E8DnqfVyUWbSo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DNj3OVRLbr43s0vd+rgWghOL3FqeO/60npdojC8Ry/M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kAYIQrjHVu49W8FTxyxJeiLVRWWjC9fPcBn+Hx1F+Ss=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aCSO7UVOpoQvu/iridarxkxV1SVxU1i9HVSYXUAeXk4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Gh6hTP/yj1IKlXQ+Q69KTfMlGZjEcXoRLGbQHNFo/1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/gDgIFQ4tAlJk3GN48IS5Qa5IPmErwGk8CHxAbp6gs0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PICyimwPjxpusyKxNssOOwUotAUbygpyEtORsVGXT8g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4lu+cBHyAUvuxC6JUNyHLzHsCogGSWFFnUCkDwfQdgI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pSndkmoNUJwXjgkbkgOrT5f9nSvuoMEZOkwAN9ElRaE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tyW+D4i26QihNM5MuBM+wnt5AdWGSJaJ4X5ydc9iWTU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9Syjr8RoxUgPKr+O5rsCu07AvcebA4P8IVKyS1NVLWc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "67tPfDYnK2tmrioI51fOBG0ygajcV0pLo5+Zm/rEW7U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "y0EiPRxYTuS1eVTIaPQUQBBxwkyxNckbePvKgChwd0M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NWd+2veAaeXQgR3vCvzlI4R1WW67D5YsVLdoXfdb8qg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PY5RQqKQsL2GqBBSPNOEVpojNFRX/NijCghIpxD6CZk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lcvwTyEjFlssCJtdjRpdN6oY+C7bxZY+WA+QAqzj9zg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWE7XRNylvTwO/9Fv56dNqUaQWMmESNS/GNIwgBaEI0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ijwlrUeS8nRYqK1F8kiCYF0mNDolEZS+/lJO1Lg93C8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8KzV+qYGYuIjoNj8eEpnTuHrMYuhzphl80rS6wrODuU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wDyTLjSEFF895hSQsHvmoEQVS6KIkZOtq1c9dVogm9I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SGrtPuMYCjUrfKF0Pq/thdaQzmGBMUvlwN3ORIu9tHU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KySHON3hIoUk4xWcwTqk6IL0kgjzjxgMBObVIkCGvk4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hBIdS9j0XJPeT4ot73ngELkpUoSixvRBvdOL9z48jY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Tx6um0q9HjS5ZvlFhvukpI6ORnyrXMWVW1OoxvgqII0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zFKlyfX5H81+d4A4J3FKn4T5JfG+OWtR06ddyX4Mxas=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cGgCDuPV7MeMMYEDpgOupqyNP4BQ4H7rBnd2QygumgM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IPaUoy98v11EoglTpJ4kBlEawoZ8y7BPwzjLYBpkvHQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Pfo4Am6tOWAyZNn8G9W5HWWGC3ZWmX0igI/RRB870Ro=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fnTSjd7bC1Udoq6iM7UDnHAC/lsIXSHp/Gy332qw+/I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fApBgVRrTDyEumkeWs5p3ag9KB48SbU4Si0dl7Ns9rc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QxudfBItgoCnUj5NXVnSmWH3HK76YtKkMmzn4lyyUYY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sSOvwhKa29Wq94bZ5jGIiJQGbG1uBrKSBfOYBz/oZeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FdaMgwwJ0NKsqmPZLC5oE+/0D74Dfpvig3LaI5yW5Fs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sRWBy12IERN43BSZIrnBfC9+zFBUdvjTlkqIH81NGt4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/4tIRpxKhoOwnXAiFn1Z7Xmric4USOIfKvTYQXk3QTc=", + "subType": "00" + } + } + ] + } + - + { + "_id": { + "$numberInt": "1" + }, + "encryptedDecimal": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Mr/laWHUijZT5VT3x2a7crb7wgd/UXOGz8jr8BVqBpM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wXVD/HSbBljko0jJcaxJ1nrzs2+pchLQqYR3vywS8SU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VDCpBYsJIxTfcI6Zgf7FTmKMxUffQv+Ys8zt5dlK76I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zYDslUwOUVNwTYkETfjceH/PU3bac9X3UuQyYJ19qK0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rAOmHSz18Jx107xpbv9fYcPOmh/KPAqge0PAtuhIRnc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BFOB1OGVUen7VsOuS0g8Ti7oDsTt2Yj/k/7ta8YAdGM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2fckE5SPs0GU+akDkUEM6mm0EtcV3WDE/sQsnTtodlk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "mi9+aNjuwIvaMpSHENvKzKRAmX9cYguo2mXLvOoftHQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "K6TWn4VcWWkz/gkUkLmbtwkG7SNeABICmLDnoYJFlLU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Z+2/cEtGU0Fq7QJFNGA/0y4aWAsw0ncG6X0LYRqwS3c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rrSIf+lgcNZFbbUkS9BmE045jRWBpcBJXHzfMVEFuzE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KlHL3Kyje1/LMIfgbCqw1SolxffJvvgsYBV5y77wxuA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hzJ1YBoETmYeCh352dBmG8d8Wse/bUcqojTWpWQlgsc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lSdcllDXx8MA+s0GULjDA1lQkcV0L8/aHtZ6dM2pZ2c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "HGr7JLTTA7ksAnlmjSIwwdBVvgr3fv46/FTdiCPYpos=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "mMr25v1VwOEVZ8xaNUTHJCcsYqV+kwK6RzGYilxPtJ4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "129hJbziPJzNo0IoTU3bECdge0FtaPW8dm4dyNVNwYU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "doiLJ96qoo+v7NqIAZLq6BI5axV8Id8gT5vyJ1ZZ0PM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cW/Lcul3xYmfyvI/0x/+ybN78aQmBK1XIGs1EEU09N8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1aVIwzu9N5EJV9yEES+/g6hOTH7cA2NTcLIc59cu0wU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kw5tyl7Ew0r1wFyrN1mB9FiVW2hK2BxxxUuJDNWjyjQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ADAY2YBrm6RJBDY/eLLcfNxmSJku+mefz74gH66oyco=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8gkqB1LojzPrstpFG7RHYmWxXpIlPDTqWnNsXH7XDRU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "TESfVQMDQjfTZmHmUeYUE2XrokJ6CcrsKx/GmypGjOw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qFM+HFVQ539S0Ouynd1fBHoemFxtU9PRxE5+Dq7Ljy4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jPiFgUZteSmOg4wf3bsEKCZzcnxmMoILsgp/GaZD+dM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YaWUgJhYgPNN7TkFK16H8SsQS226JguaVhOIQxZwQNQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x90/Qk3AgyaFsvWf2KUCu5XF3j76WFSjt/GrnG01060=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ZGWybWL/xlEdMYRFCZDUoz10sywTf7U/7wufsb78lH0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8l4ganN66jIcdxfHAdYLaym/mdzUUQ8TViw3MDRySPc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c8p5XEGTqxqvRGVlR+nkxw9uUdoqDqTB0jlYQ361qMA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1ZGFLlpQBcU3zIUg8MmgWwFKVz/SaA7eSYFrfe3Hb70=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "34529174M77rHr3Ftn9r8jU4a5ztYtyVhMn1wryZSkU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YkQ4pxFWzc49MS0vZM6S8mNo4wAwo21rePBeF3C+9mI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MhOf4mYY00KKVhptOcXf0bXB7WfuuM801MRJg4vXPgc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7pbbD8ihNIYIBJ3tAUPGzHpFPpIeCTAk5L88qCB0/9w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "C9Q5PoNJTQo6pmNzXEEXUEqH22//UUWY1gqILcIywec=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "AqGVk1QjDNDLYWGRBX/nv9QdGR2SEgXZEhF0EWBAiSE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/sGI3VCbJUKATULJmhTayPOeVW+5MjWSvVCqS77sRbU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yOtbL0ih7gsuoxVtRrACMz+4N5uo7jIR7zzmtih2Beo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uA6dkb2Iyg9Su8UNDvZzkPx33kPZtWr/CCuEY+XgzUM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1DoSFPdHIplqZk+DyWAmEPckWwXw/GdB25NLmzeEZhk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OfDVS0T3ZuIXI/LNbTp6C9UbPIWLKiMy6Wx+9tqNl+g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "3PZjHXbmG6GtPz+iapKtQ3yY4PoFFgjIy+fV2xQv1YU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kaoLN0BoBWsmqE7kKkJQejATmLShd8qffcAmlhsxsGY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vpiw9KgQdegGmp7IJnSGX2miujRLU0xzs0ITTqbPW7c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NuXFf7xGUefYjIUTuMxNUTCfVHrF8oL0AT7dPv5Plk4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8Tz53LxtfEBJ9eR+d2690kwNsqPV6XyKo2PlqZCbUrc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "e6zsOmHSyV8tyQtSX6BSwui6wK9v1xG3giY/IILJQ2w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2fedFMCxa2DzmIpfbDKGXhQg0PPwbUv6vIWdwwlvhms=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yEJKMFnWXTC8tJUfzCInzQRByNEPjHxpw4L4m8No91Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YbFuWwOiFuQyOzIJXDbOkCWC2DyrG+248TBuVCa1pXU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "w7IkwGdrguwDrar5+w0Z3va5wXyZ4VXJkDMISyRjPGo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YmJUoILTRJPhyIyWyXJTsQ6KSZHHbEpwPVup6Ldm/Ko=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FvMjcwVZJmfh6FP/yBg2wgskK+KHD8YVUY6WtrE8xbg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "h4HCtD4HyYz0nci49IVAa10Z4NJD/FHnRMV4sRX6qro=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "nC7BpXCmym+a0Is2kReM9cYN2M1Eh5rVo8fjms14Oiw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1qtVWaeVo649ZZZtN8gXbwLgMWGLhz8beODbvru0I7Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Ej+mC0QFyMNIiSjR939S+iGBm7dm+1xObu5IcF/OpbU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UQ8LbUG3cMegbr9yKfKanAPQE1EfPkFciVDrNqZ5GHY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4iI3mXIDjnX+ralk1HhJY43mZx2uTJM7hsv9MQzTX7E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0WQCcs3rvsasgohERHHCaBM4Iy6yomS4qJ5To3/yYiw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qDCTVPoue1/DOAGNAlUstdA9Sid8MgEY4e5EzHcVHRk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9F9Mus0UnlzHb8E8ImxgXtz6SU98YXD0JqswOKw/Bzs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pctHpHKVBBcsahQ6TNh6/1V1ZrqOtKSAPtATV6BJqh0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vfR3C/4cPkVdxtNaqtF/v635ONbhTf5WbwJM6s4EXNE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ejP43xUBIex6szDcqExAFpx1IE/Ksi5ywJ84GKDFRrs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jbP4AWYd3S2f3ejmMG7dS5IbrFol48UUoT+ve3JLN6U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CiDifI7958sUjNqJUBQULeyF7x0Up3loPWvYKw9uAuw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "e2dQFsiHqd2BFHNhlSxocjd+cPs4wkcUW/CnCz4KNuM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PJFckVmzBipqaEqsuP2mkjhJE4qhw36NhfQ9DcOHyEU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "S3MeuJhET/B8VcfZYDR9fvX0nscDj416jdDekhmK11s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CGVHZRXpuNtQviDB2Kj03Q8uvs4w3RwTgV847R7GwPw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yUGgmgyLrxbEpDVy89XN3c2cmFpZXWWmuJ/35zVZ+Jw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "inb6Q97mL1a9onfNTT8v9wsoi/fz7KXKq3p8j90AU9c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CCyYx/4npq9xGO1lsCo8ZJhFO9/tN7DB+/DTE778rYg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "LNnYw4fwbiAZu0kBdAHPEm/OFnreS+oArdB5O/l/I98=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "P006SxmUS/RjiQJVYPdMFnNo3827GIEmSzagggkg05Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "oyvwY+WsnYV6UHuPki1o0ILJ2jN4uyXf9yaUNtZJyBA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "36Lk3RHWh1wmtCWC/Yj6jNIo17U5y6SofAgQjzjVxD8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vOOo8FqeHnuO9mqOYjIb4vgwIwVyXZ5Y+bY5d9tGFUM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "bJiDJjwQRNxqxlGjRm5lLziFhcfTDCnQ/qU1V85qcRg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2Qgrm1n0wUELAQnpkEiIHB856yv76q8jLbpiucetcm0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "5ciPOYxTK0WDwwYyfs7yiVymwtYQXDELLxmM4JLl4/o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "31dC2WUSIOKQc4jwT6PikfeYTwi80mTlh7P31T5KNQU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YluTV2Mu53EGCKLcWfHZb0BM/IPW2xJdG3vYlDMEsM4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dh/8lGo2Ek6KukSwutH6Q35iy8TgV0FN0SJqe0ZVHN8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EVw6HpIs3BKen2qY2gz4y5dw1JpXilfh07msZfQqJpc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FYolLla9L8EZMROEdWetozroU40Dnmwwx2jIMrr7c1A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8M6k4QIutSIj6CM41vvkQtuFsaGrjoR9SZJVSLbfGKQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9LM0VoddDNHway442MqY+Z7vohB2UHau/cddshhzf40=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "66i8Ytco4Yq/FMl6pIRZazz3CZlu8fO2OI6Pne0pvHU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2a/HgX+MjZxjXtSvHgF1yEpHMJBkl8Caee8XrJtn0WM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "frhBM662c4ZVG7mWP8K/HhRjd01lydW/cPcHnDjifqc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6k1T7Q1t668PBqv6fwpVnT1HWh7Am5LtbKvwPJKcpGU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UlJ5Edfusp8S/Pyhw6KTglIejmbr1HO0zUeHn/qFETA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jsxsB+1ECB3assUdoC333do9tYH+LglHmVSJHy4N8Hg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2nzIQxGYF7j3bGsIesECEOqhObKs/9ywknPHeJ3yges=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xJYKtuWrX90JrJVoYtnwP7Ce59XQGFYoalxpNfBXEH0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NLI5lriBTleGCELcHBtNnmnvwSRkHHaLOX4cKboMgTw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hUOQV0RmE5aJdJww1AR9rirJG4zOYPo+6cCkgn/BGvQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "h4G2Of76AgxcUziBwCyH+ayMOpdBWzg4yFrTfehSC2c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VuamM75RzGfQpj2/Y1jSVuQLrhy6OAwlZxjuQLB/9Ss=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kn9+hLq7hvw02xr9vrplOCDXKBTuFhfbX7d5v/l85Pg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fAiGqKyLZpGngBYFbtYUYt8LUrJ49vYafiboifTDjxs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BxRILymgfVJCczqjUIWXcfrfSgrrYkxTM5VTg0HkZLY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CrFY/PzfPU2zsFkGLu/dI6mEeizZzCR+uYgjZBAHro0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "AEbrIuwvXLTtYgMjOqnGQ8y8axUn5Ukrn7UZRSyfQVw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ouWeVH3PEFg+dKWlXc6BmqirJOaVWjJbMzZbCsce4dA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+hd6xFB+EG+kVP7WH4uMd1CLaWMnt5xJRaY/Guuga9Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zmpGalfAOL3gmcUMJYcLYIRT/2VDO/1Dw4KdYZoNcng=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2PbHAoM/46J2UIZ/vyksKzmVVfxA7YUyIxWeL/N/vBk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7fD9x+zk5MVFesb59Klqiwwmve7P5ON/5COURXj5smE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tlrNQ4jaq051iaWonuv1sSrYhKkL1LtNZuHsvATha3s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fBodm28iClNpvlRyVq0dOdXQ08S7/N3aDwid+PdWvRo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "O+/nnRqT3Zv7yMMGug8GhKHaWy6u7BfRGtZoj0sdN1c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "5AZZ/RTMY4Photnm/cpXZr/HnFRi3eljacMsipkJLHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "oFVyo/kgoMxBIk2VE52ySSimeyU+Gr0EfCwapXnTpKA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Z8v59DfcnviA0mzvnUk+URVO0UuqAWvtarEgJva/n1c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "P64GOntZ+zBJEHkigoh9FSxSO+rJTqR20z5aiGQ9an4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xMbSuDPfWuO/Dm7wuVl06GnzG9uzTlJJX9vFy7boGlY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kXPB19mRClxdH2UsHwlttS6lLU2uHvzuZgZz7kC45jU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NDVjVYXAw4k0w4tFzvs7QDq39aaU3HQor4I2XMKKnCk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uKw/+ErVfpTO1dGUfd3T/eWfZW3nUxXCdBGdjvHtZ88=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "av0uxEzWkizYWm0QUM/MN1hLibnxPvCWJKwjOV4yVQY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ERwUC47dvgOBzIsEESMIioLYbFOxOe8PtJTnmDkKuHM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2gseKlG5Le12fS/vj4eaED4lturF16kAgJ1TpW3HxEE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7Cvg0Y3j/5i2F1TeXxlMmU7xwif5dCmwkZAOrVC5K2Y=", + "subType": "00" + } + } + ] + } \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-DecimalPrecision-Aggregate.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-DecimalPrecision-Aggregate.json new file mode 100644 index 00000000000..a3e605d1bbb --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-DecimalPrecision-Aggregate.json @@ -0,0 +1,590 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range DecimalPrecision. Aggregate.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$gt": { + "$numberDecimal": "0" + } + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1" + } + } + ] + } + ], + "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": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDecimalPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "default", + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + } + } + ], + "cursor": {}, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "aggregate" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", + "subType": "00" + } + } + ] + }, + { + "_id": { + "$numberInt": "1" + }, + "encryptedDecimalPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "mVZb+Ra0EYjQ4Zrh9X//E2T8MRj7NMqm5GUJXhRrBEI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MgwakFvPyBlwqFTbhWUF79URJQWFoJTGotlEVSPPUsQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DyBERpMSD5lEM5Nhpcn4WGgxgn/mkUVJp+PYSLX5jsE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I43iazc0xj1WVbYB/V+uTL/tughN1bBlxh1iypBnNsA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wjOBa/ATMuOywFmuPgC0GF/oeLqu0Z7eK5udzkTPbis=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gRQVwiR+m+0Vg8ZDXqrQQcVnTyobwCXNaA4BCJVXtMc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WUZ6huwx0ZbLb0R00uiC9FOJzsUocUN8qE5+YRenkvQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7s79aKEuPgQcS/YPOOVcYNZvHIo7FFsWtFCrnDKXefA=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-DecimalPrecision-Aggregate.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-DecimalPrecision-Aggregate.yml new file mode 100644 index 00000000000..26f79947537 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-DecimalPrecision-Aggregate.yml @@ -0,0 +1,315 @@ +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDecimalPrecision', 'bsonType': 'decimal', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberDecimal': '0.0'}, 'max': {'$numberDecimal': '200.0'}, 'precision': {'$numberInt': '2'}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "FLE2 Range DecimalPrecision. Aggregate." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDecimalPrecision: { $numberDecimal: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDecimalPrecision: { $numberDecimal: "1" } } + - name: aggregate + arguments: + pipeline: [{ $match: { "encryptedDecimalPrecision": { $gt: {$numberDecimal: "0" }} } }] + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + aggregate: *collection_name + pipeline: [ + { + "$match": { + "encryptedDecimalPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + } + } + ] + cursor: {} + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: aggregate + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", + "subType": "00" + } + } + ] + } + - + { + "_id": { + "$numberInt": "1" + }, + "encryptedDecimalPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "mVZb+Ra0EYjQ4Zrh9X//E2T8MRj7NMqm5GUJXhRrBEI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MgwakFvPyBlwqFTbhWUF79URJQWFoJTGotlEVSPPUsQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DyBERpMSD5lEM5Nhpcn4WGgxgn/mkUVJp+PYSLX5jsE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I43iazc0xj1WVbYB/V+uTL/tughN1bBlxh1iypBnNsA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wjOBa/ATMuOywFmuPgC0GF/oeLqu0Z7eK5udzkTPbis=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gRQVwiR+m+0Vg8ZDXqrQQcVnTyobwCXNaA4BCJVXtMc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WUZ6huwx0ZbLb0R00uiC9FOJzsUocUN8qE5+YRenkvQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7s79aKEuPgQcS/YPOOVcYNZvHIo7FFsWtFCrnDKXefA=", + "subType": "00" + } + } + ] + } \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-DecimalPrecision-Correctness.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-DecimalPrecision-Correctness.json new file mode 100644 index 00000000000..9fafc243d66 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-DecimalPrecision-Correctness.json @@ -0,0 +1,1650 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "Find with $gt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$gt": { + "$numberDecimal": "0.0" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $gte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$gte": { + "$numberDecimal": "0.0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + }, + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $gt with no results", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$gt": { + "$numberDecimal": "1.0" + } + } + } + }, + "result": [] + } + ] + }, + { + "description": "Find with $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$lt": { + "$numberDecimal": "1.0" + } + } + } + }, + "result": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + ] + } + ] + }, + { + "description": "Find with $lte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$lte": { + "$numberDecimal": "1.0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + }, + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $lt below min", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$lt": { + "$numberDecimal": "0.0" + } + } + } + }, + "result": { + "errorContains": "must be greater than the range minimum" + } + } + ] + }, + { + "description": "Find with $gt above max", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$gt": { + "$numberDecimal": "200.0" + } + } + } + }, + "result": { + "errorContains": "must be less than the range max" + } + } + ] + }, + { + "description": "Find with $gt and $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$gt": { + "$numberDecimal": "0.0" + }, + "$lt": { + "$numberDecimal": "2.0" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with equality", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "result": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + ] + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with full range", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$gte": { + "$numberDecimal": "0.0" + }, + "$lte": { + "$numberDecimal": "200.0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + }, + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $in", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$in": [ + { + "$numberDecimal": "0.0" + } + ] + } + } + }, + "result": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + ] + } + ] + }, + { + "description": "Insert out of range", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "-1" + } + } + }, + "result": { + "errorContains": "value must be greater than or equal to the minimum value" + } + } + ] + }, + { + "description": "Insert min and max", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 200, + "encryptedDecimalPrecision": { + "$numberDecimal": "200.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + }, + { + "_id": 200, + "encryptedDecimalPrecision": { + "$numberDecimal": "200.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$gte": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + }, + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gt with no results", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$gt": { + "$numberDecimal": "1.0" + } + } + } + } + ] + }, + "result": [] + } + ] + }, + { + "description": "Aggregate with $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$lt": { + "$numberDecimal": "1.0" + } + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $lte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$lte": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + }, + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $lt below min", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$lt": { + "$numberDecimal": "0.0" + } + } + } + } + ] + }, + "result": { + "errorContains": "must be greater than the range minimum" + } + } + ] + }, + { + "description": "Aggregate with $gt above max", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$gt": { + "$numberDecimal": "200.0" + } + } + } + } + ] + }, + "result": { + "errorContains": "must be less than the range max" + } + } + ] + }, + { + "description": "Aggregate with $gt and $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$gt": { + "$numberDecimal": "0.0" + }, + "$lt": { + "$numberDecimal": "2.0" + } + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with equality", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + ] + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with full range", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$gte": { + "$numberDecimal": "0.0" + }, + "$lte": { + "$numberDecimal": "200.0" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + }, + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $in", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$in": [ + { + "$numberDecimal": "0.0" + } + ] + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + ] + } + ] + }, + { + "description": "Wrong type: Insert Int", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberInt": "0" + } + } + }, + "result": { + "errorContains": "cannot encrypt element" + } + } + ] + }, + { + "description": "Wrong type: Find Int", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$gte": { + "$numberInt": "0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": { + "errorContains": "field type is not supported" + } + } + ] + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-DecimalPrecision-Correctness.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-DecimalPrecision-Correctness.yml new file mode 100644 index 00000000000..9e2ee80aedf --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-DecimalPrecision-Correctness.yml @@ -0,0 +1,422 @@ +# Test correctness results. +# Does not include command monitoring expectations or outcome assertions to make tests more readable. +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDecimalPrecision', 'bsonType': 'decimal', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberDecimal': '0.0'}, 'max': {'$numberDecimal': '200.0'}, 'precision': {'$numberInt': '2'}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "Find with $gt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDecimalPrecision: { $numberDecimal: "0.0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDecimalPrecision: { $numberDecimal: "1.0" } } + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $gt: { $numberDecimal: "0.0" } }} + result: [*doc1] + + - description: "Find with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $gte: { $numberDecimal: "0.0" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $gt: { $numberDecimal: "1.0" } }} + result: [] + + - description: "Find with $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $lt: { $numberDecimal: "1.0" } }} + result: [*doc0] + + - description: "Find with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $lte: { $numberDecimal: "1.0" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $lt below min" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $lt: { $numberDecimal: "0.0" } }} + result: + errorContains: must be greater than the range minimum + + - description: "Find with $gt above max" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $gt: { $numberDecimal: "200.0" } }} + result: + errorContains: must be less than the range max + + - description: "Find with $gt and $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $gt: { $numberDecimal: "0.0" }, $lt: { $numberDecimal: "2.0"} }} + result: [*doc1] + + - description: "Find with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $numberDecimal: "0.0" } } + result: [*doc0] + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $numberDecimal: "1.0" } } + result: [*doc1] + + - description: "Find with full range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $gte: {$numberDecimal: "0.0"}, $lte: {$numberDecimal: "200.0"} } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $in: [ {$numberDecimal: "0.0"} ] } } + result: [*doc0] + + - description: "Insert out of range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedDecimalPrecision: { $numberDecimal: "-1" }} + result: + errorContains: value must be greater than or equal to the minimum value + + - description: "Insert min and max" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: *doc0 + - name: insertOne + arguments: + document: &doc200 { _id: 200, encryptedDecimalPrecision: { $numberDecimal: "200.0" }} + - name: find + arguments: + filter: {} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc200] + + - description: "Aggregate with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalPrecision: { $gte: { $numberDecimal: "0.0" } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalPrecision: { $gt: { $numberDecimal: "1.0" } }} } + result: [] + + - description: "Aggregate with $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalPrecision: { $lt: { $numberDecimal: "1.0" } }} } + result: [*doc0] + + - description: "Aggregate with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalPrecision: { $lte: { $numberDecimal: "1.0" } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $lt below min" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalPrecision: { $lt: { $numberDecimal: "0.0" } }} } + result: + errorContains: must be greater than the range minimum + + - description: "Aggregate with $gt above max" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalPrecision: { $gt: { $numberDecimal: "200.0" } }} } + result: + errorContains: must be less than the range max + + - description: "Aggregate with $gt and $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalPrecision: { $gt: { $numberDecimal: "0.0" }, $lt: { $numberDecimal: "2.0"} }} } + result: [*doc1] + + - description: "Aggregate with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalPrecision: { $numberDecimal: "0.0" } } } + result: [*doc0] + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalPrecision: { $numberDecimal: "1.0" } } } + result: [*doc1] + + - description: "Aggregate with full range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalPrecision: { $gte: {$numberDecimal: "0.0"}, $lte: {$numberDecimal: "200.0"} } } } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalPrecision: { $in: [ {$numberDecimal: "0.0"} ] } } } + result: [*doc0] + + - description: "Wrong type: Insert Int" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: { _id: 0, encryptedDecimalPrecision: { $numberInt: "0" }} } + result: + # Expect an error from mongocryptd. + errorContains: "cannot encrypt element" + + - description: "Wrong type: Find Int" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $gte: { $numberInt: "0" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: + # expect an error from libmongocrypt. + errorContains: "field type is not supported" \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-DecimalPrecision-Delete.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-DecimalPrecision-Delete.json new file mode 100644 index 00000000000..3d7d359af6b --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-DecimalPrecision-Delete.json @@ -0,0 +1,493 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range DecimalPrecision. Delete.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1" + } + } + } + }, + { + "name": "deleteOne", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$gt": { + "$numberDecimal": "0" + } + } + } + }, + "result": { + "deletedCount": 1 + } + } + ], + "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": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDecimalPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "delete": "default", + "deletes": [ + { + "q": { + "encryptedDecimalPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ], + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + }, + "deleteTokens": { + "default.default": { + "encryptedDecimalPrecision": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + } + }, + "command_name": "delete" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-DecimalPrecision-Delete.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-DecimalPrecision-Delete.yml new file mode 100644 index 00000000000..5603f6d34cd --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-DecimalPrecision-Delete.yml @@ -0,0 +1,229 @@ +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDecimalPrecision', 'bsonType': 'decimal', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberDecimal': '0.0'}, 'max': {'$numberDecimal': '200.0'}, 'precision': {'$numberInt': '2'}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "FLE2 Range DecimalPrecision. Delete." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDecimalPrecision: { $numberDecimal: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDecimalPrecision: { $numberDecimal: "1" } } + - name: deleteOne + arguments: + filter: { "encryptedDecimalPrecision": { $gt: {$numberDecimal: "0" }} } + result: + deletedCount: 1 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + delete: *collection_name + deletes: [ + { + "q": { + "encryptedDecimalPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ] + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + "deleteTokens": { + "default.default": { + "encryptedDecimalPrecision": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + command_name: delete + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", + "subType": "00" + } + } + ] + } \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-DecimalPrecision-FindOneAndUpdate.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-DecimalPrecision-FindOneAndUpdate.json new file mode 100644 index 00000000000..b1442c3a3c5 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-DecimalPrecision-FindOneAndUpdate.json @@ -0,0 +1,612 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range DecimalPrecision. FindOneAndUpdate.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1" + } + } + } + }, + { + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$gt": { + "$numberDecimal": "0" + } + } + }, + "update": { + "$set": { + "encryptedDecimalPrecision": { + "$numberDecimal": "2" + } + } + }, + "returnDocument": "Before" + }, + "result": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1" + } + } + } + ], + "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": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDecimalPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "findAndModify": "default", + "query": { + "encryptedDecimalPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "update": { + "$set": { + "encryptedDecimalPrecision": { + "$$type": "binData" + } + } + }, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + }, + "deleteTokens": { + "default.default": { + "encryptedDecimalPrecision": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + } + }, + "command_name": "findAndModify" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", + "subType": "00" + } + } + ] + }, + { + "_id": { + "$numberInt": "1" + }, + "encryptedDecimalPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "V6knyt7Zq2CG3++l75UtBx2m32iGAPjHiAe439Bf02w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0OKSXELxPP85SBVwDGf3LtMEQCJ8TTkFUl/+6jlkdb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uEw0lpQtBppR3vqV9j9+NQRSBF1BzZukb8c9IhyWvxc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zVhZ7Q59O087ji49oMJvBIgeir2oqvUpnh4p53GcTow=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dowrzKs+qJhRMZyKDbhjXbuX43FbmUKOaw9I8YlOZDw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ep5B6cska6THLIF7Mn3tn3RvV9EiwLSt0eZM/CLRUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "URNp/YmmDh5wIZUfAzzgPyJeMNiVx9PMsz52DZRujGY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wlM4IAQhhKQEzoVqS8b1Ddd50GB95OFb9LnzOwyjCP4=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-DecimalPrecision-FindOneAndUpdate.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-DecimalPrecision-FindOneAndUpdate.yml new file mode 100644 index 00000000000..c79f5b9b7f3 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-DecimalPrecision-FindOneAndUpdate.yml @@ -0,0 +1,331 @@ +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDecimalPrecision', 'bsonType': 'decimal', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberDecimal': '0.0'}, 'max': {'$numberDecimal': '200.0'}, 'precision': {'$numberInt': '2'}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "FLE2 Range DecimalPrecision. FindOneAndUpdate." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDecimalPrecision: { $numberDecimal: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDecimalPrecision: { $numberDecimal: "1" } } + - name: findOneAndUpdate + arguments: + filter: { encryptedDecimalPrecision: { $gt: {$numberDecimal: "0"}} } + update: { "$set": { "encryptedDecimalPrecision": {$numberDecimal: "2"}}} + returnDocument: Before + result: *doc1 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + findAndModify: *collection_name + query: { + "encryptedDecimalPrecision": { + "$gt": { + "$binary": { + "base64": "CvoJAAADcGF5bG9hZADKCQAABGcAjgkAAAMwAH0AAAAFZAAgAAAAAHdJ2Vnb4MMzqVYVssjSdDy8XU4GVzMTfGifGETgQ2mYBXMAIAAAAAD7cFfKJGIXo6PjyeX2ria02CckW7dWFDoY/3FyBdm1NQVjACAAAAAAqZO+/+gRWlPaMOvuiXizSmBe7lp1VWg1vJ4UmW8o3bQAAzEAfQAAAAVkACAAAAAAlmvfDrZoydUet4eCVMq7z6a58Ea+1HLJOWxN5lNcrWEFcwAgAAAAAEBo5AWZyC41b9ayjWNQSL4iYEAIwR/JG+ssN8bdoK9RBWMAIAAAAAD4FTKJ6CTzKBAyAwZCLUoDEfnZTRZmhF1q/2hnDzmG9gADMgB9AAAABWQAIAAAAABbiLaoxAA6rinMJw1hC8ZUiq6UU1AQaPFn/py/Y06WuQVzACAAAAAAhtDasFkvYE7SCNu1je/hxdE9TJtAvvH3NtdEbKzNbCUFYwAgAAAAAHHy019aPatHTST+0wGsmukUcsQNQj6KpoS9b7iGeThAAAMzAH0AAAAFZAAgAAAAALORWwSr+tYNxcil2KIGSbNhTHvcPbdj+rLVQNx21S/KBXMAIAAAAAD6diZBkPEJ1cQy06LAxdbNK8Nlxbb44fH4Wk3Y3260nQVjACAAAAAAvUc1q7pyhjU0ilgmwiKkHIY3V4/LxO+Y2uT7eSpBOs8AAzQAfQAAAAVkACAAAAAAG5XMK96PjClNlUvg82j4pMY1YxsznZfj4uNweD394FoFcwAgAAAAAKHgQLdGJHkrfFg9nB93Ac+3VgBw6aU44MTkKIQ91dZoBWMAIAAAAACtbNc1DCoUUyzlkrYmJi4NlwOqLYmb6au4pDc8clXVXwADNQB9AAAABWQAIAAAAAA6M++b9I0YFemmWBAWAE3glu2Ah3Ta1FBxAQEIWS0toAVzACAAAAAANXYTqPf1Y6X3Ns6YQIX0C3FKCyWUo+Kk+fNcQvc0WSoFYwAgAAAAAAaqju6Dv8wqXxcsIbP67V1QGaD5kNTFofZ9Zuf1LGnKAAM2AH0AAAAFZAAgAAAAAKl8bV1riH/uyJ+X0HHd3+18k2cJl2dQFXCdoagutFcaBXMAIAAAAABm8F2Ew9f0VOABdcF+lP0Bi+zWvEUPniWgrxPq/Sx3uwVjACAAAAAAQd2pWVqlmmLg8m8xbs7yLewmR0Z6UQgXofbCsMHaGSoAAzcAfQAAAAVkACAAAAAAsYZD8JEP6kYsPncFnNZwJxhu4YtUTKPNcjHtv67H+rYFcwAgAAAAAI4LqZcRkvbs/2F62Flu0pixNcor4WmBD0DHGaf039wLBWMAIAAAAAAqzpfyBpr4Ano+nFWJyyTuIJelJgiRDnMHQqdeqV8JaAADOAB9AAAABWQAIAAAAAC0vdAi+dmoIXvZ5LqUqvyKV9/tHqSI2SWiSJO5pTnA2wVzACAAAAAAS2qvf9fvfVUH5WtsVxjxmskpGjYTQV34LwvQQw1y9wIFYwAgAAAAAFhNY4qwNntyA+GIoNHZsTkIUbPgy4TBlvNnTPjp4bMFAAM5AH0AAAAFZAAgAAAAAH31lb/srBcrOXkzddCwAnclsR5/3QijEVgECs2JjOWBBXMAIAAAAABg7+prDT73YcCvLE5QbuIrqGcjLc5pQD2Miq0d29yrxgVjACAAAAAAFKqAqXG/ktejFQ7fM2aobO2VmEvZLXnRaJH97Jy/sJYAAzEwAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVjACAAAAAAfsofSP7nQHv8ic8ZW0aNlWxplS46Z+mywPR4rQk+wcgAAzExAH0AAAAFZAAgAAAAAFdthRhe2Q8CvxGIhjTJZv0Lk97GkHciTPxZ/mckLoNaBXMAIAAAAAAqOxsAr23LOVB0DIHbPf9UDJJRFXY2YoKbjhRqw5psbQVjACAAAAAA7ty+Nif6KjS3v1zWKaHX9n4Zj3XC4ajuCduKNIYr3l8AAzEyAH0AAAAFZAAgAAAAAMWymwwbvIeMqmnKWWifUqoCxOsdpnonM2qdLPyjqJO/BXMAIAAAAAB6IDmmpUhBD2zpRj8/y/kmOSXcjuIU14sNh6GKSsg2uwVjACAAAAAABSWO0Ii+NGcsHZQ9MR5EjPXVKeXlI4FQ1pcxeKDiuooAAzEzAH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVjACAAAAAAD51NYesiO4Fo7w7iWBfqAFxEqkaDVctpvzZ28nT4SE8AAzE0AH0AAAAFZAAgAAAAAJaRYmo8zqI2BEUzdSwp4tVRpPmVWsfydkYN3UHh6TMuBXMAIAAAAAAeD6mDnQeLlbC9i0sVgE8+RH6y+e94OJQ0tJ0PvblVSgVjACAAAAAAKUhYSt4nvvUfbNgPJ2E79SciVZ0ZzbzoZ2nKr4ewNLsAAzE1AH0AAAAFZAAgAAAAAAZZ538coNPwyRjhEwr5P8Xw32oWOJF+R+nfCGgy2qO3BXMAIAAAAACOPLnJlKwGNPDBReRKnHfteq0wFb3ezhrc7BVXs8RUHwVjACAAAAAAzCICkPZAkfTiD0MUt155dIPgLJ4/e0qFTM2FR0U261YAAzE2AH0AAAAFZAAgAAAAAFH9l9GGA1I52atJV5jNUf1lx8jBjoEoVoME97v5GFJiBXMAIAAAAAC1qH3Kd78Dr9NGbw7y9D/XYBwv5h1LLO8la5OU7g8UkQVjACAAAAAAn27H0Mpwatgc1R/4nXSRjsG2PzB0ol5YR9f3mCb2y/0AAzE3AH0AAAAFZAAgAAAAADtbVEI2tdkrowEMdkacD2w0Y3T3Ofi7PH6HmA6sP0c/BXMAIAAAAADuBSROnZHA+NgUPH8d0LnWFiDsM2bY8bzjC1+elSsIygVjACAAAAAAMinHEu4wkbeOpdZbXQ94q5o5pIEubqXUDrTRYOGmJC0AAzE4AH0AAAAFZAAgAAAAAAh3WpeMVlikPFYj9hLj+fmIqVt6omCSF75W3TPExyWpBXMAIAAAAAAsQkRmwqeVj2gGE03orb6PtrIzDt6dDU3hgSQi8E2wKgVjACAAAAAAvlZo8Qj3eAdxzZxN5sHKhxi+a9Npj7cZC5+pE6qrOawAAAVlACAAAAAA65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+wSY20AAAAAAAAAAAAAEHBheWxvYWRJZAAAAAAAEGZpcnN0T3BlcmF0b3IAAQAAAAA=", + "subType": "06" + } + } + } + } + update: { "$set": {"encryptedDecimalPrecision": { $$type: "binData" }} } + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + "deleteTokens": { + "default.default": { + "encryptedDecimalPrecision": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + command_name: findAndModify + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", + "subType": "00" + } + } + ] + } + - + { + "_id": { + "$numberInt": "1" + }, + "encryptedDecimalPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "V6knyt7Zq2CG3++l75UtBx2m32iGAPjHiAe439Bf02w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0OKSXELxPP85SBVwDGf3LtMEQCJ8TTkFUl/+6jlkdb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uEw0lpQtBppR3vqV9j9+NQRSBF1BzZukb8c9IhyWvxc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zVhZ7Q59O087ji49oMJvBIgeir2oqvUpnh4p53GcTow=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dowrzKs+qJhRMZyKDbhjXbuX43FbmUKOaw9I8YlOZDw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ep5B6cska6THLIF7Mn3tn3RvV9EiwLSt0eZM/CLRUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "URNp/YmmDh5wIZUfAzzgPyJeMNiVx9PMsz52DZRujGY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wlM4IAQhhKQEzoVqS8b1Ddd50GB95OFb9LnzOwyjCP4=", + "subType": "00" + } + } + ] + } diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-DecimalPrecision-InsertFind.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-DecimalPrecision-InsertFind.json new file mode 100644 index 00000000000..3b8202ff87f --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-DecimalPrecision-InsertFind.json @@ -0,0 +1,577 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range DecimalPrecision. Insert and Find.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$gt": { + "$numberDecimal": "0" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1" + } + } + ] + } + ], + "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": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDecimalPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "encryptedDecimalPrecision": { + "$gt": { + "$binary": { + "base64": "CvoJAAADcGF5bG9hZADKCQAABGcAjgkAAAMwAH0AAAAFZAAgAAAAAHdJ2Vnb4MMzqVYVssjSdDy8XU4GVzMTfGifGETgQ2mYBXMAIAAAAAD7cFfKJGIXo6PjyeX2ria02CckW7dWFDoY/3FyBdm1NQVjACAAAAAAqZO+/+gRWlPaMOvuiXizSmBe7lp1VWg1vJ4UmW8o3bQAAzEAfQAAAAVkACAAAAAAlmvfDrZoydUet4eCVMq7z6a58Ea+1HLJOWxN5lNcrWEFcwAgAAAAAEBo5AWZyC41b9ayjWNQSL4iYEAIwR/JG+ssN8bdoK9RBWMAIAAAAAD4FTKJ6CTzKBAyAwZCLUoDEfnZTRZmhF1q/2hnDzmG9gADMgB9AAAABWQAIAAAAABbiLaoxAA6rinMJw1hC8ZUiq6UU1AQaPFn/py/Y06WuQVzACAAAAAAhtDasFkvYE7SCNu1je/hxdE9TJtAvvH3NtdEbKzNbCUFYwAgAAAAAHHy019aPatHTST+0wGsmukUcsQNQj6KpoS9b7iGeThAAAMzAH0AAAAFZAAgAAAAALORWwSr+tYNxcil2KIGSbNhTHvcPbdj+rLVQNx21S/KBXMAIAAAAAD6diZBkPEJ1cQy06LAxdbNK8Nlxbb44fH4Wk3Y3260nQVjACAAAAAAvUc1q7pyhjU0ilgmwiKkHIY3V4/LxO+Y2uT7eSpBOs8AAzQAfQAAAAVkACAAAAAAG5XMK96PjClNlUvg82j4pMY1YxsznZfj4uNweD394FoFcwAgAAAAAKHgQLdGJHkrfFg9nB93Ac+3VgBw6aU44MTkKIQ91dZoBWMAIAAAAACtbNc1DCoUUyzlkrYmJi4NlwOqLYmb6au4pDc8clXVXwADNQB9AAAABWQAIAAAAAA6M++b9I0YFemmWBAWAE3glu2Ah3Ta1FBxAQEIWS0toAVzACAAAAAANXYTqPf1Y6X3Ns6YQIX0C3FKCyWUo+Kk+fNcQvc0WSoFYwAgAAAAAAaqju6Dv8wqXxcsIbP67V1QGaD5kNTFofZ9Zuf1LGnKAAM2AH0AAAAFZAAgAAAAAKl8bV1riH/uyJ+X0HHd3+18k2cJl2dQFXCdoagutFcaBXMAIAAAAABm8F2Ew9f0VOABdcF+lP0Bi+zWvEUPniWgrxPq/Sx3uwVjACAAAAAAQd2pWVqlmmLg8m8xbs7yLewmR0Z6UQgXofbCsMHaGSoAAzcAfQAAAAVkACAAAAAAsYZD8JEP6kYsPncFnNZwJxhu4YtUTKPNcjHtv67H+rYFcwAgAAAAAI4LqZcRkvbs/2F62Flu0pixNcor4WmBD0DHGaf039wLBWMAIAAAAAAqzpfyBpr4Ano+nFWJyyTuIJelJgiRDnMHQqdeqV8JaAADOAB9AAAABWQAIAAAAAC0vdAi+dmoIXvZ5LqUqvyKV9/tHqSI2SWiSJO5pTnA2wVzACAAAAAAS2qvf9fvfVUH5WtsVxjxmskpGjYTQV34LwvQQw1y9wIFYwAgAAAAAFhNY4qwNntyA+GIoNHZsTkIUbPgy4TBlvNnTPjp4bMFAAM5AH0AAAAFZAAgAAAAAH31lb/srBcrOXkzddCwAnclsR5/3QijEVgECs2JjOWBBXMAIAAAAABg7+prDT73YcCvLE5QbuIrqGcjLc5pQD2Miq0d29yrxgVjACAAAAAAFKqAqXG/ktejFQ7fM2aobO2VmEvZLXnRaJH97Jy/sJYAAzEwAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVjACAAAAAAfsofSP7nQHv8ic8ZW0aNlWxplS46Z+mywPR4rQk+wcgAAzExAH0AAAAFZAAgAAAAAFdthRhe2Q8CvxGIhjTJZv0Lk97GkHciTPxZ/mckLoNaBXMAIAAAAAAqOxsAr23LOVB0DIHbPf9UDJJRFXY2YoKbjhRqw5psbQVjACAAAAAA7ty+Nif6KjS3v1zWKaHX9n4Zj3XC4ajuCduKNIYr3l8AAzEyAH0AAAAFZAAgAAAAAMWymwwbvIeMqmnKWWifUqoCxOsdpnonM2qdLPyjqJO/BXMAIAAAAAB6IDmmpUhBD2zpRj8/y/kmOSXcjuIU14sNh6GKSsg2uwVjACAAAAAABSWO0Ii+NGcsHZQ9MR5EjPXVKeXlI4FQ1pcxeKDiuooAAzEzAH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVjACAAAAAAD51NYesiO4Fo7w7iWBfqAFxEqkaDVctpvzZ28nT4SE8AAzE0AH0AAAAFZAAgAAAAAJaRYmo8zqI2BEUzdSwp4tVRpPmVWsfydkYN3UHh6TMuBXMAIAAAAAAeD6mDnQeLlbC9i0sVgE8+RH6y+e94OJQ0tJ0PvblVSgVjACAAAAAAKUhYSt4nvvUfbNgPJ2E79SciVZ0ZzbzoZ2nKr4ewNLsAAzE1AH0AAAAFZAAgAAAAAAZZ538coNPwyRjhEwr5P8Xw32oWOJF+R+nfCGgy2qO3BXMAIAAAAACOPLnJlKwGNPDBReRKnHfteq0wFb3ezhrc7BVXs8RUHwVjACAAAAAAzCICkPZAkfTiD0MUt155dIPgLJ4/e0qFTM2FR0U261YAAzE2AH0AAAAFZAAgAAAAAFH9l9GGA1I52atJV5jNUf1lx8jBjoEoVoME97v5GFJiBXMAIAAAAAC1qH3Kd78Dr9NGbw7y9D/XYBwv5h1LLO8la5OU7g8UkQVjACAAAAAAn27H0Mpwatgc1R/4nXSRjsG2PzB0ol5YR9f3mCb2y/0AAzE3AH0AAAAFZAAgAAAAADtbVEI2tdkrowEMdkacD2w0Y3T3Ofi7PH6HmA6sP0c/BXMAIAAAAADuBSROnZHA+NgUPH8d0LnWFiDsM2bY8bzjC1+elSsIygVjACAAAAAAMinHEu4wkbeOpdZbXQ94q5o5pIEubqXUDrTRYOGmJC0AAzE4AH0AAAAFZAAgAAAAAAh3WpeMVlikPFYj9hLj+fmIqVt6omCSF75W3TPExyWpBXMAIAAAAAAsQkRmwqeVj2gGE03orb6PtrIzDt6dDU3hgSQi8E2wKgVjACAAAAAAvlZo8Qj3eAdxzZxN5sHKhxi+a9Npj7cZC5+pE6qrOawAAAVlACAAAAAA65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+wSY20AAAAAAAAAAAAAEHBheWxvYWRJZAAAAAAAEGZpcnN0T3BlcmF0b3IAAQAAAAA=", + "subType": "06" + } + } + } + }, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedDecimalPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "mVZb+Ra0EYjQ4Zrh9X//E2T8MRj7NMqm5GUJXhRrBEI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MgwakFvPyBlwqFTbhWUF79URJQWFoJTGotlEVSPPUsQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DyBERpMSD5lEM5Nhpcn4WGgxgn/mkUVJp+PYSLX5jsE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I43iazc0xj1WVbYB/V+uTL/tughN1bBlxh1iypBnNsA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wjOBa/ATMuOywFmuPgC0GF/oeLqu0Z7eK5udzkTPbis=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gRQVwiR+m+0Vg8ZDXqrQQcVnTyobwCXNaA4BCJVXtMc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WUZ6huwx0ZbLb0R00uiC9FOJzsUocUN8qE5+YRenkvQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7s79aKEuPgQcS/YPOOVcYNZvHIo7FFsWtFCrnDKXefA=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-DecimalPrecision-InsertFind.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-DecimalPrecision-InsertFind.yml new file mode 100644 index 00000000000..2bdd29cdb3e --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-DecimalPrecision-InsertFind.yml @@ -0,0 +1,305 @@ +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDecimalPrecision', 'bsonType': 'decimal', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberDecimal': '0.0'}, 'max': {'$numberDecimal': '200.0'}, 'precision': {'$numberInt': '2'}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "FLE2 Range DecimalPrecision. Insert and Find." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDecimalPrecision: { $numberDecimal: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDecimalPrecision: { $numberDecimal: "1" } } + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $gt: { $numberDecimal: "0" } } } + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: + "encryptedDecimalPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDecimalPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", + "subType": "00" + } + } + ] + } + - + { + "_id": 1, + "encryptedDecimalPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "mVZb+Ra0EYjQ4Zrh9X//E2T8MRj7NMqm5GUJXhRrBEI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MgwakFvPyBlwqFTbhWUF79URJQWFoJTGotlEVSPPUsQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DyBERpMSD5lEM5Nhpcn4WGgxgn/mkUVJp+PYSLX5jsE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I43iazc0xj1WVbYB/V+uTL/tughN1bBlxh1iypBnNsA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wjOBa/ATMuOywFmuPgC0GF/oeLqu0Z7eK5udzkTPbis=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gRQVwiR+m+0Vg8ZDXqrQQcVnTyobwCXNaA4BCJVXtMc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WUZ6huwx0ZbLb0R00uiC9FOJzsUocUN8qE5+YRenkvQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7s79aKEuPgQcS/YPOOVcYNZvHIo7FFsWtFCrnDKXefA=", + "subType": "00" + } + } + ] + } \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-DecimalPrecision-Update.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-DecimalPrecision-Update.json new file mode 100644 index 00000000000..3dc6631c617 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-DecimalPrecision-Update.json @@ -0,0 +1,612 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range DecimalPrecision. Update.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1" + } + } + } + }, + { + "name": "updateOne", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$gt": { + "$numberDecimal": "0" + } + } + }, + "update": { + "$set": { + "encryptedDecimalPrecision": { + "$numberDecimal": "2" + } + } + } + }, + "result": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "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": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDecimalPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command_name": "update", + "command": { + "update": "default", + "ordered": true, + "updates": [ + { + "q": { + "encryptedDecimalPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encryptedDecimalPrecision": { + "$$type": "binData" + } + } + } + } + ], + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + }, + "deleteTokens": { + "default.default": { + "encryptedDecimalPrecision": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + }, + "$db": "default" + } + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedDecimalPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "V6knyt7Zq2CG3++l75UtBx2m32iGAPjHiAe439Bf02w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0OKSXELxPP85SBVwDGf3LtMEQCJ8TTkFUl/+6jlkdb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uEw0lpQtBppR3vqV9j9+NQRSBF1BzZukb8c9IhyWvxc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zVhZ7Q59O087ji49oMJvBIgeir2oqvUpnh4p53GcTow=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dowrzKs+qJhRMZyKDbhjXbuX43FbmUKOaw9I8YlOZDw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ep5B6cska6THLIF7Mn3tn3RvV9EiwLSt0eZM/CLRUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "URNp/YmmDh5wIZUfAzzgPyJeMNiVx9PMsz52DZRujGY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wlM4IAQhhKQEzoVqS8b1Ddd50GB95OFb9LnzOwyjCP4=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-DecimalPrecision-Update.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-DecimalPrecision-Update.yml new file mode 100644 index 00000000000..e852a30c61c --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-DecimalPrecision-Update.yml @@ -0,0 +1,344 @@ +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDecimalPrecision', 'bsonType': 'decimal', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberDecimal': '0.0'}, 'max': {'$numberDecimal': '200.0'}, 'precision': {'$numberInt': '2'}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "FLE2 Range DecimalPrecision. Update." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDecimalPrecision: { $numberDecimal: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDecimalPrecision: { $numberDecimal: "1" } } + - name: updateOne + arguments: + filter: { encryptedDecimalPrecision: { $gt: { $numberDecimal: "0" } } } + update: { "$set": { "encryptedDecimalPrecision": { $numberDecimal: "2" } }} + result: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command_name: update + command: + { + "update": "default", + "ordered": true, + "updates": [ + { + "q": { + "encryptedDecimalPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encryptedDecimalPrecision": { $$type: "binData" } + } + } + } + ], + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": *encrypted_fields + }, + "deleteTokens": { + "default.default": { + "encryptedDecimalPrecision": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + }, + "$db": "default" + } + + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDecimalPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", + "subType": "00" + } + } + ] + } + - + { + "_id": 1, + "encryptedDecimalPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "V6knyt7Zq2CG3++l75UtBx2m32iGAPjHiAe439Bf02w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0OKSXELxPP85SBVwDGf3LtMEQCJ8TTkFUl/+6jlkdb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uEw0lpQtBppR3vqV9j9+NQRSBF1BzZukb8c9IhyWvxc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zVhZ7Q59O087ji49oMJvBIgeir2oqvUpnh4p53GcTow=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dowrzKs+qJhRMZyKDbhjXbuX43FbmUKOaw9I8YlOZDw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ep5B6cska6THLIF7Mn3tn3RvV9EiwLSt0eZM/CLRUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "URNp/YmmDh5wIZUfAzzgPyJeMNiVx9PMsz52DZRujGY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wlM4IAQhhKQEzoVqS8b1Ddd50GB95OFb9LnzOwyjCP4=", + "subType": "00" + } + } + ] + } \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Double-Aggregate.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-Double-Aggregate.json new file mode 100644 index 00000000000..3d54be3d188 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Double-Aggregate.json @@ -0,0 +1,1138 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDouble", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Double. Aggregate.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDouble": { + "$numberDouble": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDouble": { + "$numberDouble": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDouble": { + "$gt": { + "$numberDouble": "0" + } + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedDouble": { + "$numberDouble": "1" + } + } + ] + } + ], + "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": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDouble": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDouble", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDouble": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDouble", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "default", + "pipeline": [ + { + "$match": { + "encryptedDouble": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + } + } + ], + "cursor": {}, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDouble", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "aggregate" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDouble": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedDouble": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2FIZh/9N+NeJEQwxYIX5ikQT85xJzulBNReXk8PnG/s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I93Md7QNPGmEEGYU1+VVCqBPBEvXdqHPtTJtMOn06Yk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "GecBFQ1PemlECWZWCl7f74vmsL6eB6mzQ9n6tK6FYfs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QpjhZl+O1ORifgtCZuWAdcP6OKL7IZ2cA46v8FJcV28=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FWXI/yZ1M+2fIboeMCDMlp+I2NwPQDtoM/wWselOPYw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uk26nvN/LdRLaBphiBgIZzT0sSpoO1z0RdDWRm/xrSA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hiiYSH1KZovAULc7rlmEU74wCjzDR+mm6ZnsgvFQjMw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hRzvMvWPX0sJme+wck67lwbKDFaWOa+Eyef+JSdc1s4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PSx5D+zqC9c295dguX4+EobT4IEzfffdfjzC8DWpB5Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QzfXQCVTjPQv2h21v95HYPq8uCsVJ2tPnjv79gAaM9M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XcGDO/dlTcEMLqwcm55UmOqK+KpBmbzZO1LIzX7GPaQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Lf+o4E7YB5ynzUPC6KTyW0lj6Cg9oLIu1Sdd1ODHctA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wAuVn02LAVo5Y+TUocvkoenFYWzpu38k0NmGZOsAjS4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yJGDtveLbbo/0HtCtiTSsvVI/0agg/U1bFaQ0yhK12o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KsEy0zgYcmkM+O/fWF9z3aJGIk22XCk+Aw96HB6JU68=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "p+AnMI5ZxdJMSIEJmXXya+FeH5yubmOdViwUO89j0Rc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/jLix56jzeywBtNuGw55lCXyebQoSIhbful0hOKxKDY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fvDvSPomtJsl1S3+8/tzFCE8scHIdJY5hB9CdTEsoFo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "oV5hOJzPXxfTuRdKIlF4uYEoMDuqH+G7/3qgndDr0PM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "3ALwcvLj3VOfgD6OqXAO13h1ZkOv46R6+Oy6SUKh53I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gxaB9FJj0IM+InhvAjwWaex3UIZ9SAnDiUd5WHSY/l0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "66NPvDygJzKJqddfNuDuNOpvGajjFRtvhkwfUkiYmXw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1dWcQIocRAcO9XnXYqbhl83jc0RgjQpsrWd8dC27trg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "npos0Uf1DT3ztSCjPVY9EImlRnTHB1KLrvmVSqBQ/8E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "TEI9qBx/tK1l1H0v1scMG8Srmtwo5VxWHADPBSlWrXk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "3wUN2ypQKoj+5ASkeIK9ycxhahVxyTmGopigoUAlyYs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o/oksSnUS+nIq6ozWTbB5bJh+NoaPj8deAA23uxiWCk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KExYPruhA31e8xuSwvfUfDcyY/H2Va6taUd0k4yFgLc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/x+dNfxdd/lkx8Z8VZVfoYl7LPoaZ/iKEzZXBrAtIJc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DE4cmjFLPqZlmRomO0qQiruUBtzoCe8ZdNRcfNH92pU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "M6EKNcLPw/iojAChgYUSieaBYWcbsjKtB94SaHOr8vk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+qP49lDPeyhaduTvXJgtJEqHNEYANVu9Bg3Bxz7Td9w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ruMrC2VIS+VKbJwCFb3bfkaLTju9nE+yPONV9s0M0Vo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EbjDlSB5JKnDKff4d8hOmaOwJ7B9Q6NQFisLj+DPC+0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "C/yYOTB94edyqAbiQNu8/H7FoG3yRRjHDkMykz4+Mv0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CBxqrejG+qQQq2YTd6iP/06kiu2CxxzBFaZK3Ofb1CM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2ZOQ/fpho+AbDENWBZaln7wRoepIRdhyT648dr8O5cU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EghIgEPz01+myPgj8oid+PgncvobvC7vjvG3THEEQ0M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "92CysZYNF8riwAMhdrIPKxfODw9p07cKQy/Snn8XmVY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VO0LeTBQmsEf7sCHzTnZwUPNTqRZ49R8V5E9XnZ/5N4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "exs8BQMJq7U6ZXYgIizT7XN+X/hOmmn4YEuzev9zgSI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qHpS4k1I+gPniNp4CA8TY8lLN36vBYmgbKMFpbYMEqg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+7lWKCKAWFw6gPZdHE6E8KIfI14/fSvtWUmllb5WLi0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YiH/US0q6679hWblFDDKNqUjCgggoU8sUCssTIF1QbU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YgwkKElEubNfvXL9hJxzqQUQtHiXN/OCGxNL1MUZZlM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hZFST4INZTTuhvJlGJeMwlUAK270UCOTCDeBAnN4a7g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "24I1Zw35AuGnK3CqJhbCwYb0IPuu5sCRrM5iyeITOLc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vgD12JB4Q1S/kGPSQ1KOgp386KnG1GbM/5+60oRGcGw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+wNE+OL+CB9d4AUJdVxd56jUJCAXmmk9fapuB2TAc4g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uhQh1B2Pe4RkNw/kPEcgaLenuikKoRf1iyfZhpXdodc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "eu8gjAUIp8ybO204AgeOq5v1neI1yljqy5v3I6lo1lM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7QG6oVbASBAjrnCPxzzUNnuFSFNlKhbuBafkF8pr7Is=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PUS1xb2oHSDTdYltutoSSxBiJ1NjxH3l2kA4P1CZLEs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XPMh/JDC/O93gJJCwwgJDb8ssWZvRvezNmKmyn3nIfk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jWz+KGwMk/GOvFAK2rOxF3OjxeZAWfmUQ1HGJ7icw4A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o7XbW68pc6flYigf3LW4WAGUWxpeqxaQLkHUhUR9RZ8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "nqR+g60+5U0okbqJadSqGgnC+j1JcP8rwMcfzOs2ACI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Hz43qVK95tSfbYFtaE/8fE97XMk1RiO8XpWjwZHB80o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "noZUWlZ8M6KXU5rkifyo8/duw5IL7/fXbJvT7bNmW9k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WONVHCuPSanXDRQQ/3tmyJ0Vq+Lu/4hRaMUf0g0kSuw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UEaj6vQRoIghE8Movd8AGXhtwIOXlP4cBsECIUvE5Y8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "D3n2YcO8+PB4C8brDo7kxKjF9Y844rVkdRMLTgsQkrw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "C+YA0G9KjxZVaWwOMuh/dcnHnHAlYnbFrRl0IEpmsY0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rUnmbmQanxrbFPYYrwyQ53x66OSt27yAvF+s48ezKDc=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Double-Aggregate.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-Double-Aggregate.yml new file mode 100644 index 00000000000..67598094c1e --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Double-Aggregate.yml @@ -0,0 +1,899 @@ +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDouble', 'bsonType': 'double', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "FLE2 Range Double. Aggregate." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDouble: { $numberDouble: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDouble: { $numberDouble: "1" } } + - name: aggregate + arguments: + pipeline: [{ $match: { "encryptedDouble": { $gt: {$numberDouble: "0" }} } }] + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDouble": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDouble": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + aggregate: *collection_name + pipeline: [ + { + "$match": { + "encryptedDouble": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + } + } + ] + cursor: {} + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: aggregate + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDouble": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", + "subType": "00" + } + } + ] + } + - + { + "_id": 1, + "encryptedDouble": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2FIZh/9N+NeJEQwxYIX5ikQT85xJzulBNReXk8PnG/s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I93Md7QNPGmEEGYU1+VVCqBPBEvXdqHPtTJtMOn06Yk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "GecBFQ1PemlECWZWCl7f74vmsL6eB6mzQ9n6tK6FYfs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QpjhZl+O1ORifgtCZuWAdcP6OKL7IZ2cA46v8FJcV28=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FWXI/yZ1M+2fIboeMCDMlp+I2NwPQDtoM/wWselOPYw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uk26nvN/LdRLaBphiBgIZzT0sSpoO1z0RdDWRm/xrSA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hiiYSH1KZovAULc7rlmEU74wCjzDR+mm6ZnsgvFQjMw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hRzvMvWPX0sJme+wck67lwbKDFaWOa+Eyef+JSdc1s4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PSx5D+zqC9c295dguX4+EobT4IEzfffdfjzC8DWpB5Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QzfXQCVTjPQv2h21v95HYPq8uCsVJ2tPnjv79gAaM9M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XcGDO/dlTcEMLqwcm55UmOqK+KpBmbzZO1LIzX7GPaQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Lf+o4E7YB5ynzUPC6KTyW0lj6Cg9oLIu1Sdd1ODHctA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wAuVn02LAVo5Y+TUocvkoenFYWzpu38k0NmGZOsAjS4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yJGDtveLbbo/0HtCtiTSsvVI/0agg/U1bFaQ0yhK12o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KsEy0zgYcmkM+O/fWF9z3aJGIk22XCk+Aw96HB6JU68=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "p+AnMI5ZxdJMSIEJmXXya+FeH5yubmOdViwUO89j0Rc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/jLix56jzeywBtNuGw55lCXyebQoSIhbful0hOKxKDY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fvDvSPomtJsl1S3+8/tzFCE8scHIdJY5hB9CdTEsoFo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "oV5hOJzPXxfTuRdKIlF4uYEoMDuqH+G7/3qgndDr0PM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "3ALwcvLj3VOfgD6OqXAO13h1ZkOv46R6+Oy6SUKh53I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gxaB9FJj0IM+InhvAjwWaex3UIZ9SAnDiUd5WHSY/l0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "66NPvDygJzKJqddfNuDuNOpvGajjFRtvhkwfUkiYmXw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1dWcQIocRAcO9XnXYqbhl83jc0RgjQpsrWd8dC27trg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "npos0Uf1DT3ztSCjPVY9EImlRnTHB1KLrvmVSqBQ/8E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "TEI9qBx/tK1l1H0v1scMG8Srmtwo5VxWHADPBSlWrXk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "3wUN2ypQKoj+5ASkeIK9ycxhahVxyTmGopigoUAlyYs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o/oksSnUS+nIq6ozWTbB5bJh+NoaPj8deAA23uxiWCk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KExYPruhA31e8xuSwvfUfDcyY/H2Va6taUd0k4yFgLc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/x+dNfxdd/lkx8Z8VZVfoYl7LPoaZ/iKEzZXBrAtIJc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DE4cmjFLPqZlmRomO0qQiruUBtzoCe8ZdNRcfNH92pU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "M6EKNcLPw/iojAChgYUSieaBYWcbsjKtB94SaHOr8vk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+qP49lDPeyhaduTvXJgtJEqHNEYANVu9Bg3Bxz7Td9w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ruMrC2VIS+VKbJwCFb3bfkaLTju9nE+yPONV9s0M0Vo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EbjDlSB5JKnDKff4d8hOmaOwJ7B9Q6NQFisLj+DPC+0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "C/yYOTB94edyqAbiQNu8/H7FoG3yRRjHDkMykz4+Mv0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CBxqrejG+qQQq2YTd6iP/06kiu2CxxzBFaZK3Ofb1CM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2ZOQ/fpho+AbDENWBZaln7wRoepIRdhyT648dr8O5cU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EghIgEPz01+myPgj8oid+PgncvobvC7vjvG3THEEQ0M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "92CysZYNF8riwAMhdrIPKxfODw9p07cKQy/Snn8XmVY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VO0LeTBQmsEf7sCHzTnZwUPNTqRZ49R8V5E9XnZ/5N4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "exs8BQMJq7U6ZXYgIizT7XN+X/hOmmn4YEuzev9zgSI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qHpS4k1I+gPniNp4CA8TY8lLN36vBYmgbKMFpbYMEqg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+7lWKCKAWFw6gPZdHE6E8KIfI14/fSvtWUmllb5WLi0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YiH/US0q6679hWblFDDKNqUjCgggoU8sUCssTIF1QbU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YgwkKElEubNfvXL9hJxzqQUQtHiXN/OCGxNL1MUZZlM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hZFST4INZTTuhvJlGJeMwlUAK270UCOTCDeBAnN4a7g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "24I1Zw35AuGnK3CqJhbCwYb0IPuu5sCRrM5iyeITOLc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vgD12JB4Q1S/kGPSQ1KOgp386KnG1GbM/5+60oRGcGw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+wNE+OL+CB9d4AUJdVxd56jUJCAXmmk9fapuB2TAc4g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uhQh1B2Pe4RkNw/kPEcgaLenuikKoRf1iyfZhpXdodc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "eu8gjAUIp8ybO204AgeOq5v1neI1yljqy5v3I6lo1lM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7QG6oVbASBAjrnCPxzzUNnuFSFNlKhbuBafkF8pr7Is=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PUS1xb2oHSDTdYltutoSSxBiJ1NjxH3l2kA4P1CZLEs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XPMh/JDC/O93gJJCwwgJDb8ssWZvRvezNmKmyn3nIfk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jWz+KGwMk/GOvFAK2rOxF3OjxeZAWfmUQ1HGJ7icw4A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o7XbW68pc6flYigf3LW4WAGUWxpeqxaQLkHUhUR9RZ8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "nqR+g60+5U0okbqJadSqGgnC+j1JcP8rwMcfzOs2ACI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Hz43qVK95tSfbYFtaE/8fE97XMk1RiO8XpWjwZHB80o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "noZUWlZ8M6KXU5rkifyo8/duw5IL7/fXbJvT7bNmW9k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WONVHCuPSanXDRQQ/3tmyJ0Vq+Lu/4hRaMUf0g0kSuw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UEaj6vQRoIghE8Movd8AGXhtwIOXlP4cBsECIUvE5Y8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "D3n2YcO8+PB4C8brDo7kxKjF9Y844rVkdRMLTgsQkrw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "C+YA0G9KjxZVaWwOMuh/dcnHnHAlYnbFrRl0IEpmsY0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rUnmbmQanxrbFPYYrwyQ53x66OSt27yAvF+s48ezKDc=", + "subType": "00" + } + } + ] + } \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Double-Correctness.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-Double-Correctness.json new file mode 100644 index 00000000000..b09e966324e --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Double-Correctness.json @@ -0,0 +1,1160 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDouble", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "Find with $gt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDouble": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDouble": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDouble": { + "$gt": { + "$numberDouble": "0.0" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDouble": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $gte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDouble": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDouble": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDouble": { + "$gte": { + "$numberDouble": "0.0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedDouble": { + "$numberDouble": "0.0" + } + }, + { + "_id": 1, + "encryptedDouble": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $gt with no results", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDouble": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDouble": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDouble": { + "$gt": { + "$numberDouble": "1.0" + } + } + } + }, + "result": [] + } + ] + }, + { + "description": "Find with $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDouble": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDouble": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDouble": { + "$lt": { + "$numberDouble": "1.0" + } + } + } + }, + "result": [ + { + "_id": 0, + "encryptedDouble": { + "$numberDouble": "0.0" + } + } + ] + } + ] + }, + { + "description": "Find with $lte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDouble": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDouble": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDouble": { + "$lte": { + "$numberDouble": "1.0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedDouble": { + "$numberDouble": "0.0" + } + }, + { + "_id": 1, + "encryptedDouble": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $gt and $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDouble": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDouble": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDouble": { + "$gt": { + "$numberDouble": "0.0" + }, + "$lt": { + "$numberDouble": "2.0" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDouble": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with equality", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDouble": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDouble": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDouble": { + "$numberDouble": "0.0" + } + } + }, + "result": [ + { + "_id": 0, + "encryptedDouble": { + "$numberDouble": "0.0" + } + } + ] + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDouble": { + "$numberDouble": "1.0" + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDouble": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $in", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDouble": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDouble": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDouble": { + "$in": [ + { + "$numberDouble": "0.0" + } + ] + } + } + }, + "result": [ + { + "_id": 0, + "encryptedDouble": { + "$numberDouble": "0.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDouble": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDouble": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDouble": { + "$gte": { + "$numberDouble": "0.0" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDouble": { + "$numberDouble": "0.0" + } + }, + { + "_id": 1, + "encryptedDouble": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gt with no results", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDouble": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDouble": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDouble": { + "$gt": { + "$numberDouble": "1.0" + } + } + } + } + ] + }, + "result": [] + } + ] + }, + { + "description": "Aggregate with $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDouble": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDouble": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDouble": { + "$lt": { + "$numberDouble": "1.0" + } + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDouble": { + "$numberDouble": "0.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $lte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDouble": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDouble": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDouble": { + "$lte": { + "$numberDouble": "1.0" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDouble": { + "$numberDouble": "0.0" + } + }, + { + "_id": 1, + "encryptedDouble": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gt and $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDouble": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDouble": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDouble": { + "$gt": { + "$numberDouble": "0.0" + }, + "$lt": { + "$numberDouble": "2.0" + } + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedDouble": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with equality", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDouble": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDouble": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDouble": { + "$numberDouble": "0.0" + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDouble": { + "$numberDouble": "0.0" + } + } + ] + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDouble": { + "$numberDouble": "1.0" + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedDouble": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $in", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDouble": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDouble": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDouble": { + "$in": [ + { + "$numberDouble": "0.0" + } + ] + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDouble": { + "$numberDouble": "0.0" + } + } + ] + } + ] + }, + { + "description": "Wrong type: Insert Int", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDouble": { + "$numberInt": "0" + } + } + }, + "result": { + "errorContains": "cannot encrypt element" + } + } + ] + }, + { + "description": "Wrong type: Find Int", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "encryptedDouble": { + "$gte": { + "$numberInt": "0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": { + "errorContains": "field type is not supported" + } + } + ] + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Double-Correctness.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-Double-Correctness.yml new file mode 100644 index 00000000000..bca46f2c66d --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Double-Correctness.yml @@ -0,0 +1,290 @@ +# Test correctness results. +# Does not include command monitoring expectations or outcome assertions to make tests more readable. +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDouble', 'bsonType': 'double', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "Find with $gt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDouble: { $numberDouble: "0.0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDouble: { $numberDouble: "1.0" } } + - name: find + arguments: + filter: { encryptedDouble: { $gt: { $numberDouble: "0.0" } }} + result: [*doc1] + + - description: "Find with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDouble: { $gte: { $numberDouble: "0.0" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDouble: { $gt: { $numberDouble: "1.0" } }} + result: [] + + - description: "Find with $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDouble: { $lt: { $numberDouble: "1.0" } }} + result: [*doc0] + + - description: "Find with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDouble: { $lte: { $numberDouble: "1.0" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $gt and $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDouble: { $gt: { $numberDouble: "0.0" }, $lt: { $numberDouble: "2.0"} }} + result: [*doc1] + + - description: "Find with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDouble: { $numberDouble: "0.0" } } + result: [*doc0] + - name: find + arguments: + filter: { encryptedDouble: { $numberDouble: "1.0" } } + result: [*doc1] + + - description: "Find with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDouble: { $in: [ {$numberDouble: "0.0"} ] } } + result: [*doc0] + + - description: "Aggregate with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDouble: { $gte: { $numberDouble: "0.0" } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDouble: { $gt: { $numberDouble: "1.0" } }} } + result: [] + + - description: "Aggregate with $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDouble: { $lt: { $numberDouble: "1.0" } }} } + result: [*doc0] + + - description: "Aggregate with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDouble: { $lte: { $numberDouble: "1.0" } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $gt and $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDouble: { $gt: { $numberDouble: "0.0" }, $lt: { $numberDouble: "2.0"} }} } + result: [*doc1] + + - description: "Aggregate with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDouble: { $numberDouble: "0.0" } } } + result: [*doc0] + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDouble: { $numberDouble: "1.0" } } } + result: [*doc1] + + - description: "Aggregate with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDouble: { $in: [ {$numberDouble: "0.0"} ] } } } + result: [*doc0] + + - description: "Wrong type: Insert Int" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: { _id: 0, encryptedDouble: { $numberInt: "0" }} } + result: + # Expect an error from mongocryptd. + errorContains: "cannot encrypt element" + + - description: "Wrong type: Find Int" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: find + arguments: + filter: { encryptedDouble: { $gte: { $numberInt: "0" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: + # expect an error from libmongocrypt. + errorContains: "field type is not supported" \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Double-Delete.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-Double-Delete.json new file mode 100644 index 00000000000..fa09cb87df6 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Double-Delete.json @@ -0,0 +1,749 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDouble", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Double. Delete.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDouble": { + "$numberDouble": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDouble": { + "$numberDouble": "1" + } + } + } + }, + { + "name": "deleteOne", + "arguments": { + "filter": { + "encryptedDouble": { + "$gt": { + "$numberDouble": "0" + } + } + } + }, + "result": { + "deletedCount": 1 + } + } + ], + "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": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDouble": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDouble", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDouble": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDouble", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "delete": "default", + "deletes": [ + { + "q": { + "encryptedDouble": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ], + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDouble", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + }, + "deleteTokens": { + "default.default": { + "encryptedDouble": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + } + }, + "command_name": "delete" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDouble": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Double-Delete.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-Double-Delete.yml new file mode 100644 index 00000000000..5fc9680ae07 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Double-Delete.yml @@ -0,0 +1,521 @@ +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDouble', 'bsonType': 'double', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "FLE2 Range Double. Delete." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDouble: { $numberDouble: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDouble: { $numberDouble: "1" } } + - name: deleteOne + arguments: + filter: { "encryptedDouble": { $gt: {$numberDouble: "0" }} } + result: + deletedCount: 1 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDouble": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDouble": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + delete: *collection_name + deletes: [ + { + "q": { + "encryptedDouble": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ] + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + "deleteTokens": { + "default.default": { + "encryptedDouble": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + command_name: delete + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDouble": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", + "subType": "00" + } + } + ] + } \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Double-FindOneAndUpdate.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-Double-FindOneAndUpdate.json new file mode 100644 index 00000000000..59a304166bd --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Double-FindOneAndUpdate.json @@ -0,0 +1,1160 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDouble", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Double. FindOneAndUpdate.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDouble": { + "$numberDouble": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDouble": { + "$numberDouble": "1" + } + } + } + }, + { + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "encryptedDouble": { + "$gt": { + "$numberDouble": "0" + } + } + }, + "update": { + "$set": { + "encryptedDouble": { + "$numberDouble": "2" + } + } + }, + "returnDocument": "Before" + }, + "result": { + "_id": 1, + "encryptedDouble": { + "$numberDouble": "1" + } + } + } + ], + "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": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDouble": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDouble", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDouble": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDouble", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "findAndModify": "default", + "query": { + "encryptedDouble": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "update": { + "$set": { + "encryptedDouble": { + "$$type": "binData" + } + } + }, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDouble", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + }, + "deleteTokens": { + "default.default": { + "encryptedDouble": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + } + }, + "command_name": "findAndModify" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDouble": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedDouble": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "HI88j1zrIsFoijIXKybr9mYubNV5uVeODyLHFH4Ueco=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wXVD/HSbBljko0jJcaxJ1nrzs2+pchLQqYR3vywS8SU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KhscCh+tt/pp8lxtKZQSPPUU94RvJYPKG/sjtzIa4Ws=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RISnuNrTTVNW5HnwCgQJ301pFw8DOcYrAMQIwVwjOkI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Ra5zukLh2boua0Bh74qA+mtIoixGXlsNsxiJqHtqdTI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "eqr0v+NNWXWszi9ni8qH58Q6gw5x737tJvH3lPaNHO4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d42QupriWIwGrFAquXNFi0ehEuidIbHLFZtg1Sm2nN8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2azRVxaaTIJKcgY2FU012gcyP8Y05cRDpfUaMnCBaQU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "3nlgkM4K/AAcHesRYYdEu24UGetHodVnVfHzw4yxZBM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hqy91FNmAAac2zUaPO6eWFkx0/37rOWGrwXN+fzL0tU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "akX+fmscSDSF9pB5MPj56iaJPtohr0hfXNk/OPWsGv8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1ZvUb10Q7cN4cNLktd5yNjqgtawsYnkbeVBZV6WuY/I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "otCwtuKiY4hCyXvYzXvo10OcnzZppebo38KsAlq49QM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Mty8EscckeT/dhMfrPFyDbLnmMOcYRUQ3mLK4KTu6V8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tnvgLLkJINO7csREYu4dEVe1ICrBeu7OP+HdfoX3M2E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kOefsHgEVhkJ17UuP7Dxogy6sAQbzf1SFPKCj6XRlrQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "F+JQ79xavpaHdJzdhvwyHbzdZJLNHAymc/+67La3gao=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NCZ9zp5rDRceENuSgAfTLEyKg0YgmXAhK0B8WSj7+Pw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wL1CJ7cYR5slx8mHq++uMdjDfkt9037lQTUztEMF56M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "txefkzTMITZE+XvvRFZ7QcgwDT/7m8jNmxRk4QBaoZI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jFunW3v1tSYMyZtQQD28eEy9qqDp4Kqo7gMN29N4bfQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QMO915KUiS3X3R1bU1YoafVM2s0NeHo3EjgTA9PnGwY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "nwdKJEXdilzvb7494vbuDJ+y6SrfJahza1dYIsHIWVI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vpWMX+T/VXXajFo0UbuYjtp0AEzBU0Y+lP+ih2EQ7mg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1lmzG0J1DhKDRhhq5y5Buygu4G8eV2X0t7kUY90EohM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SiKqpXqO0trwhFvBWK274hMklpCgMhNs/JY84yyn/NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7cPGPYCKPTay+ZR9Gx6oOueduOgaFrSuAXmNDpDHXdI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4THEYvAkjs2Fh7FIe5LC45P4i4N0L7ob67UOVbhp6Nk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "B+UGsChLLZR7iqnt8yq91OgmTgwiUKTJhFxY4NT0O6c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X1uYwBCsCg1H+PnKdwtBqXlt0zKEURi8bOM940GcPfk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xYOgT5l7shlNXCwHlguovmDkcEnF8dXyYlTyYrgZ8GE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vFMTZqV8bh1+gcKzTkXweMddJlgdUnwX0DWzUUaMok4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4HI0y9FrtleZxZ7M6INdNhLelrQ2Rv/+ykWCBl+tMC8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jpJ0bBE474OUkn1vUiLWumIBtYmwc7J5+LQU/nyeLQc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jQTPeXZvdxY/DjtPfYfKUArIDsf0E9MVFy2O26sv1ec=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QLLto0ExR2ZYMGqlyaMZc/hXFFTlwmgtKbiVq/xJIeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yBJNviU1nchbGbhx6InXCVRXa90sEepz1EwbYuKXu2U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jpEf0vHxrPu9gTJutNXSi2g/2Mc4WXFEN7yHonZEb7A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "E09kLFckMYwNuhggMxmPtwndyvIAx+Vl+b2CV6FP75s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "N+ue6/cLPb5NssmJCCeo18LlbKPz6r2z20AsnTKRvOo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yVQNZP8hhsvNGyDph2QP2qTNdXZTiIEVineKg+Qf33o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cSC9uI+9c5S8X+0G7amVyug1p0ZlgBsbEDYYyezBevQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1NpZGjoQzuQtekj80Rifxe9HbE08W07dfwxaFHaVn84=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "5Ghuq/8l11Ug9Uf/RTwf9On3OxOwIXUcb9soiy4J7/w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0LWKaEty6ywxLFhDaAqulqfMnYc+tgPfH4apyEeKg80=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OwSthmCBtt6NIAoAh7aCbj82Yr/+9t8U7WuBQhFT3AQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "iYiyg6/1isqbMdvFPIGucu3cNM4NAZNtJhHpGZ4eM+c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "waBgs8jWuGJPIF5zCRh6OmIyfK5GCBQgTMfmKSR2wyY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1Jdtbe2BKJXPU2G9ywOrlODZ/cNYEQlKzAW3aMe1Hy4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xaLEnNUS/2ySerBpb9dN/D31t+wYcKekwTfkwtni0Mc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "bIVBrOhOvr6cL55Tr24+B+CC9MiG7U6K54aAr2IXXuw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6Cdq5wroGu2TEFnekuT7LhOpd/K/+PcipIljcHU9QL4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "K5l64vI4S/pLviLW6Pl0U3iQkI3ge0xg4RAHcEsyKJo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "bzhuvZ0Ls22yIOX+Hz51eAHlSuDbWR/e0u4EhfdpHbc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Qv+fr6uD4o0bZRp69QJCFL6zvn3G82c7L+N1IFzj7H0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XAmISMbD3aEyQT+BQEphCKFNa0F0GDKFuhM9cGceKoQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4VLCokntMfm1AogpUnYGvhV7nllWSo3mS3hVESMy+hA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xiXNLj/CipEH63Vb5cidi8q9X47EF4f3HtJSOH7mfM8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4XlCYfYBjI9XA5zOSgTiEBYcZsdwyXL+f5XtH2xUIOc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "k6DfQy7ZYJIkEly2B5hjOZznL4NcgMkllZjJLb7yq7w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ZzM6gwWesa3lxbZVZthpPFs2s3GV0RZREE2zOMhBRBo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "US+jeMeeOd7J0wR0efJtq2/18lcO8YFvhT4O3DeaonQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "b6iSxiI1FM9SzxuG1bHqGA1i4+3GOi0/SPW00XB4L7o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kn3LsxAVkzIZKK9I6fi0Cctr0yjXOYgaQWMCoj4hLpM=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Double-FindOneAndUpdate.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-Double-FindOneAndUpdate.yml new file mode 100644 index 00000000000..0b3d39dffa0 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Double-FindOneAndUpdate.yml @@ -0,0 +1,915 @@ +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDouble', 'bsonType': 'double', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "FLE2 Range Double. FindOneAndUpdate." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDouble: { $numberDouble: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDouble: { $numberDouble: "1" } } + - name: findOneAndUpdate + arguments: + filter: { encryptedDouble: { $gt: {$numberDouble: "0"}} } + update: { "$set": { "encryptedDouble": {$numberDouble: "2"}}} + returnDocument: Before + result: *doc1 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDouble": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDouble": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + findAndModify: *collection_name + query: { + "encryptedDouble": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + } + update: { "$set": {"encryptedDouble": { $$type: "binData" }} } + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + "deleteTokens": { + "default.default": { + "encryptedDouble": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + command_name: findAndModify + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDouble": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", + "subType": "00" + } + } + ] + } + - + { + "_id": 1, + "encryptedDouble": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "HI88j1zrIsFoijIXKybr9mYubNV5uVeODyLHFH4Ueco=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wXVD/HSbBljko0jJcaxJ1nrzs2+pchLQqYR3vywS8SU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KhscCh+tt/pp8lxtKZQSPPUU94RvJYPKG/sjtzIa4Ws=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RISnuNrTTVNW5HnwCgQJ301pFw8DOcYrAMQIwVwjOkI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Ra5zukLh2boua0Bh74qA+mtIoixGXlsNsxiJqHtqdTI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "eqr0v+NNWXWszi9ni8qH58Q6gw5x737tJvH3lPaNHO4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d42QupriWIwGrFAquXNFi0ehEuidIbHLFZtg1Sm2nN8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2azRVxaaTIJKcgY2FU012gcyP8Y05cRDpfUaMnCBaQU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "3nlgkM4K/AAcHesRYYdEu24UGetHodVnVfHzw4yxZBM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hqy91FNmAAac2zUaPO6eWFkx0/37rOWGrwXN+fzL0tU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "akX+fmscSDSF9pB5MPj56iaJPtohr0hfXNk/OPWsGv8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1ZvUb10Q7cN4cNLktd5yNjqgtawsYnkbeVBZV6WuY/I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "otCwtuKiY4hCyXvYzXvo10OcnzZppebo38KsAlq49QM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Mty8EscckeT/dhMfrPFyDbLnmMOcYRUQ3mLK4KTu6V8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tnvgLLkJINO7csREYu4dEVe1ICrBeu7OP+HdfoX3M2E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kOefsHgEVhkJ17UuP7Dxogy6sAQbzf1SFPKCj6XRlrQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "F+JQ79xavpaHdJzdhvwyHbzdZJLNHAymc/+67La3gao=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NCZ9zp5rDRceENuSgAfTLEyKg0YgmXAhK0B8WSj7+Pw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wL1CJ7cYR5slx8mHq++uMdjDfkt9037lQTUztEMF56M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "txefkzTMITZE+XvvRFZ7QcgwDT/7m8jNmxRk4QBaoZI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jFunW3v1tSYMyZtQQD28eEy9qqDp4Kqo7gMN29N4bfQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QMO915KUiS3X3R1bU1YoafVM2s0NeHo3EjgTA9PnGwY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "nwdKJEXdilzvb7494vbuDJ+y6SrfJahza1dYIsHIWVI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vpWMX+T/VXXajFo0UbuYjtp0AEzBU0Y+lP+ih2EQ7mg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1lmzG0J1DhKDRhhq5y5Buygu4G8eV2X0t7kUY90EohM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SiKqpXqO0trwhFvBWK274hMklpCgMhNs/JY84yyn/NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7cPGPYCKPTay+ZR9Gx6oOueduOgaFrSuAXmNDpDHXdI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4THEYvAkjs2Fh7FIe5LC45P4i4N0L7ob67UOVbhp6Nk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "B+UGsChLLZR7iqnt8yq91OgmTgwiUKTJhFxY4NT0O6c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X1uYwBCsCg1H+PnKdwtBqXlt0zKEURi8bOM940GcPfk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xYOgT5l7shlNXCwHlguovmDkcEnF8dXyYlTyYrgZ8GE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vFMTZqV8bh1+gcKzTkXweMddJlgdUnwX0DWzUUaMok4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4HI0y9FrtleZxZ7M6INdNhLelrQ2Rv/+ykWCBl+tMC8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jpJ0bBE474OUkn1vUiLWumIBtYmwc7J5+LQU/nyeLQc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jQTPeXZvdxY/DjtPfYfKUArIDsf0E9MVFy2O26sv1ec=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QLLto0ExR2ZYMGqlyaMZc/hXFFTlwmgtKbiVq/xJIeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yBJNviU1nchbGbhx6InXCVRXa90sEepz1EwbYuKXu2U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jpEf0vHxrPu9gTJutNXSi2g/2Mc4WXFEN7yHonZEb7A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "E09kLFckMYwNuhggMxmPtwndyvIAx+Vl+b2CV6FP75s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "N+ue6/cLPb5NssmJCCeo18LlbKPz6r2z20AsnTKRvOo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yVQNZP8hhsvNGyDph2QP2qTNdXZTiIEVineKg+Qf33o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cSC9uI+9c5S8X+0G7amVyug1p0ZlgBsbEDYYyezBevQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1NpZGjoQzuQtekj80Rifxe9HbE08W07dfwxaFHaVn84=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "5Ghuq/8l11Ug9Uf/RTwf9On3OxOwIXUcb9soiy4J7/w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0LWKaEty6ywxLFhDaAqulqfMnYc+tgPfH4apyEeKg80=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OwSthmCBtt6NIAoAh7aCbj82Yr/+9t8U7WuBQhFT3AQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "iYiyg6/1isqbMdvFPIGucu3cNM4NAZNtJhHpGZ4eM+c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "waBgs8jWuGJPIF5zCRh6OmIyfK5GCBQgTMfmKSR2wyY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1Jdtbe2BKJXPU2G9ywOrlODZ/cNYEQlKzAW3aMe1Hy4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xaLEnNUS/2ySerBpb9dN/D31t+wYcKekwTfkwtni0Mc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "bIVBrOhOvr6cL55Tr24+B+CC9MiG7U6K54aAr2IXXuw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6Cdq5wroGu2TEFnekuT7LhOpd/K/+PcipIljcHU9QL4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "K5l64vI4S/pLviLW6Pl0U3iQkI3ge0xg4RAHcEsyKJo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "bzhuvZ0Ls22yIOX+Hz51eAHlSuDbWR/e0u4EhfdpHbc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Qv+fr6uD4o0bZRp69QJCFL6zvn3G82c7L+N1IFzj7H0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XAmISMbD3aEyQT+BQEphCKFNa0F0GDKFuhM9cGceKoQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4VLCokntMfm1AogpUnYGvhV7nllWSo3mS3hVESMy+hA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xiXNLj/CipEH63Vb5cidi8q9X47EF4f3HtJSOH7mfM8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4XlCYfYBjI9XA5zOSgTiEBYcZsdwyXL+f5XtH2xUIOc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "k6DfQy7ZYJIkEly2B5hjOZznL4NcgMkllZjJLb7yq7w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ZzM6gwWesa3lxbZVZthpPFs2s3GV0RZREE2zOMhBRBo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "US+jeMeeOd7J0wR0efJtq2/18lcO8YFvhT4O3DeaonQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "b6iSxiI1FM9SzxuG1bHqGA1i4+3GOi0/SPW00XB4L7o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kn3LsxAVkzIZKK9I6fi0Cctr0yjXOYgaQWMCoj4hLpM=", + "subType": "00" + } + } + ] + } \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Double-InsertFind.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-Double-InsertFind.json new file mode 100644 index 00000000000..634230eacad --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Double-InsertFind.json @@ -0,0 +1,1129 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDouble", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Double. Insert and Find.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDouble": { + "$numberDouble": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDouble": { + "$numberDouble": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDouble": { + "$gt": { + "$numberDouble": "0" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDouble": { + "$numberDouble": "1" + } + } + ] + } + ], + "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": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDouble": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDouble", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDouble": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDouble", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "encryptedDouble": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDouble", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDouble": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedDouble": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2FIZh/9N+NeJEQwxYIX5ikQT85xJzulBNReXk8PnG/s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I93Md7QNPGmEEGYU1+VVCqBPBEvXdqHPtTJtMOn06Yk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "GecBFQ1PemlECWZWCl7f74vmsL6eB6mzQ9n6tK6FYfs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QpjhZl+O1ORifgtCZuWAdcP6OKL7IZ2cA46v8FJcV28=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FWXI/yZ1M+2fIboeMCDMlp+I2NwPQDtoM/wWselOPYw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uk26nvN/LdRLaBphiBgIZzT0sSpoO1z0RdDWRm/xrSA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hiiYSH1KZovAULc7rlmEU74wCjzDR+mm6ZnsgvFQjMw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hRzvMvWPX0sJme+wck67lwbKDFaWOa+Eyef+JSdc1s4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PSx5D+zqC9c295dguX4+EobT4IEzfffdfjzC8DWpB5Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QzfXQCVTjPQv2h21v95HYPq8uCsVJ2tPnjv79gAaM9M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XcGDO/dlTcEMLqwcm55UmOqK+KpBmbzZO1LIzX7GPaQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Lf+o4E7YB5ynzUPC6KTyW0lj6Cg9oLIu1Sdd1ODHctA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wAuVn02LAVo5Y+TUocvkoenFYWzpu38k0NmGZOsAjS4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yJGDtveLbbo/0HtCtiTSsvVI/0agg/U1bFaQ0yhK12o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KsEy0zgYcmkM+O/fWF9z3aJGIk22XCk+Aw96HB6JU68=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "p+AnMI5ZxdJMSIEJmXXya+FeH5yubmOdViwUO89j0Rc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/jLix56jzeywBtNuGw55lCXyebQoSIhbful0hOKxKDY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fvDvSPomtJsl1S3+8/tzFCE8scHIdJY5hB9CdTEsoFo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "oV5hOJzPXxfTuRdKIlF4uYEoMDuqH+G7/3qgndDr0PM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "3ALwcvLj3VOfgD6OqXAO13h1ZkOv46R6+Oy6SUKh53I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gxaB9FJj0IM+InhvAjwWaex3UIZ9SAnDiUd5WHSY/l0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "66NPvDygJzKJqddfNuDuNOpvGajjFRtvhkwfUkiYmXw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1dWcQIocRAcO9XnXYqbhl83jc0RgjQpsrWd8dC27trg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "npos0Uf1DT3ztSCjPVY9EImlRnTHB1KLrvmVSqBQ/8E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "TEI9qBx/tK1l1H0v1scMG8Srmtwo5VxWHADPBSlWrXk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "3wUN2ypQKoj+5ASkeIK9ycxhahVxyTmGopigoUAlyYs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o/oksSnUS+nIq6ozWTbB5bJh+NoaPj8deAA23uxiWCk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KExYPruhA31e8xuSwvfUfDcyY/H2Va6taUd0k4yFgLc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/x+dNfxdd/lkx8Z8VZVfoYl7LPoaZ/iKEzZXBrAtIJc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DE4cmjFLPqZlmRomO0qQiruUBtzoCe8ZdNRcfNH92pU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "M6EKNcLPw/iojAChgYUSieaBYWcbsjKtB94SaHOr8vk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+qP49lDPeyhaduTvXJgtJEqHNEYANVu9Bg3Bxz7Td9w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ruMrC2VIS+VKbJwCFb3bfkaLTju9nE+yPONV9s0M0Vo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EbjDlSB5JKnDKff4d8hOmaOwJ7B9Q6NQFisLj+DPC+0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "C/yYOTB94edyqAbiQNu8/H7FoG3yRRjHDkMykz4+Mv0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CBxqrejG+qQQq2YTd6iP/06kiu2CxxzBFaZK3Ofb1CM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2ZOQ/fpho+AbDENWBZaln7wRoepIRdhyT648dr8O5cU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EghIgEPz01+myPgj8oid+PgncvobvC7vjvG3THEEQ0M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "92CysZYNF8riwAMhdrIPKxfODw9p07cKQy/Snn8XmVY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VO0LeTBQmsEf7sCHzTnZwUPNTqRZ49R8V5E9XnZ/5N4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "exs8BQMJq7U6ZXYgIizT7XN+X/hOmmn4YEuzev9zgSI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qHpS4k1I+gPniNp4CA8TY8lLN36vBYmgbKMFpbYMEqg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+7lWKCKAWFw6gPZdHE6E8KIfI14/fSvtWUmllb5WLi0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YiH/US0q6679hWblFDDKNqUjCgggoU8sUCssTIF1QbU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YgwkKElEubNfvXL9hJxzqQUQtHiXN/OCGxNL1MUZZlM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hZFST4INZTTuhvJlGJeMwlUAK270UCOTCDeBAnN4a7g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "24I1Zw35AuGnK3CqJhbCwYb0IPuu5sCRrM5iyeITOLc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vgD12JB4Q1S/kGPSQ1KOgp386KnG1GbM/5+60oRGcGw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+wNE+OL+CB9d4AUJdVxd56jUJCAXmmk9fapuB2TAc4g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uhQh1B2Pe4RkNw/kPEcgaLenuikKoRf1iyfZhpXdodc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "eu8gjAUIp8ybO204AgeOq5v1neI1yljqy5v3I6lo1lM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7QG6oVbASBAjrnCPxzzUNnuFSFNlKhbuBafkF8pr7Is=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PUS1xb2oHSDTdYltutoSSxBiJ1NjxH3l2kA4P1CZLEs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XPMh/JDC/O93gJJCwwgJDb8ssWZvRvezNmKmyn3nIfk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jWz+KGwMk/GOvFAK2rOxF3OjxeZAWfmUQ1HGJ7icw4A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o7XbW68pc6flYigf3LW4WAGUWxpeqxaQLkHUhUR9RZ8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "nqR+g60+5U0okbqJadSqGgnC+j1JcP8rwMcfzOs2ACI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Hz43qVK95tSfbYFtaE/8fE97XMk1RiO8XpWjwZHB80o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "noZUWlZ8M6KXU5rkifyo8/duw5IL7/fXbJvT7bNmW9k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WONVHCuPSanXDRQQ/3tmyJ0Vq+Lu/4hRaMUf0g0kSuw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UEaj6vQRoIghE8Movd8AGXhtwIOXlP4cBsECIUvE5Y8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "D3n2YcO8+PB4C8brDo7kxKjF9Y844rVkdRMLTgsQkrw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "C+YA0G9KjxZVaWwOMuh/dcnHnHAlYnbFrRl0IEpmsY0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rUnmbmQanxrbFPYYrwyQ53x66OSt27yAvF+s48ezKDc=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Double-InsertFind.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-Double-InsertFind.yml new file mode 100644 index 00000000000..87f3f05d0b0 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Double-InsertFind.yml @@ -0,0 +1,893 @@ +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDouble', 'bsonType': 'double', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "FLE2 Range Double. Insert and Find." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDouble: { $numberDouble: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDouble: { $numberDouble: "1" } } + - name: find + arguments: + filter: { encryptedDouble: { $gt: { $numberDouble: "0" } } } + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDouble": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDouble": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: + "encryptedDouble": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDouble": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", + "subType": "00" + } + } + ] + } + - + { + "_id": 1, + "encryptedDouble": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2FIZh/9N+NeJEQwxYIX5ikQT85xJzulBNReXk8PnG/s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I93Md7QNPGmEEGYU1+VVCqBPBEvXdqHPtTJtMOn06Yk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "GecBFQ1PemlECWZWCl7f74vmsL6eB6mzQ9n6tK6FYfs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QpjhZl+O1ORifgtCZuWAdcP6OKL7IZ2cA46v8FJcV28=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FWXI/yZ1M+2fIboeMCDMlp+I2NwPQDtoM/wWselOPYw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uk26nvN/LdRLaBphiBgIZzT0sSpoO1z0RdDWRm/xrSA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hiiYSH1KZovAULc7rlmEU74wCjzDR+mm6ZnsgvFQjMw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hRzvMvWPX0sJme+wck67lwbKDFaWOa+Eyef+JSdc1s4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PSx5D+zqC9c295dguX4+EobT4IEzfffdfjzC8DWpB5Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QzfXQCVTjPQv2h21v95HYPq8uCsVJ2tPnjv79gAaM9M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XcGDO/dlTcEMLqwcm55UmOqK+KpBmbzZO1LIzX7GPaQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Lf+o4E7YB5ynzUPC6KTyW0lj6Cg9oLIu1Sdd1ODHctA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wAuVn02LAVo5Y+TUocvkoenFYWzpu38k0NmGZOsAjS4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yJGDtveLbbo/0HtCtiTSsvVI/0agg/U1bFaQ0yhK12o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KsEy0zgYcmkM+O/fWF9z3aJGIk22XCk+Aw96HB6JU68=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "p+AnMI5ZxdJMSIEJmXXya+FeH5yubmOdViwUO89j0Rc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/jLix56jzeywBtNuGw55lCXyebQoSIhbful0hOKxKDY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fvDvSPomtJsl1S3+8/tzFCE8scHIdJY5hB9CdTEsoFo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "oV5hOJzPXxfTuRdKIlF4uYEoMDuqH+G7/3qgndDr0PM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "3ALwcvLj3VOfgD6OqXAO13h1ZkOv46R6+Oy6SUKh53I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gxaB9FJj0IM+InhvAjwWaex3UIZ9SAnDiUd5WHSY/l0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "66NPvDygJzKJqddfNuDuNOpvGajjFRtvhkwfUkiYmXw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1dWcQIocRAcO9XnXYqbhl83jc0RgjQpsrWd8dC27trg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "npos0Uf1DT3ztSCjPVY9EImlRnTHB1KLrvmVSqBQ/8E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "TEI9qBx/tK1l1H0v1scMG8Srmtwo5VxWHADPBSlWrXk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "3wUN2ypQKoj+5ASkeIK9ycxhahVxyTmGopigoUAlyYs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o/oksSnUS+nIq6ozWTbB5bJh+NoaPj8deAA23uxiWCk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KExYPruhA31e8xuSwvfUfDcyY/H2Va6taUd0k4yFgLc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/x+dNfxdd/lkx8Z8VZVfoYl7LPoaZ/iKEzZXBrAtIJc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DE4cmjFLPqZlmRomO0qQiruUBtzoCe8ZdNRcfNH92pU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "M6EKNcLPw/iojAChgYUSieaBYWcbsjKtB94SaHOr8vk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+qP49lDPeyhaduTvXJgtJEqHNEYANVu9Bg3Bxz7Td9w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ruMrC2VIS+VKbJwCFb3bfkaLTju9nE+yPONV9s0M0Vo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EbjDlSB5JKnDKff4d8hOmaOwJ7B9Q6NQFisLj+DPC+0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "C/yYOTB94edyqAbiQNu8/H7FoG3yRRjHDkMykz4+Mv0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CBxqrejG+qQQq2YTd6iP/06kiu2CxxzBFaZK3Ofb1CM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2ZOQ/fpho+AbDENWBZaln7wRoepIRdhyT648dr8O5cU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EghIgEPz01+myPgj8oid+PgncvobvC7vjvG3THEEQ0M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "92CysZYNF8riwAMhdrIPKxfODw9p07cKQy/Snn8XmVY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VO0LeTBQmsEf7sCHzTnZwUPNTqRZ49R8V5E9XnZ/5N4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "exs8BQMJq7U6ZXYgIizT7XN+X/hOmmn4YEuzev9zgSI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qHpS4k1I+gPniNp4CA8TY8lLN36vBYmgbKMFpbYMEqg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+7lWKCKAWFw6gPZdHE6E8KIfI14/fSvtWUmllb5WLi0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YiH/US0q6679hWblFDDKNqUjCgggoU8sUCssTIF1QbU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YgwkKElEubNfvXL9hJxzqQUQtHiXN/OCGxNL1MUZZlM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hZFST4INZTTuhvJlGJeMwlUAK270UCOTCDeBAnN4a7g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "24I1Zw35AuGnK3CqJhbCwYb0IPuu5sCRrM5iyeITOLc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vgD12JB4Q1S/kGPSQ1KOgp386KnG1GbM/5+60oRGcGw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+wNE+OL+CB9d4AUJdVxd56jUJCAXmmk9fapuB2TAc4g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uhQh1B2Pe4RkNw/kPEcgaLenuikKoRf1iyfZhpXdodc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "eu8gjAUIp8ybO204AgeOq5v1neI1yljqy5v3I6lo1lM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7QG6oVbASBAjrnCPxzzUNnuFSFNlKhbuBafkF8pr7Is=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PUS1xb2oHSDTdYltutoSSxBiJ1NjxH3l2kA4P1CZLEs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XPMh/JDC/O93gJJCwwgJDb8ssWZvRvezNmKmyn3nIfk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jWz+KGwMk/GOvFAK2rOxF3OjxeZAWfmUQ1HGJ7icw4A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o7XbW68pc6flYigf3LW4WAGUWxpeqxaQLkHUhUR9RZ8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "nqR+g60+5U0okbqJadSqGgnC+j1JcP8rwMcfzOs2ACI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Hz43qVK95tSfbYFtaE/8fE97XMk1RiO8XpWjwZHB80o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "noZUWlZ8M6KXU5rkifyo8/duw5IL7/fXbJvT7bNmW9k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WONVHCuPSanXDRQQ/3tmyJ0Vq+Lu/4hRaMUf0g0kSuw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UEaj6vQRoIghE8Movd8AGXhtwIOXlP4cBsECIUvE5Y8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "D3n2YcO8+PB4C8brDo7kxKjF9Y844rVkdRMLTgsQkrw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "C+YA0G9KjxZVaWwOMuh/dcnHnHAlYnbFrRl0IEpmsY0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rUnmbmQanxrbFPYYrwyQ53x66OSt27yAvF+s48ezKDc=", + "subType": "00" + } + } + ] + } \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Double-Update.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-Double-Update.json new file mode 100644 index 00000000000..cdc9f28e76e --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Double-Update.json @@ -0,0 +1,1164 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDouble", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Double. Update.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDouble": { + "$numberDouble": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDouble": { + "$numberDouble": "1" + } + } + } + }, + { + "name": "updateOne", + "arguments": { + "filter": { + "encryptedDouble": { + "$gt": { + "$numberDouble": "0" + } + } + }, + "update": { + "$set": { + "encryptedDouble": { + "$numberDouble": "2" + } + } + } + }, + "result": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "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": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDouble": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDouble", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDouble": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDouble", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command_name": "update", + "command": { + "update": "default", + "ordered": true, + "updates": [ + { + "q": { + "encryptedDouble": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encryptedDouble": { + "$$type": "binData" + } + } + } + } + ], + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDouble", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + }, + "deleteTokens": { + "default.default": { + "encryptedDouble": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + }, + "$db": "default" + } + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDouble": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedDouble": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "HI88j1zrIsFoijIXKybr9mYubNV5uVeODyLHFH4Ueco=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wXVD/HSbBljko0jJcaxJ1nrzs2+pchLQqYR3vywS8SU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KhscCh+tt/pp8lxtKZQSPPUU94RvJYPKG/sjtzIa4Ws=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RISnuNrTTVNW5HnwCgQJ301pFw8DOcYrAMQIwVwjOkI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Ra5zukLh2boua0Bh74qA+mtIoixGXlsNsxiJqHtqdTI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "eqr0v+NNWXWszi9ni8qH58Q6gw5x737tJvH3lPaNHO4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d42QupriWIwGrFAquXNFi0ehEuidIbHLFZtg1Sm2nN8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2azRVxaaTIJKcgY2FU012gcyP8Y05cRDpfUaMnCBaQU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "3nlgkM4K/AAcHesRYYdEu24UGetHodVnVfHzw4yxZBM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hqy91FNmAAac2zUaPO6eWFkx0/37rOWGrwXN+fzL0tU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "akX+fmscSDSF9pB5MPj56iaJPtohr0hfXNk/OPWsGv8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1ZvUb10Q7cN4cNLktd5yNjqgtawsYnkbeVBZV6WuY/I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "otCwtuKiY4hCyXvYzXvo10OcnzZppebo38KsAlq49QM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Mty8EscckeT/dhMfrPFyDbLnmMOcYRUQ3mLK4KTu6V8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tnvgLLkJINO7csREYu4dEVe1ICrBeu7OP+HdfoX3M2E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kOefsHgEVhkJ17UuP7Dxogy6sAQbzf1SFPKCj6XRlrQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "F+JQ79xavpaHdJzdhvwyHbzdZJLNHAymc/+67La3gao=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NCZ9zp5rDRceENuSgAfTLEyKg0YgmXAhK0B8WSj7+Pw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wL1CJ7cYR5slx8mHq++uMdjDfkt9037lQTUztEMF56M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "txefkzTMITZE+XvvRFZ7QcgwDT/7m8jNmxRk4QBaoZI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jFunW3v1tSYMyZtQQD28eEy9qqDp4Kqo7gMN29N4bfQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QMO915KUiS3X3R1bU1YoafVM2s0NeHo3EjgTA9PnGwY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "nwdKJEXdilzvb7494vbuDJ+y6SrfJahza1dYIsHIWVI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vpWMX+T/VXXajFo0UbuYjtp0AEzBU0Y+lP+ih2EQ7mg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1lmzG0J1DhKDRhhq5y5Buygu4G8eV2X0t7kUY90EohM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SiKqpXqO0trwhFvBWK274hMklpCgMhNs/JY84yyn/NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7cPGPYCKPTay+ZR9Gx6oOueduOgaFrSuAXmNDpDHXdI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4THEYvAkjs2Fh7FIe5LC45P4i4N0L7ob67UOVbhp6Nk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "B+UGsChLLZR7iqnt8yq91OgmTgwiUKTJhFxY4NT0O6c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X1uYwBCsCg1H+PnKdwtBqXlt0zKEURi8bOM940GcPfk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xYOgT5l7shlNXCwHlguovmDkcEnF8dXyYlTyYrgZ8GE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vFMTZqV8bh1+gcKzTkXweMddJlgdUnwX0DWzUUaMok4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4HI0y9FrtleZxZ7M6INdNhLelrQ2Rv/+ykWCBl+tMC8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jpJ0bBE474OUkn1vUiLWumIBtYmwc7J5+LQU/nyeLQc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jQTPeXZvdxY/DjtPfYfKUArIDsf0E9MVFy2O26sv1ec=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QLLto0ExR2ZYMGqlyaMZc/hXFFTlwmgtKbiVq/xJIeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yBJNviU1nchbGbhx6InXCVRXa90sEepz1EwbYuKXu2U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jpEf0vHxrPu9gTJutNXSi2g/2Mc4WXFEN7yHonZEb7A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "E09kLFckMYwNuhggMxmPtwndyvIAx+Vl+b2CV6FP75s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "N+ue6/cLPb5NssmJCCeo18LlbKPz6r2z20AsnTKRvOo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yVQNZP8hhsvNGyDph2QP2qTNdXZTiIEVineKg+Qf33o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cSC9uI+9c5S8X+0G7amVyug1p0ZlgBsbEDYYyezBevQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1NpZGjoQzuQtekj80Rifxe9HbE08W07dfwxaFHaVn84=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "5Ghuq/8l11Ug9Uf/RTwf9On3OxOwIXUcb9soiy4J7/w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0LWKaEty6ywxLFhDaAqulqfMnYc+tgPfH4apyEeKg80=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OwSthmCBtt6NIAoAh7aCbj82Yr/+9t8U7WuBQhFT3AQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "iYiyg6/1isqbMdvFPIGucu3cNM4NAZNtJhHpGZ4eM+c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "waBgs8jWuGJPIF5zCRh6OmIyfK5GCBQgTMfmKSR2wyY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1Jdtbe2BKJXPU2G9ywOrlODZ/cNYEQlKzAW3aMe1Hy4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xaLEnNUS/2ySerBpb9dN/D31t+wYcKekwTfkwtni0Mc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "bIVBrOhOvr6cL55Tr24+B+CC9MiG7U6K54aAr2IXXuw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6Cdq5wroGu2TEFnekuT7LhOpd/K/+PcipIljcHU9QL4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "K5l64vI4S/pLviLW6Pl0U3iQkI3ge0xg4RAHcEsyKJo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "bzhuvZ0Ls22yIOX+Hz51eAHlSuDbWR/e0u4EhfdpHbc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Qv+fr6uD4o0bZRp69QJCFL6zvn3G82c7L+N1IFzj7H0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XAmISMbD3aEyQT+BQEphCKFNa0F0GDKFuhM9cGceKoQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4VLCokntMfm1AogpUnYGvhV7nllWSo3mS3hVESMy+hA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xiXNLj/CipEH63Vb5cidi8q9X47EF4f3HtJSOH7mfM8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4XlCYfYBjI9XA5zOSgTiEBYcZsdwyXL+f5XtH2xUIOc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "k6DfQy7ZYJIkEly2B5hjOZznL4NcgMkllZjJLb7yq7w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ZzM6gwWesa3lxbZVZthpPFs2s3GV0RZREE2zOMhBRBo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "US+jeMeeOd7J0wR0efJtq2/18lcO8YFvhT4O3DeaonQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "b6iSxiI1FM9SzxuG1bHqGA1i4+3GOi0/SPW00XB4L7o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kn3LsxAVkzIZKK9I6fi0Cctr0yjXOYgaQWMCoj4hLpM=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Double-Update.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-Double-Update.yml new file mode 100644 index 00000000000..5f8423b8fec --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Double-Update.yml @@ -0,0 +1,932 @@ +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDouble', 'bsonType': 'double', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "FLE2 Range Double. Update." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDouble: { $numberDouble: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDouble: { $numberDouble: "1" } } + - name: updateOne + arguments: + filter: { encryptedDouble: { $gt: { $numberDouble: "0" } } } + update: { "$set": { "encryptedDouble": { $numberDouble: "2" } }} + result: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDouble": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDouble": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command_name: update + command: + { + "update": "default", + "ordered": true, + "updates": [ + { + "q": { + "encryptedDouble": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encryptedDouble": { $$type: "binData" } + } + } + } + ], + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": *encrypted_fields + }, + "deleteTokens": { + "default.default": { + "encryptedDouble": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + }, + "$db": "default" + } + + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDouble": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", + "subType": "00" + } + } + ] + } + - + { + "_id": 1, + "encryptedDouble": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "HI88j1zrIsFoijIXKybr9mYubNV5uVeODyLHFH4Ueco=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wXVD/HSbBljko0jJcaxJ1nrzs2+pchLQqYR3vywS8SU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KhscCh+tt/pp8lxtKZQSPPUU94RvJYPKG/sjtzIa4Ws=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RISnuNrTTVNW5HnwCgQJ301pFw8DOcYrAMQIwVwjOkI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Ra5zukLh2boua0Bh74qA+mtIoixGXlsNsxiJqHtqdTI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "eqr0v+NNWXWszi9ni8qH58Q6gw5x737tJvH3lPaNHO4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d42QupriWIwGrFAquXNFi0ehEuidIbHLFZtg1Sm2nN8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2azRVxaaTIJKcgY2FU012gcyP8Y05cRDpfUaMnCBaQU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "3nlgkM4K/AAcHesRYYdEu24UGetHodVnVfHzw4yxZBM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hqy91FNmAAac2zUaPO6eWFkx0/37rOWGrwXN+fzL0tU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "akX+fmscSDSF9pB5MPj56iaJPtohr0hfXNk/OPWsGv8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1ZvUb10Q7cN4cNLktd5yNjqgtawsYnkbeVBZV6WuY/I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "otCwtuKiY4hCyXvYzXvo10OcnzZppebo38KsAlq49QM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Mty8EscckeT/dhMfrPFyDbLnmMOcYRUQ3mLK4KTu6V8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tnvgLLkJINO7csREYu4dEVe1ICrBeu7OP+HdfoX3M2E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kOefsHgEVhkJ17UuP7Dxogy6sAQbzf1SFPKCj6XRlrQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "F+JQ79xavpaHdJzdhvwyHbzdZJLNHAymc/+67La3gao=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NCZ9zp5rDRceENuSgAfTLEyKg0YgmXAhK0B8WSj7+Pw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wL1CJ7cYR5slx8mHq++uMdjDfkt9037lQTUztEMF56M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "txefkzTMITZE+XvvRFZ7QcgwDT/7m8jNmxRk4QBaoZI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jFunW3v1tSYMyZtQQD28eEy9qqDp4Kqo7gMN29N4bfQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QMO915KUiS3X3R1bU1YoafVM2s0NeHo3EjgTA9PnGwY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "nwdKJEXdilzvb7494vbuDJ+y6SrfJahza1dYIsHIWVI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vpWMX+T/VXXajFo0UbuYjtp0AEzBU0Y+lP+ih2EQ7mg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1lmzG0J1DhKDRhhq5y5Buygu4G8eV2X0t7kUY90EohM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SiKqpXqO0trwhFvBWK274hMklpCgMhNs/JY84yyn/NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7cPGPYCKPTay+ZR9Gx6oOueduOgaFrSuAXmNDpDHXdI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4THEYvAkjs2Fh7FIe5LC45P4i4N0L7ob67UOVbhp6Nk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "B+UGsChLLZR7iqnt8yq91OgmTgwiUKTJhFxY4NT0O6c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X1uYwBCsCg1H+PnKdwtBqXlt0zKEURi8bOM940GcPfk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xYOgT5l7shlNXCwHlguovmDkcEnF8dXyYlTyYrgZ8GE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vFMTZqV8bh1+gcKzTkXweMddJlgdUnwX0DWzUUaMok4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4HI0y9FrtleZxZ7M6INdNhLelrQ2Rv/+ykWCBl+tMC8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jpJ0bBE474OUkn1vUiLWumIBtYmwc7J5+LQU/nyeLQc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jQTPeXZvdxY/DjtPfYfKUArIDsf0E9MVFy2O26sv1ec=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QLLto0ExR2ZYMGqlyaMZc/hXFFTlwmgtKbiVq/xJIeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yBJNviU1nchbGbhx6InXCVRXa90sEepz1EwbYuKXu2U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jpEf0vHxrPu9gTJutNXSi2g/2Mc4WXFEN7yHonZEb7A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "E09kLFckMYwNuhggMxmPtwndyvIAx+Vl+b2CV6FP75s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "N+ue6/cLPb5NssmJCCeo18LlbKPz6r2z20AsnTKRvOo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yVQNZP8hhsvNGyDph2QP2qTNdXZTiIEVineKg+Qf33o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cSC9uI+9c5S8X+0G7amVyug1p0ZlgBsbEDYYyezBevQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1NpZGjoQzuQtekj80Rifxe9HbE08W07dfwxaFHaVn84=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "5Ghuq/8l11Ug9Uf/RTwf9On3OxOwIXUcb9soiy4J7/w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0LWKaEty6ywxLFhDaAqulqfMnYc+tgPfH4apyEeKg80=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OwSthmCBtt6NIAoAh7aCbj82Yr/+9t8U7WuBQhFT3AQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "iYiyg6/1isqbMdvFPIGucu3cNM4NAZNtJhHpGZ4eM+c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "waBgs8jWuGJPIF5zCRh6OmIyfK5GCBQgTMfmKSR2wyY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1Jdtbe2BKJXPU2G9ywOrlODZ/cNYEQlKzAW3aMe1Hy4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xaLEnNUS/2ySerBpb9dN/D31t+wYcKekwTfkwtni0Mc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "bIVBrOhOvr6cL55Tr24+B+CC9MiG7U6K54aAr2IXXuw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6Cdq5wroGu2TEFnekuT7LhOpd/K/+PcipIljcHU9QL4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "K5l64vI4S/pLviLW6Pl0U3iQkI3ge0xg4RAHcEsyKJo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "bzhuvZ0Ls22yIOX+Hz51eAHlSuDbWR/e0u4EhfdpHbc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Qv+fr6uD4o0bZRp69QJCFL6zvn3G82c7L+N1IFzj7H0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XAmISMbD3aEyQT+BQEphCKFNa0F0GDKFuhM9cGceKoQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4VLCokntMfm1AogpUnYGvhV7nllWSo3mS3hVESMy+hA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xiXNLj/CipEH63Vb5cidi8q9X47EF4f3HtJSOH7mfM8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4XlCYfYBjI9XA5zOSgTiEBYcZsdwyXL+f5XtH2xUIOc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "k6DfQy7ZYJIkEly2B5hjOZznL4NcgMkllZjJLb7yq7w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ZzM6gwWesa3lxbZVZthpPFs2s3GV0RZREE2zOMhBRBo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "US+jeMeeOd7J0wR0efJtq2/18lcO8YFvhT4O3DeaonQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "b6iSxiI1FM9SzxuG1bHqGA1i4+3GOi0/SPW00XB4L7o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kn3LsxAVkzIZKK9I6fi0Cctr0yjXOYgaQWMCoj4hLpM=", + "subType": "00" + } + } + ] + } \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-DoublePrecision-Aggregate.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-DoublePrecision-Aggregate.json new file mode 100644 index 00000000000..f2ea49ad75e --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-DoublePrecision-Aggregate.json @@ -0,0 +1,586 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range DoublePrecision. Aggregate.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$gt": { + "$numberDouble": "0" + } + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1" + } + } + ] + } + ], + "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": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDoublePrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "default", + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + } + } + ], + "cursor": {}, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "aggregate" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedDoublePrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "mVZb+Ra0EYjQ4Zrh9X//E2T8MRj7NMqm5GUJXhRrBEI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MgwakFvPyBlwqFTbhWUF79URJQWFoJTGotlEVSPPUsQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DyBERpMSD5lEM5Nhpcn4WGgxgn/mkUVJp+PYSLX5jsE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I43iazc0xj1WVbYB/V+uTL/tughN1bBlxh1iypBnNsA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wjOBa/ATMuOywFmuPgC0GF/oeLqu0Z7eK5udzkTPbis=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gRQVwiR+m+0Vg8ZDXqrQQcVnTyobwCXNaA4BCJVXtMc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WUZ6huwx0ZbLb0R00uiC9FOJzsUocUN8qE5+YRenkvQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7s79aKEuPgQcS/YPOOVcYNZvHIo7FFsWtFCrnDKXefA=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-DoublePrecision-Aggregate.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-DoublePrecision-Aggregate.yml new file mode 100644 index 00000000000..47ac5d44448 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-DoublePrecision-Aggregate.yml @@ -0,0 +1,311 @@ +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDoublePrecision', 'bsonType': 'double', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberDouble': '0.0'}, 'max': {'$numberDouble': '200.0'}, 'precision': {'$numberInt': '2'}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "FLE2 Range DoublePrecision. Aggregate." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDoublePrecision: { $numberDouble: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDoublePrecision: { $numberDouble: "1" } } + - name: aggregate + arguments: + pipeline: [{ $match: { "encryptedDoublePrecision": { $gt: {$numberDouble: "0" }} } }] + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + aggregate: *collection_name + pipeline: [ + { + "$match": { + "encryptedDoublePrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + } + } + ] + cursor: {} + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: aggregate + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDoublePrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", + "subType": "00" + } + } + ] + } + - + { + "_id": 1, + "encryptedDoublePrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "mVZb+Ra0EYjQ4Zrh9X//E2T8MRj7NMqm5GUJXhRrBEI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MgwakFvPyBlwqFTbhWUF79URJQWFoJTGotlEVSPPUsQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DyBERpMSD5lEM5Nhpcn4WGgxgn/mkUVJp+PYSLX5jsE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I43iazc0xj1WVbYB/V+uTL/tughN1bBlxh1iypBnNsA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wjOBa/ATMuOywFmuPgC0GF/oeLqu0Z7eK5udzkTPbis=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gRQVwiR+m+0Vg8ZDXqrQQcVnTyobwCXNaA4BCJVXtMc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WUZ6huwx0ZbLb0R00uiC9FOJzsUocUN8qE5+YRenkvQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7s79aKEuPgQcS/YPOOVcYNZvHIo7FFsWtFCrnDKXefA=", + "subType": "00" + } + } + ] + } \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-DoublePrecision-Correctness.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-DoublePrecision-Correctness.json new file mode 100644 index 00000000000..e69d9126941 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-DoublePrecision-Correctness.json @@ -0,0 +1,1650 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "Find with $gt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$gt": { + "$numberDouble": "0.0" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $gte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$gte": { + "$numberDouble": "0.0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + }, + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $gt with no results", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$gt": { + "$numberDouble": "1.0" + } + } + } + }, + "result": [] + } + ] + }, + { + "description": "Find with $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$lt": { + "$numberDouble": "1.0" + } + } + } + }, + "result": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + ] + } + ] + }, + { + "description": "Find with $lte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$lte": { + "$numberDouble": "1.0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + }, + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $lt below min", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$lt": { + "$numberDouble": "0.0" + } + } + } + }, + "result": { + "errorContains": "must be greater than the range minimum" + } + } + ] + }, + { + "description": "Find with $gt above max", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$gt": { + "$numberDouble": "200.0" + } + } + } + }, + "result": { + "errorContains": "must be less than the range max" + } + } + ] + }, + { + "description": "Find with $gt and $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$gt": { + "$numberDouble": "0.0" + }, + "$lt": { + "$numberDouble": "2.0" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with equality", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + }, + "result": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + ] + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with full range", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$gte": { + "$numberDouble": "0.0" + }, + "$lte": { + "$numberDouble": "200.0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + }, + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $in", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$in": [ + { + "$numberDouble": "0.0" + } + ] + } + } + }, + "result": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + ] + } + ] + }, + { + "description": "Insert out of range", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "-1" + } + } + }, + "result": { + "errorContains": "value must be greater than or equal to the minimum value" + } + } + ] + }, + { + "description": "Insert min and max", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 200, + "encryptedDoublePrecision": { + "$numberDouble": "200.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + }, + { + "_id": 200, + "encryptedDoublePrecision": { + "$numberDouble": "200.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$gte": { + "$numberDouble": "0.0" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + }, + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gt with no results", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$gt": { + "$numberDouble": "1.0" + } + } + } + } + ] + }, + "result": [] + } + ] + }, + { + "description": "Aggregate with $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$lt": { + "$numberDouble": "1.0" + } + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $lte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$lte": { + "$numberDouble": "1.0" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + }, + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $lt below min", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$lt": { + "$numberDouble": "0.0" + } + } + } + } + ] + }, + "result": { + "errorContains": "must be greater than the range minimum" + } + } + ] + }, + { + "description": "Aggregate with $gt above max", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$gt": { + "$numberDouble": "200.0" + } + } + } + } + ] + }, + "result": { + "errorContains": "must be less than the range max" + } + } + ] + }, + { + "description": "Aggregate with $gt and $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$gt": { + "$numberDouble": "0.0" + }, + "$lt": { + "$numberDouble": "2.0" + } + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with equality", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + ] + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with full range", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$gte": { + "$numberDouble": "0.0" + }, + "$lte": { + "$numberDouble": "200.0" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + }, + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $in", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$in": [ + { + "$numberDouble": "0.0" + } + ] + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + ] + } + ] + }, + { + "description": "Wrong type: Insert Int", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberInt": "0" + } + } + }, + "result": { + "errorContains": "cannot encrypt element" + } + } + ] + }, + { + "description": "Wrong type: Find Int", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$gte": { + "$numberInt": "0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": { + "errorContains": "field type is not supported" + } + } + ] + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-DoublePrecision-Correctness.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-DoublePrecision-Correctness.yml new file mode 100644 index 00000000000..291ffa58ab8 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-DoublePrecision-Correctness.yml @@ -0,0 +1,422 @@ +# Test correctness results. +# Does not include command monitoring expectations or outcome assertions to make tests more readable. +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDoublePrecision', 'bsonType': 'double', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberDouble': '0.0'}, 'max': {'$numberDouble': '200.0'}, 'precision': {'$numberInt': '2'}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "Find with $gt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDoublePrecision: { $numberDouble: "0.0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDoublePrecision: { $numberDouble: "1.0" } } + - name: find + arguments: + filter: { encryptedDoublePrecision: { $gt: { $numberDouble: "0.0" } }} + result: [*doc1] + + - description: "Find with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoublePrecision: { $gte: { $numberDouble: "0.0" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoublePrecision: { $gt: { $numberDouble: "1.0" } }} + result: [] + + - description: "Find with $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoublePrecision: { $lt: { $numberDouble: "1.0" } }} + result: [*doc0] + + - description: "Find with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoublePrecision: { $lte: { $numberDouble: "1.0" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $lt below min" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoublePrecision: { $lt: { $numberDouble: "0.0" } }} + result: + errorContains: must be greater than the range minimum + + - description: "Find with $gt above max" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoublePrecision: { $gt: { $numberDouble: "200.0" } }} + result: + errorContains: must be less than the range max + + - description: "Find with $gt and $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoublePrecision: { $gt: { $numberDouble: "0.0" }, $lt: { $numberDouble: "2.0"} }} + result: [*doc1] + + - description: "Find with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoublePrecision: { $numberDouble: "0.0" } } + result: [*doc0] + - name: find + arguments: + filter: { encryptedDoublePrecision: { $numberDouble: "1.0" } } + result: [*doc1] + + - description: "Find with full range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoublePrecision: { $gte: {$numberDouble: "0.0"}, $lte: {$numberDouble: "200.0"} } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoublePrecision: { $in: [ {$numberDouble: "0.0"} ] } } + result: [*doc0] + + - description: "Insert out of range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedDoublePrecision: { $numberDouble: "-1" }} + result: + errorContains: value must be greater than or equal to the minimum value + + - description: "Insert min and max" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: *doc0 + - name: insertOne + arguments: + document: &doc200 { _id: 200, encryptedDoublePrecision: { $numberDouble: "200.0" }} + - name: find + arguments: + filter: {} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc200] + + - description: "Aggregate with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $gte: { $numberDouble: "0.0" } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $gt: { $numberDouble: "1.0" } }} } + result: [] + + - description: "Aggregate with $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $lt: { $numberDouble: "1.0" } }} } + result: [*doc0] + + - description: "Aggregate with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $lte: { $numberDouble: "1.0" } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $lt below min" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $lt: { $numberDouble: "0.0" } }} } + result: + errorContains: must be greater than the range minimum + + - description: "Aggregate with $gt above max" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $gt: { $numberDouble: "200.0" } }} } + result: + errorContains: must be less than the range max + + - description: "Aggregate with $gt and $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $gt: { $numberDouble: "0.0" }, $lt: { $numberDouble: "2.0"} }} } + result: [*doc1] + + - description: "Aggregate with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $numberDouble: "0.0" } } } + result: [*doc0] + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $numberDouble: "1.0" } } } + result: [*doc1] + + - description: "Aggregate with full range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $gte: {$numberDouble: "0.0"}, $lte: {$numberDouble: "200.0"} } } } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $in: [ {$numberDouble: "0.0"} ] } } } + result: [*doc0] + + - description: "Wrong type: Insert Int" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: { _id: 0, encryptedDoublePrecision: { $numberInt: "0" }} } + result: + # Expect an error from mongocryptd. + errorContains: "cannot encrypt element" + + - description: "Wrong type: Find Int" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: find + arguments: + filter: { encryptedDoublePrecision: { $gte: { $numberInt: "0" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: + # expect an error from libmongocrypt. + errorContains: "field type is not supported" \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-DoublePrecision-Delete.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-DoublePrecision-Delete.json new file mode 100644 index 00000000000..d6a9c4b7e7a --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-DoublePrecision-Delete.json @@ -0,0 +1,491 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range DoublePrecision. Delete.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1" + } + } + } + }, + { + "name": "deleteOne", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$gt": { + "$numberDouble": "0" + } + } + } + }, + "result": { + "deletedCount": 1 + } + } + ], + "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": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDoublePrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "delete": "default", + "deletes": [ + { + "q": { + "encryptedDoublePrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ], + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + }, + "deleteTokens": { + "default.default": { + "encryptedDoublePrecision": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + } + }, + "command_name": "delete" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-DoublePrecision-Delete.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-DoublePrecision-Delete.yml new file mode 100644 index 00000000000..dd8b8624712 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-DoublePrecision-Delete.yml @@ -0,0 +1,227 @@ +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDoublePrecision', 'bsonType': 'double', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberDouble': '0.0'}, 'max': {'$numberDouble': '200.0'}, 'precision': {'$numberInt': '2'}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "FLE2 Range DoublePrecision. Delete." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDoublePrecision: { $numberDouble: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDoublePrecision: { $numberDouble: "1" } } + - name: deleteOne + arguments: + filter: { "encryptedDoublePrecision": { $gt: {$numberDouble: "0" }} } + result: + deletedCount: 1 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + delete: *collection_name + deletes: [ + { + "q": { + "encryptedDoublePrecision": { + "$gt": { + "$binary": { + "base64": "CvoJAAADcGF5bG9hZADKCQAABGcAjgkAAAMwAH0AAAAFZAAgAAAAAHdJ2Vnb4MMzqVYVssjSdDy8XU4GVzMTfGifGETgQ2mYBXMAIAAAAAD7cFfKJGIXo6PjyeX2ria02CckW7dWFDoY/3FyBdm1NQVjACAAAAAAqZO+/+gRWlPaMOvuiXizSmBe7lp1VWg1vJ4UmW8o3bQAAzEAfQAAAAVkACAAAAAAlmvfDrZoydUet4eCVMq7z6a58Ea+1HLJOWxN5lNcrWEFcwAgAAAAAEBo5AWZyC41b9ayjWNQSL4iYEAIwR/JG+ssN8bdoK9RBWMAIAAAAAD4FTKJ6CTzKBAyAwZCLUoDEfnZTRZmhF1q/2hnDzmG9gADMgB9AAAABWQAIAAAAABbiLaoxAA6rinMJw1hC8ZUiq6UU1AQaPFn/py/Y06WuQVzACAAAAAAhtDasFkvYE7SCNu1je/hxdE9TJtAvvH3NtdEbKzNbCUFYwAgAAAAAHHy019aPatHTST+0wGsmukUcsQNQj6KpoS9b7iGeThAAAMzAH0AAAAFZAAgAAAAALORWwSr+tYNxcil2KIGSbNhTHvcPbdj+rLVQNx21S/KBXMAIAAAAAD6diZBkPEJ1cQy06LAxdbNK8Nlxbb44fH4Wk3Y3260nQVjACAAAAAAvUc1q7pyhjU0ilgmwiKkHIY3V4/LxO+Y2uT7eSpBOs8AAzQAfQAAAAVkACAAAAAAG5XMK96PjClNlUvg82j4pMY1YxsznZfj4uNweD394FoFcwAgAAAAAKHgQLdGJHkrfFg9nB93Ac+3VgBw6aU44MTkKIQ91dZoBWMAIAAAAACtbNc1DCoUUyzlkrYmJi4NlwOqLYmb6au4pDc8clXVXwADNQB9AAAABWQAIAAAAAA6M++b9I0YFemmWBAWAE3glu2Ah3Ta1FBxAQEIWS0toAVzACAAAAAANXYTqPf1Y6X3Ns6YQIX0C3FKCyWUo+Kk+fNcQvc0WSoFYwAgAAAAAAaqju6Dv8wqXxcsIbP67V1QGaD5kNTFofZ9Zuf1LGnKAAM2AH0AAAAFZAAgAAAAAKl8bV1riH/uyJ+X0HHd3+18k2cJl2dQFXCdoagutFcaBXMAIAAAAABm8F2Ew9f0VOABdcF+lP0Bi+zWvEUPniWgrxPq/Sx3uwVjACAAAAAAQd2pWVqlmmLg8m8xbs7yLewmR0Z6UQgXofbCsMHaGSoAAzcAfQAAAAVkACAAAAAAsYZD8JEP6kYsPncFnNZwJxhu4YtUTKPNcjHtv67H+rYFcwAgAAAAAI4LqZcRkvbs/2F62Flu0pixNcor4WmBD0DHGaf039wLBWMAIAAAAAAqzpfyBpr4Ano+nFWJyyTuIJelJgiRDnMHQqdeqV8JaAADOAB9AAAABWQAIAAAAAC0vdAi+dmoIXvZ5LqUqvyKV9/tHqSI2SWiSJO5pTnA2wVzACAAAAAAS2qvf9fvfVUH5WtsVxjxmskpGjYTQV34LwvQQw1y9wIFYwAgAAAAAFhNY4qwNntyA+GIoNHZsTkIUbPgy4TBlvNnTPjp4bMFAAM5AH0AAAAFZAAgAAAAAH31lb/srBcrOXkzddCwAnclsR5/3QijEVgECs2JjOWBBXMAIAAAAABg7+prDT73YcCvLE5QbuIrqGcjLc5pQD2Miq0d29yrxgVjACAAAAAAFKqAqXG/ktejFQ7fM2aobO2VmEvZLXnRaJH97Jy/sJYAAzEwAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVjACAAAAAAfsofSP7nQHv8ic8ZW0aNlWxplS46Z+mywPR4rQk+wcgAAzExAH0AAAAFZAAgAAAAAFdthRhe2Q8CvxGIhjTJZv0Lk97GkHciTPxZ/mckLoNaBXMAIAAAAAAqOxsAr23LOVB0DIHbPf9UDJJRFXY2YoKbjhRqw5psbQVjACAAAAAA7ty+Nif6KjS3v1zWKaHX9n4Zj3XC4ajuCduKNIYr3l8AAzEyAH0AAAAFZAAgAAAAAMWymwwbvIeMqmnKWWifUqoCxOsdpnonM2qdLPyjqJO/BXMAIAAAAAB6IDmmpUhBD2zpRj8/y/kmOSXcjuIU14sNh6GKSsg2uwVjACAAAAAABSWO0Ii+NGcsHZQ9MR5EjPXVKeXlI4FQ1pcxeKDiuooAAzEzAH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVjACAAAAAAD51NYesiO4Fo7w7iWBfqAFxEqkaDVctpvzZ28nT4SE8AAzE0AH0AAAAFZAAgAAAAAJaRYmo8zqI2BEUzdSwp4tVRpPmVWsfydkYN3UHh6TMuBXMAIAAAAAAeD6mDnQeLlbC9i0sVgE8+RH6y+e94OJQ0tJ0PvblVSgVjACAAAAAAKUhYSt4nvvUfbNgPJ2E79SciVZ0ZzbzoZ2nKr4ewNLsAAzE1AH0AAAAFZAAgAAAAAAZZ538coNPwyRjhEwr5P8Xw32oWOJF+R+nfCGgy2qO3BXMAIAAAAACOPLnJlKwGNPDBReRKnHfteq0wFb3ezhrc7BVXs8RUHwVjACAAAAAAzCICkPZAkfTiD0MUt155dIPgLJ4/e0qFTM2FR0U261YAAzE2AH0AAAAFZAAgAAAAAFH9l9GGA1I52atJV5jNUf1lx8jBjoEoVoME97v5GFJiBXMAIAAAAAC1qH3Kd78Dr9NGbw7y9D/XYBwv5h1LLO8la5OU7g8UkQVjACAAAAAAn27H0Mpwatgc1R/4nXSRjsG2PzB0ol5YR9f3mCb2y/0AAzE3AH0AAAAFZAAgAAAAADtbVEI2tdkrowEMdkacD2w0Y3T3Ofi7PH6HmA6sP0c/BXMAIAAAAADuBSROnZHA+NgUPH8d0LnWFiDsM2bY8bzjC1+elSsIygVjACAAAAAAMinHEu4wkbeOpdZbXQ94q5o5pIEubqXUDrTRYOGmJC0AAzE4AH0AAAAFZAAgAAAAAAh3WpeMVlikPFYj9hLj+fmIqVt6omCSF75W3TPExyWpBXMAIAAAAAAsQkRmwqeVj2gGE03orb6PtrIzDt6dDU3hgSQi8E2wKgVjACAAAAAAvlZo8Qj3eAdxzZxN5sHKhxi+a9Npj7cZC5+pE6qrOawAAAVlACAAAAAA65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+wSY20AAAAAAAAAAAAAEHBheWxvYWRJZAAAAAAAEGZpcnN0T3BlcmF0b3IAAQAAAAA=", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ] + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + "deleteTokens": { + "default.default": { + "encryptedDoublePrecision": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + command_name: delete + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDoublePrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", + "subType": "00" + } + } + ] + } \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-DoublePrecision-FindOneAndUpdate.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-DoublePrecision-FindOneAndUpdate.json new file mode 100644 index 00000000000..0511c2e37ec --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-DoublePrecision-FindOneAndUpdate.json @@ -0,0 +1,608 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range DoublePrecision. FindOneAndUpdate.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1" + } + } + } + }, + { + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$gt": { + "$numberDouble": "0" + } + } + }, + "update": { + "$set": { + "encryptedDoublePrecision": { + "$numberDouble": "2" + } + } + }, + "returnDocument": "Before" + }, + "result": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1" + } + } + } + ], + "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": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDoublePrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "findAndModify": "default", + "query": { + "encryptedDoublePrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "update": { + "$set": { + "encryptedDoublePrecision": { + "$$type": "binData" + } + } + }, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + }, + "deleteTokens": { + "default.default": { + "encryptedDoublePrecision": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + } + }, + "command_name": "findAndModify" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedDoublePrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "V6knyt7Zq2CG3++l75UtBx2m32iGAPjHiAe439Bf02w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0OKSXELxPP85SBVwDGf3LtMEQCJ8TTkFUl/+6jlkdb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uEw0lpQtBppR3vqV9j9+NQRSBF1BzZukb8c9IhyWvxc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zVhZ7Q59O087ji49oMJvBIgeir2oqvUpnh4p53GcTow=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dowrzKs+qJhRMZyKDbhjXbuX43FbmUKOaw9I8YlOZDw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ep5B6cska6THLIF7Mn3tn3RvV9EiwLSt0eZM/CLRUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "URNp/YmmDh5wIZUfAzzgPyJeMNiVx9PMsz52DZRujGY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wlM4IAQhhKQEzoVqS8b1Ddd50GB95OFb9LnzOwyjCP4=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-DoublePrecision-FindOneAndUpdate.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-DoublePrecision-FindOneAndUpdate.yml new file mode 100644 index 00000000000..a13f878e307 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-DoublePrecision-FindOneAndUpdate.yml @@ -0,0 +1,327 @@ +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDoublePrecision', 'bsonType': 'double', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberDouble': '0.0'}, 'max': {'$numberDouble': '200.0'}, 'precision': {'$numberInt': '2'}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "FLE2 Range DoublePrecision. FindOneAndUpdate." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDoublePrecision: { $numberDouble: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDoublePrecision: { $numberDouble: "1" } } + - name: findOneAndUpdate + arguments: + filter: { encryptedDoublePrecision: { $gt: {$numberDouble: "0"}} } + update: { "$set": { "encryptedDoublePrecision": {$numberDouble: "2"}}} + returnDocument: Before + result: *doc1 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + findAndModify: *collection_name + query: { + "encryptedDoublePrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + } + update: { "$set": {"encryptedDoublePrecision": { $$type: "binData" }} } + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + "deleteTokens": { + "default.default": { + "encryptedDoublePrecision": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + command_name: findAndModify + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDoublePrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", + "subType": "00" + } + } + ] + } + - + { + "_id": 1, + "encryptedDoublePrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "V6knyt7Zq2CG3++l75UtBx2m32iGAPjHiAe439Bf02w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0OKSXELxPP85SBVwDGf3LtMEQCJ8TTkFUl/+6jlkdb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uEw0lpQtBppR3vqV9j9+NQRSBF1BzZukb8c9IhyWvxc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zVhZ7Q59O087ji49oMJvBIgeir2oqvUpnh4p53GcTow=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dowrzKs+qJhRMZyKDbhjXbuX43FbmUKOaw9I8YlOZDw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ep5B6cska6THLIF7Mn3tn3RvV9EiwLSt0eZM/CLRUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "URNp/YmmDh5wIZUfAzzgPyJeMNiVx9PMsz52DZRujGY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wlM4IAQhhKQEzoVqS8b1Ddd50GB95OFb9LnzOwyjCP4=", + "subType": "00" + } + } + ] + } \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-DoublePrecision-InsertFind.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-DoublePrecision-InsertFind.json new file mode 100644 index 00000000000..616101b4d44 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-DoublePrecision-InsertFind.json @@ -0,0 +1,577 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range DoublePrecision. Insert and Find.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$gt": { + "$numberDouble": "0" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1" + } + } + ] + } + ], + "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": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDoublePrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "encryptedDoublePrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedDoublePrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "mVZb+Ra0EYjQ4Zrh9X//E2T8MRj7NMqm5GUJXhRrBEI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MgwakFvPyBlwqFTbhWUF79URJQWFoJTGotlEVSPPUsQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DyBERpMSD5lEM5Nhpcn4WGgxgn/mkUVJp+PYSLX5jsE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I43iazc0xj1WVbYB/V+uTL/tughN1bBlxh1iypBnNsA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wjOBa/ATMuOywFmuPgC0GF/oeLqu0Z7eK5udzkTPbis=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gRQVwiR+m+0Vg8ZDXqrQQcVnTyobwCXNaA4BCJVXtMc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WUZ6huwx0ZbLb0R00uiC9FOJzsUocUN8qE5+YRenkvQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7s79aKEuPgQcS/YPOOVcYNZvHIo7FFsWtFCrnDKXefA=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-DoublePrecision-InsertFind.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-DoublePrecision-InsertFind.yml new file mode 100644 index 00000000000..1cc9969c011 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-DoublePrecision-InsertFind.yml @@ -0,0 +1,305 @@ +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDoublePrecision', 'bsonType': 'double', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberDouble': '0.0'}, 'max': {'$numberDouble': '200.0'}, 'precision': {'$numberInt': '2'}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "FLE2 Range DoublePrecision. Insert and Find." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDoublePrecision: { $numberDouble: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDoublePrecision: { $numberDouble: "1" } } + - name: find + arguments: + filter: { encryptedDoublePrecision: { $gt: { $numberDouble: "0" } } } + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: + "encryptedDoublePrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDoublePrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", + "subType": "00" + } + } + ] + } + - + { + "_id": 1, + "encryptedDoublePrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "mVZb+Ra0EYjQ4Zrh9X//E2T8MRj7NMqm5GUJXhRrBEI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MgwakFvPyBlwqFTbhWUF79URJQWFoJTGotlEVSPPUsQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DyBERpMSD5lEM5Nhpcn4WGgxgn/mkUVJp+PYSLX5jsE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I43iazc0xj1WVbYB/V+uTL/tughN1bBlxh1iypBnNsA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wjOBa/ATMuOywFmuPgC0GF/oeLqu0Z7eK5udzkTPbis=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gRQVwiR+m+0Vg8ZDXqrQQcVnTyobwCXNaA4BCJVXtMc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WUZ6huwx0ZbLb0R00uiC9FOJzsUocUN8qE5+YRenkvQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7s79aKEuPgQcS/YPOOVcYNZvHIo7FFsWtFCrnDKXefA=", + "subType": "00" + } + } + ] + } \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-DoublePrecision-Update.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-DoublePrecision-Update.json new file mode 100644 index 00000000000..300202e2274 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-DoublePrecision-Update.json @@ -0,0 +1,612 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range DoublePrecision. Update.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1" + } + } + } + }, + { + "name": "updateOne", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$gt": { + "$numberDouble": "0" + } + } + }, + "update": { + "$set": { + "encryptedDoublePrecision": { + "$numberDouble": "2" + } + } + } + }, + "result": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "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": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDoublePrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command_name": "update", + "command": { + "update": "default", + "ordered": true, + "updates": [ + { + "q": { + "encryptedDoublePrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encryptedDoublePrecision": { + "$$type": "binData" + } + } + } + } + ], + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + }, + "deleteTokens": { + "default.default": { + "encryptedDoublePrecision": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + }, + "$db": "default" + } + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedDoublePrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "V6knyt7Zq2CG3++l75UtBx2m32iGAPjHiAe439Bf02w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0OKSXELxPP85SBVwDGf3LtMEQCJ8TTkFUl/+6jlkdb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uEw0lpQtBppR3vqV9j9+NQRSBF1BzZukb8c9IhyWvxc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zVhZ7Q59O087ji49oMJvBIgeir2oqvUpnh4p53GcTow=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dowrzKs+qJhRMZyKDbhjXbuX43FbmUKOaw9I8YlOZDw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ep5B6cska6THLIF7Mn3tn3RvV9EiwLSt0eZM/CLRUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "URNp/YmmDh5wIZUfAzzgPyJeMNiVx9PMsz52DZRujGY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wlM4IAQhhKQEzoVqS8b1Ddd50GB95OFb9LnzOwyjCP4=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-DoublePrecision-Update.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-DoublePrecision-Update.yml new file mode 100644 index 00000000000..116b623ac0d --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-DoublePrecision-Update.yml @@ -0,0 +1,344 @@ +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDoublePrecision', 'bsonType': 'double', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberDouble': '0.0'}, 'max': {'$numberDouble': '200.0'}, 'precision': {'$numberInt': '2'}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "FLE2 Range DoublePrecision. Update." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDoublePrecision: { $numberDouble: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDoublePrecision: { $numberDouble: "1" } } + - name: updateOne + arguments: + filter: { encryptedDoublePrecision: { $gt: { $numberDouble: "0" } } } + update: { "$set": { "encryptedDoublePrecision": { $numberDouble: "2" } }} + result: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command_name: update + command: + { + "update": "default", + "ordered": true, + "updates": [ + { + "q": { + "encryptedDoublePrecision": { + "$gt": { + "$binary": { + "base64": "CvoJAAADcGF5bG9hZADKCQAABGcAjgkAAAMwAH0AAAAFZAAgAAAAAHdJ2Vnb4MMzqVYVssjSdDy8XU4GVzMTfGifGETgQ2mYBXMAIAAAAAD7cFfKJGIXo6PjyeX2ria02CckW7dWFDoY/3FyBdm1NQVjACAAAAAAqZO+/+gRWlPaMOvuiXizSmBe7lp1VWg1vJ4UmW8o3bQAAzEAfQAAAAVkACAAAAAAlmvfDrZoydUet4eCVMq7z6a58Ea+1HLJOWxN5lNcrWEFcwAgAAAAAEBo5AWZyC41b9ayjWNQSL4iYEAIwR/JG+ssN8bdoK9RBWMAIAAAAAD4FTKJ6CTzKBAyAwZCLUoDEfnZTRZmhF1q/2hnDzmG9gADMgB9AAAABWQAIAAAAABbiLaoxAA6rinMJw1hC8ZUiq6UU1AQaPFn/py/Y06WuQVzACAAAAAAhtDasFkvYE7SCNu1je/hxdE9TJtAvvH3NtdEbKzNbCUFYwAgAAAAAHHy019aPatHTST+0wGsmukUcsQNQj6KpoS9b7iGeThAAAMzAH0AAAAFZAAgAAAAALORWwSr+tYNxcil2KIGSbNhTHvcPbdj+rLVQNx21S/KBXMAIAAAAAD6diZBkPEJ1cQy06LAxdbNK8Nlxbb44fH4Wk3Y3260nQVjACAAAAAAvUc1q7pyhjU0ilgmwiKkHIY3V4/LxO+Y2uT7eSpBOs8AAzQAfQAAAAVkACAAAAAAG5XMK96PjClNlUvg82j4pMY1YxsznZfj4uNweD394FoFcwAgAAAAAKHgQLdGJHkrfFg9nB93Ac+3VgBw6aU44MTkKIQ91dZoBWMAIAAAAACtbNc1DCoUUyzlkrYmJi4NlwOqLYmb6au4pDc8clXVXwADNQB9AAAABWQAIAAAAAA6M++b9I0YFemmWBAWAE3glu2Ah3Ta1FBxAQEIWS0toAVzACAAAAAANXYTqPf1Y6X3Ns6YQIX0C3FKCyWUo+Kk+fNcQvc0WSoFYwAgAAAAAAaqju6Dv8wqXxcsIbP67V1QGaD5kNTFofZ9Zuf1LGnKAAM2AH0AAAAFZAAgAAAAAKl8bV1riH/uyJ+X0HHd3+18k2cJl2dQFXCdoagutFcaBXMAIAAAAABm8F2Ew9f0VOABdcF+lP0Bi+zWvEUPniWgrxPq/Sx3uwVjACAAAAAAQd2pWVqlmmLg8m8xbs7yLewmR0Z6UQgXofbCsMHaGSoAAzcAfQAAAAVkACAAAAAAsYZD8JEP6kYsPncFnNZwJxhu4YtUTKPNcjHtv67H+rYFcwAgAAAAAI4LqZcRkvbs/2F62Flu0pixNcor4WmBD0DHGaf039wLBWMAIAAAAAAqzpfyBpr4Ano+nFWJyyTuIJelJgiRDnMHQqdeqV8JaAADOAB9AAAABWQAIAAAAAC0vdAi+dmoIXvZ5LqUqvyKV9/tHqSI2SWiSJO5pTnA2wVzACAAAAAAS2qvf9fvfVUH5WtsVxjxmskpGjYTQV34LwvQQw1y9wIFYwAgAAAAAFhNY4qwNntyA+GIoNHZsTkIUbPgy4TBlvNnTPjp4bMFAAM5AH0AAAAFZAAgAAAAAH31lb/srBcrOXkzddCwAnclsR5/3QijEVgECs2JjOWBBXMAIAAAAABg7+prDT73YcCvLE5QbuIrqGcjLc5pQD2Miq0d29yrxgVjACAAAAAAFKqAqXG/ktejFQ7fM2aobO2VmEvZLXnRaJH97Jy/sJYAAzEwAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVjACAAAAAAfsofSP7nQHv8ic8ZW0aNlWxplS46Z+mywPR4rQk+wcgAAzExAH0AAAAFZAAgAAAAAFdthRhe2Q8CvxGIhjTJZv0Lk97GkHciTPxZ/mckLoNaBXMAIAAAAAAqOxsAr23LOVB0DIHbPf9UDJJRFXY2YoKbjhRqw5psbQVjACAAAAAA7ty+Nif6KjS3v1zWKaHX9n4Zj3XC4ajuCduKNIYr3l8AAzEyAH0AAAAFZAAgAAAAAMWymwwbvIeMqmnKWWifUqoCxOsdpnonM2qdLPyjqJO/BXMAIAAAAAB6IDmmpUhBD2zpRj8/y/kmOSXcjuIU14sNh6GKSsg2uwVjACAAAAAABSWO0Ii+NGcsHZQ9MR5EjPXVKeXlI4FQ1pcxeKDiuooAAzEzAH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVjACAAAAAAD51NYesiO4Fo7w7iWBfqAFxEqkaDVctpvzZ28nT4SE8AAzE0AH0AAAAFZAAgAAAAAJaRYmo8zqI2BEUzdSwp4tVRpPmVWsfydkYN3UHh6TMuBXMAIAAAAAAeD6mDnQeLlbC9i0sVgE8+RH6y+e94OJQ0tJ0PvblVSgVjACAAAAAAKUhYSt4nvvUfbNgPJ2E79SciVZ0ZzbzoZ2nKr4ewNLsAAzE1AH0AAAAFZAAgAAAAAAZZ538coNPwyRjhEwr5P8Xw32oWOJF+R+nfCGgy2qO3BXMAIAAAAACOPLnJlKwGNPDBReRKnHfteq0wFb3ezhrc7BVXs8RUHwVjACAAAAAAzCICkPZAkfTiD0MUt155dIPgLJ4/e0qFTM2FR0U261YAAzE2AH0AAAAFZAAgAAAAAFH9l9GGA1I52atJV5jNUf1lx8jBjoEoVoME97v5GFJiBXMAIAAAAAC1qH3Kd78Dr9NGbw7y9D/XYBwv5h1LLO8la5OU7g8UkQVjACAAAAAAn27H0Mpwatgc1R/4nXSRjsG2PzB0ol5YR9f3mCb2y/0AAzE3AH0AAAAFZAAgAAAAADtbVEI2tdkrowEMdkacD2w0Y3T3Ofi7PH6HmA6sP0c/BXMAIAAAAADuBSROnZHA+NgUPH8d0LnWFiDsM2bY8bzjC1+elSsIygVjACAAAAAAMinHEu4wkbeOpdZbXQ94q5o5pIEubqXUDrTRYOGmJC0AAzE4AH0AAAAFZAAgAAAAAAh3WpeMVlikPFYj9hLj+fmIqVt6omCSF75W3TPExyWpBXMAIAAAAAAsQkRmwqeVj2gGE03orb6PtrIzDt6dDU3hgSQi8E2wKgVjACAAAAAAvlZo8Qj3eAdxzZxN5sHKhxi+a9Npj7cZC5+pE6qrOawAAAVlACAAAAAA65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+wSY20AAAAAAAAAAAAAEHBheWxvYWRJZAAAAAAAEGZpcnN0T3BlcmF0b3IAAQAAAAA=", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encryptedDoublePrecision": { $$type: "binData" } + } + } + } + ], + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": *encrypted_fields + }, + "deleteTokens": { + "default.default": { + "encryptedDoublePrecision": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + }, + "$db": "default" + } + + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDoublePrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", + "subType": "00" + } + } + ] + } + - + { + "_id": 1, + "encryptedDoublePrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "V6knyt7Zq2CG3++l75UtBx2m32iGAPjHiAe439Bf02w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0OKSXELxPP85SBVwDGf3LtMEQCJ8TTkFUl/+6jlkdb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uEw0lpQtBppR3vqV9j9+NQRSBF1BzZukb8c9IhyWvxc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zVhZ7Q59O087ji49oMJvBIgeir2oqvUpnh4p53GcTow=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dowrzKs+qJhRMZyKDbhjXbuX43FbmUKOaw9I8YlOZDw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ep5B6cska6THLIF7Mn3tn3RvV9EiwLSt0eZM/CLRUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "URNp/YmmDh5wIZUfAzzgPyJeMNiVx9PMsz52DZRujGY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wlM4IAQhhKQEzoVqS8b1Ddd50GB95OFb9LnzOwyjCP4=", + "subType": "00" + } + } + ] + } \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Int-Aggregate.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-Int-Aggregate.json new file mode 100644 index 00000000000..536415f3feb --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Int-Aggregate.json @@ -0,0 +1,490 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Int. Aggregate.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$gt": { + "$numberInt": "0" + } + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + } + ], + "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": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedInt": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedInt": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "default", + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$gt": { + "$binary": { + "base64": "CnEFAAADcGF5bG9hZABBBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVjACAAAAAAKs6X8gaa+AJ6PpxVicsk7iCXpSYIkQ5zB0KnXqlfCWgAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWMAIAAAAABYTWOKsDZ7cgPhiKDR2bE5CFGz4MuEwZbzZ0z46eGzBQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFYwAgAAAAABSqgKlxv5LXoxUO3zNmqGztlZhL2S150WiR/eycv7CWAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVjACAAAAAAfsofSP7nQHv8ic8ZW0aNlWxplS46Z+mywPR4rQk+wcgAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWMAIAAAAADu3L42J/oqNLe/XNYpodf2fhmPdcLhqO4J24o0hiveXwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFYwAgAAAAAAUljtCIvjRnLB2UPTEeRIz11Snl5SOBUNaXMXig4rqKAAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVjACAAAAAAD51NYesiO4Fo7w7iWBfqAFxEqkaDVctpvzZ28nT4SE8AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWMAIAAAAABJDtFhJ2tPbowp1UUmOCN/rqSqHRL1dtMu0c47vIlK4wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FYwAgAAAAAE6kvmXPqTnYIH4EJmNhy8OLVJZFOmdiBXLMorhELjKWAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVjACAAAAAA85AiE+bNFAYQTXQAFexgeczdVhf8FUnf16WzJlI/kmsAAAVlACAAAAAA65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+wSY20AAAAAAAAAAAAAEHBheWxvYWRJZAAAAAAAEGZpcnN0T3BlcmF0b3IAAQAAAAA=", + "subType": "06" + } + } + } + } + } + ], + "cursor": {}, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "aggregate" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedInt": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedInt": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Int-Aggregate.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-Int-Aggregate.yml new file mode 100644 index 00000000000..163ff4f750f --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Int-Aggregate.yml @@ -0,0 +1,227 @@ +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedInt', 'bsonType': 'int', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberInt': '0'}, 'max': {'$numberInt': '200'}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "FLE2 Range Int. Aggregate." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedInt: { $numberInt: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedInt: { $numberInt: "1" } } + - name: aggregate + arguments: + pipeline: [{ $match: { "encryptedInt": { $gt: {$numberInt: "0" }} } }] + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + aggregate: *collection_name + pipeline: [ + { + "$match": { + "encryptedInt": { + "$gt": { + "$binary": { + "base64": "CnEFAAADcGF5bG9hZABBBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVjACAAAAAAKs6X8gaa+AJ6PpxVicsk7iCXpSYIkQ5zB0KnXqlfCWgAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWMAIAAAAABYTWOKsDZ7cgPhiKDR2bE5CFGz4MuEwZbzZ0z46eGzBQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFYwAgAAAAABSqgKlxv5LXoxUO3zNmqGztlZhL2S150WiR/eycv7CWAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVjACAAAAAAfsofSP7nQHv8ic8ZW0aNlWxplS46Z+mywPR4rQk+wcgAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWMAIAAAAADu3L42J/oqNLe/XNYpodf2fhmPdcLhqO4J24o0hiveXwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFYwAgAAAAAAUljtCIvjRnLB2UPTEeRIz11Snl5SOBUNaXMXig4rqKAAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVjACAAAAAAD51NYesiO4Fo7w7iWBfqAFxEqkaDVctpvzZ28nT4SE8AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWMAIAAAAABJDtFhJ2tPbowp1UUmOCN/rqSqHRL1dtMu0c47vIlK4wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FYwAgAAAAAE6kvmXPqTnYIH4EJmNhy8OLVJZFOmdiBXLMorhELjKWAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVjACAAAAAA85AiE+bNFAYQTXQAFexgeczdVhf8FUnf16WzJlI/kmsAAAVlACAAAAAA65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+wSY20AAAAAAAAAAAAAEHBheWxvYWRJZAAAAAAAEGZpcnN0T3BlcmF0b3IAAQAAAAA=", + "subType": "06" + } + } + } + } + } + ] + cursor: {} + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: aggregate + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedInt": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + } + - + { + "_id": 1, + "encryptedInt": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", + "subType": "00" + } + } + ] + } \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Int-Correctness.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-Int-Correctness.json new file mode 100644 index 00000000000..6abd773da89 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Int-Correctness.json @@ -0,0 +1,1644 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "Find with $gt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$gt": { + "$numberInt": "0" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + } + ] + }, + { + "description": "Find with $gte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$gte": { + "$numberInt": "0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + }, + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + } + ] + }, + { + "description": "Find with $gt with no results", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$gt": { + "$numberInt": "1" + } + } + } + }, + "result": [] + } + ] + }, + { + "description": "Find with $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$lt": { + "$numberInt": "1" + } + } + } + }, + "result": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + ] + } + ] + }, + { + "description": "Find with $lte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$lte": { + "$numberInt": "1" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + }, + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + } + ] + }, + { + "description": "Find with $lt below min", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$lt": { + "$numberInt": "0" + } + } + } + }, + "result": { + "errorContains": "must be greater than the range minimum" + } + } + ] + }, + { + "description": "Find with $gt above max", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$gt": { + "$numberInt": "200" + } + } + } + }, + "result": { + "errorContains": "must be less than the range maximum" + } + } + ] + }, + { + "description": "Find with $gt and $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$gt": { + "$numberInt": "0" + }, + "$lt": { + "$numberInt": "2" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + } + ] + }, + { + "description": "Find with equality", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$numberInt": "0" + } + } + }, + "result": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + ] + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$numberInt": "1" + } + } + }, + "result": [ + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + } + ] + }, + { + "description": "Find with full range", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$gte": { + "$numberInt": "0" + }, + "$lte": { + "$numberInt": "200" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + }, + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + } + ] + }, + { + "description": "Find with $in", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$in": [ + { + "$numberInt": "0" + } + ] + } + } + }, + "result": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + ] + } + ] + }, + { + "description": "Insert out of range", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "-1" + } + } + }, + "result": { + "errorContains": "value must be greater than or equal to the minimum value" + } + } + ] + }, + { + "description": "Insert min and max", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 200, + "encryptedInt": { + "$numberInt": "200" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + }, + { + "_id": 200, + "encryptedInt": { + "$numberInt": "200" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$gte": { + "$numberInt": "0" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + }, + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gt with no results", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$gt": { + "$numberInt": "1" + } + } + } + } + ] + }, + "result": [] + } + ] + }, + { + "description": "Aggregate with $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$lt": { + "$numberInt": "1" + } + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $lte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$lte": { + "$numberInt": "1" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + }, + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $lt below min", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$lt": { + "$numberInt": "0" + } + } + } + } + ] + }, + "result": { + "errorContains": "must be greater than the range minimum" + } + } + ] + }, + { + "description": "Aggregate with $gt above max", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$gt": { + "$numberInt": "200" + } + } + } + } + ] + }, + "result": { + "errorContains": "must be less than the range maximum" + } + } + ] + }, + { + "description": "Aggregate with $gt and $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$gt": { + "$numberInt": "0" + }, + "$lt": { + "$numberInt": "2" + } + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + } + ] + }, + { + "description": "Aggregate with equality", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$numberInt": "0" + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + ] + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$numberInt": "1" + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + } + ] + }, + { + "description": "Aggregate with full range", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$gte": { + "$numberInt": "0" + }, + "$lte": { + "$numberInt": "200" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + }, + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $in", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$in": [ + { + "$numberInt": "0" + } + ] + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + ] + } + ] + }, + { + "description": "Wrong type: Insert Double", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberDouble": "0" + } + } + }, + "result": { + "errorContains": "cannot encrypt element" + } + } + ] + }, + { + "description": "Wrong type: Find Double", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$gte": { + "$numberDouble": "0" + } + } + } + }, + "result": { + "errorContains": "field type is not supported" + } + } + ] + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Int-Correctness.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-Int-Correctness.yml new file mode 100644 index 00000000000..db2a1e46a8f --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Int-Correctness.yml @@ -0,0 +1,421 @@ +# Test correctness results. +# Does not include command monitoring expectations or outcome assertions to make tests more readable. +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedInt', 'bsonType': 'int', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberInt': '0'}, 'max': {'$numberInt': '200'}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "Find with $gt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedInt: { $numberInt: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedInt: { $numberInt: "1" } } + - name: find + arguments: + filter: { encryptedInt: { $gt: { $numberInt: "0" } }} + result: [*doc1] + + - description: "Find with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedInt: { $gte: { $numberInt: "0" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedInt: { $gt: { $numberInt: "1" } }} + result: [] + + - description: "Find with $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedInt: { $lt: { $numberInt: "1" } }} + result: [*doc0] + + - description: "Find with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedInt: { $lte: { $numberInt: "1" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $lt below min" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedInt: { $lt: { $numberInt: "0" } }} + result: + errorContains: must be greater than the range minimum + + - description: "Find with $gt above max" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedInt: { $gt: { $numberInt: "200" } }} + result: + errorContains: must be less than the range maximum + + - description: "Find with $gt and $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedInt: { $gt: { $numberInt: "0" }, $lt: { $numberInt: "2"} }} + result: [*doc1] + + - description: "Find with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedInt: { $numberInt: "0" } } + result: [*doc0] + - name: find + arguments: + filter: { encryptedInt: { $numberInt: "1" } } + result: [*doc1] + + - description: "Find with full range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedInt: { $gte: {$numberInt: "0"}, $lte: {$numberInt: "200"} } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedInt: { $in: [ {$numberInt: "0"} ] } } + result: [*doc0] + + - description: "Insert out of range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedInt: { $numberInt: "-1" }} + result: + errorContains: value must be greater than or equal to the minimum value + + - description: "Insert min and max" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: *doc0 + - name: insertOne + arguments: + document: &doc200 { _id: 200, encryptedInt: { $numberInt: "200" }} + - name: find + arguments: + filter: {} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc200] + + - description: "Aggregate with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $gte: { $numberInt: "0" } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + + result: [*doc0, *doc1] + + - description: "Aggregate with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $gt: { $numberInt: "1" } }} } + result: [] + + - description: "Aggregate with $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $lt: { $numberInt: "1" } }} } + result: [*doc0] + + - description: "Aggregate with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $lte: { $numberInt: "1" } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $lt below min" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $lt: { $numberInt: "0" } }} } + result: + errorContains: must be greater than the range minimum + + - description: "Aggregate with $gt above max" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $gt: { $numberInt: "200" } }} } + result: + errorContains: must be less than the range maximum + + - description: "Aggregate with $gt and $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $gt: { $numberInt: "0" }, $lt: { $numberInt: "2"} }} } + result: [*doc1] + + - description: "Aggregate with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $numberInt: "0" } } } + result: [*doc0] + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $numberInt: "1" } } } + result: [*doc1] + + - description: "Aggregate with full range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $gte: {$numberInt: "0"}, $lte: {$numberInt: "200"} } } } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $in: [ {$numberInt: "0"} ] } } } + result: [*doc0] + + - description: "Wrong type: Insert Double" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: { _id: 0, encryptedInt: { $numberDouble: "0" }} } + result: + # Expect an error from mongocryptd. + errorContains: "cannot encrypt element" + + - description: "Wrong type: Find Double" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: find + arguments: + filter: { encryptedInt: { $gte: { $numberDouble: "0" } }} + result: + # expect an error from libmongocrypt. + errorContains: "field type is not supported" \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Int-Delete.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-Int-Delete.json new file mode 100644 index 00000000000..9d5bff1d19f --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Int-Delete.json @@ -0,0 +1,437 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Int. Delete.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "deleteOne", + "arguments": { + "filter": { + "encryptedInt": { + "$gt": { + "$numberInt": "0" + } + } + } + }, + "result": { + "deletedCount": 1 + } + } + ], + "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": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedInt": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedInt": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "delete": "default", + "deletes": [ + { + "q": { + "encryptedInt": { + "$gt": { + "$binary": { + "base64": "CnEFAAADcGF5bG9hZABBBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVjACAAAAAAKs6X8gaa+AJ6PpxVicsk7iCXpSYIkQ5zB0KnXqlfCWgAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWMAIAAAAABYTWOKsDZ7cgPhiKDR2bE5CFGz4MuEwZbzZ0z46eGzBQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFYwAgAAAAABSqgKlxv5LXoxUO3zNmqGztlZhL2S150WiR/eycv7CWAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVjACAAAAAAfsofSP7nQHv8ic8ZW0aNlWxplS46Z+mywPR4rQk+wcgAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWMAIAAAAADu3L42J/oqNLe/XNYpodf2fhmPdcLhqO4J24o0hiveXwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFYwAgAAAAAAUljtCIvjRnLB2UPTEeRIz11Snl5SOBUNaXMXig4rqKAAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVjACAAAAAAD51NYesiO4Fo7w7iWBfqAFxEqkaDVctpvzZ28nT4SE8AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWMAIAAAAABJDtFhJ2tPbowp1UUmOCN/rqSqHRL1dtMu0c47vIlK4wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FYwAgAAAAAE6kvmXPqTnYIH4EJmNhy8OLVJZFOmdiBXLMorhELjKWAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVjACAAAAAA85AiE+bNFAYQTXQAFexgeczdVhf8FUnf16WzJlI/kmsAAAVlACAAAAAA65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+wSY20AAAAAAAAAAAAAEHBheWxvYWRJZAAAAAAAEGZpcnN0T3BlcmF0b3IAAQAAAAA=", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ], + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + }, + "deleteTokens": { + "default.default": { + "encryptedInt": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + } + }, + "command_name": "delete" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedInt": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Int-Delete.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-Int-Delete.yml new file mode 100644 index 00000000000..51a343493f1 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Int-Delete.yml @@ -0,0 +1,185 @@ +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedInt', 'bsonType': 'int', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberInt': '0'}, 'max': {'$numberInt': '200'}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "FLE2 Range Int. Delete." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedInt: { $numberInt: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedInt: { $numberInt: "1" } } + - name: deleteOne + arguments: + filter: { "encryptedInt": { $gt: {$numberInt: "0" }} } + result: + deletedCount: 1 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + delete: *collection_name + deletes: [ + { + "q": { + "encryptedInt": { + "$gt": { + "$binary": { + "base64": "CnEFAAADcGF5bG9hZABBBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVjACAAAAAAKs6X8gaa+AJ6PpxVicsk7iCXpSYIkQ5zB0KnXqlfCWgAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWMAIAAAAABYTWOKsDZ7cgPhiKDR2bE5CFGz4MuEwZbzZ0z46eGzBQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFYwAgAAAAABSqgKlxv5LXoxUO3zNmqGztlZhL2S150WiR/eycv7CWAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVjACAAAAAAfsofSP7nQHv8ic8ZW0aNlWxplS46Z+mywPR4rQk+wcgAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWMAIAAAAADu3L42J/oqNLe/XNYpodf2fhmPdcLhqO4J24o0hiveXwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFYwAgAAAAAAUljtCIvjRnLB2UPTEeRIz11Snl5SOBUNaXMXig4rqKAAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVjACAAAAAAD51NYesiO4Fo7w7iWBfqAFxEqkaDVctpvzZ28nT4SE8AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWMAIAAAAABJDtFhJ2tPbowp1UUmOCN/rqSqHRL1dtMu0c47vIlK4wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FYwAgAAAAAE6kvmXPqTnYIH4EJmNhy8OLVJZFOmdiBXLMorhELjKWAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVjACAAAAAA85AiE+bNFAYQTXQAFexgeczdVhf8FUnf16WzJlI/kmsAAAVlACAAAAAA65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+wSY20AAAAAAAAAAAAAEHBheWxvYWRJZAAAAAAAEGZpcnN0T3BlcmF0b3IAAQAAAAA=", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ] + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + "deleteTokens": { + "default.default": { + "encryptedInt": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + command_name: delete + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedInt": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + } \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Int-FindOneAndUpdate.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-Int-FindOneAndUpdate.json new file mode 100644 index 00000000000..4bf57700c94 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Int-FindOneAndUpdate.json @@ -0,0 +1,512 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Int. FindOneAndUpdate.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "encryptedInt": { + "$gt": { + "$numberInt": "0" + } + } + }, + "update": { + "$set": { + "encryptedInt": { + "$numberInt": "2" + } + } + }, + "returnDocument": "Before" + }, + "result": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + ], + "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": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedInt": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedInt": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "findAndModify": "default", + "query": { + "encryptedInt": { + "$gt": { + "$binary": { + "base64": "CnEFAAADcGF5bG9hZABBBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVjACAAAAAAKs6X8gaa+AJ6PpxVicsk7iCXpSYIkQ5zB0KnXqlfCWgAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWMAIAAAAABYTWOKsDZ7cgPhiKDR2bE5CFGz4MuEwZbzZ0z46eGzBQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFYwAgAAAAABSqgKlxv5LXoxUO3zNmqGztlZhL2S150WiR/eycv7CWAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVjACAAAAAAfsofSP7nQHv8ic8ZW0aNlWxplS46Z+mywPR4rQk+wcgAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWMAIAAAAADu3L42J/oqNLe/XNYpodf2fhmPdcLhqO4J24o0hiveXwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFYwAgAAAAAAUljtCIvjRnLB2UPTEeRIz11Snl5SOBUNaXMXig4rqKAAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVjACAAAAAAD51NYesiO4Fo7w7iWBfqAFxEqkaDVctpvzZ28nT4SE8AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWMAIAAAAABJDtFhJ2tPbowp1UUmOCN/rqSqHRL1dtMu0c47vIlK4wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FYwAgAAAAAE6kvmXPqTnYIH4EJmNhy8OLVJZFOmdiBXLMorhELjKWAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVjACAAAAAA85AiE+bNFAYQTXQAFexgeczdVhf8FUnf16WzJlI/kmsAAAVlACAAAAAA65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+wSY20AAAAAAAAAAAAAEHBheWxvYWRJZAAAAAAAEGZpcnN0T3BlcmF0b3IAAQAAAAA=", + "subType": "06" + } + } + } + }, + "update": { + "$set": { + "encryptedInt": { + "$$type": "binData" + } + } + }, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + }, + "deleteTokens": { + "default.default": { + "encryptedInt": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + } + }, + "command_name": "findAndModify" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedInt": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedInt": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hyDcE6QQjPrYJaIS/n7evEZFYcm31Tj89CpEYGF45cI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ty4cnzJdAlbQKnh7px3GEYjBnvO+jIOaKjoTRDtmh3M=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Int-FindOneAndUpdate.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-Int-FindOneAndUpdate.yml new file mode 100644 index 00000000000..433be15d310 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Int-FindOneAndUpdate.yml @@ -0,0 +1,243 @@ +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedInt', 'bsonType': 'int', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberInt': '0'}, 'max': {'$numberInt': '200'}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "FLE2 Range Int. FindOneAndUpdate." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedInt: { $numberInt: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedInt: { $numberInt: "1" } } + - name: findOneAndUpdate + arguments: + filter: { encryptedInt: { $gt: {$numberInt: "0"}} } + update: { "$set": { "encryptedInt": {$numberInt: "2"}}} + returnDocument: Before + result: *doc1 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + findAndModify: *collection_name + query: { + "encryptedInt": { + "$gt": { + "$binary": { + "base64": "CnEFAAADcGF5bG9hZABBBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVjACAAAAAAKs6X8gaa+AJ6PpxVicsk7iCXpSYIkQ5zB0KnXqlfCWgAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWMAIAAAAABYTWOKsDZ7cgPhiKDR2bE5CFGz4MuEwZbzZ0z46eGzBQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFYwAgAAAAABSqgKlxv5LXoxUO3zNmqGztlZhL2S150WiR/eycv7CWAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVjACAAAAAAfsofSP7nQHv8ic8ZW0aNlWxplS46Z+mywPR4rQk+wcgAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWMAIAAAAADu3L42J/oqNLe/XNYpodf2fhmPdcLhqO4J24o0hiveXwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFYwAgAAAAAAUljtCIvjRnLB2UPTEeRIz11Snl5SOBUNaXMXig4rqKAAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVjACAAAAAAD51NYesiO4Fo7w7iWBfqAFxEqkaDVctpvzZ28nT4SE8AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWMAIAAAAABJDtFhJ2tPbowp1UUmOCN/rqSqHRL1dtMu0c47vIlK4wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FYwAgAAAAAE6kvmXPqTnYIH4EJmNhy8OLVJZFOmdiBXLMorhELjKWAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVjACAAAAAA85AiE+bNFAYQTXQAFexgeczdVhf8FUnf16WzJlI/kmsAAAVlACAAAAAA65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+wSY20AAAAAAAAAAAAAEHBheWxvYWRJZAAAAAAAEGZpcnN0T3BlcmF0b3IAAQAAAAA=", + "subType": "06" + } + } + } + } + update: { "$set": {"encryptedInt": { $$type: "binData" }} } + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + "deleteTokens": { + "default.default": { + "encryptedInt": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + command_name: findAndModify + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedInt": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + } + - + { + "_id": 1, + "encryptedInt": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hyDcE6QQjPrYJaIS/n7evEZFYcm31Tj89CpEYGF45cI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ty4cnzJdAlbQKnh7px3GEYjBnvO+jIOaKjoTRDtmh3M=", + "subType": "00" + } + } + ] + } \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Int-InsertFind.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-Int-InsertFind.json new file mode 100644 index 00000000000..6f6022e7490 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Int-InsertFind.json @@ -0,0 +1,481 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Int. Insert and Find.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$gt": { + "$numberInt": "0" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + } + ], + "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": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedInt": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedInt": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "encryptedInt": { + "$gt": { + "$binary": { + "base64": "CnEFAAADcGF5bG9hZABBBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVjACAAAAAAKs6X8gaa+AJ6PpxVicsk7iCXpSYIkQ5zB0KnXqlfCWgAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWMAIAAAAABYTWOKsDZ7cgPhiKDR2bE5CFGz4MuEwZbzZ0z46eGzBQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFYwAgAAAAABSqgKlxv5LXoxUO3zNmqGztlZhL2S150WiR/eycv7CWAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVjACAAAAAAfsofSP7nQHv8ic8ZW0aNlWxplS46Z+mywPR4rQk+wcgAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWMAIAAAAADu3L42J/oqNLe/XNYpodf2fhmPdcLhqO4J24o0hiveXwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFYwAgAAAAAAUljtCIvjRnLB2UPTEeRIz11Snl5SOBUNaXMXig4rqKAAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVjACAAAAAAD51NYesiO4Fo7w7iWBfqAFxEqkaDVctpvzZ28nT4SE8AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWMAIAAAAABJDtFhJ2tPbowp1UUmOCN/rqSqHRL1dtMu0c47vIlK4wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FYwAgAAAAAE6kvmXPqTnYIH4EJmNhy8OLVJZFOmdiBXLMorhELjKWAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVjACAAAAAA85AiE+bNFAYQTXQAFexgeczdVhf8FUnf16WzJlI/kmsAAAVlACAAAAAA65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+wSY20AAAAAAAAAAAAAEHBheWxvYWRJZAAAAAAAEGZpcnN0T3BlcmF0b3IAAQAAAAA=", + "subType": "06" + } + } + } + }, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedInt": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedInt": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Int-InsertFind.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-Int-InsertFind.yml new file mode 100644 index 00000000000..dc5215eae64 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Int-InsertFind.yml @@ -0,0 +1,221 @@ +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedInt', 'bsonType': 'int', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberInt': '0'}, 'max': {'$numberInt': '200'}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "FLE2 Range Int. Insert and Find." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedInt: { $numberInt: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedInt: { $numberInt: "1" } } + - name: find + arguments: + filter: { encryptedInt: { $gt: { $numberInt: "0" } } } + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: + "encryptedInt": { + "$gt": { + "$binary": { + "base64": "CnEFAAADcGF5bG9hZABBBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVjACAAAAAAKs6X8gaa+AJ6PpxVicsk7iCXpSYIkQ5zB0KnXqlfCWgAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWMAIAAAAABYTWOKsDZ7cgPhiKDR2bE5CFGz4MuEwZbzZ0z46eGzBQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFYwAgAAAAABSqgKlxv5LXoxUO3zNmqGztlZhL2S150WiR/eycv7CWAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVjACAAAAAAfsofSP7nQHv8ic8ZW0aNlWxplS46Z+mywPR4rQk+wcgAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWMAIAAAAADu3L42J/oqNLe/XNYpodf2fhmPdcLhqO4J24o0hiveXwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFYwAgAAAAAAUljtCIvjRnLB2UPTEeRIz11Snl5SOBUNaXMXig4rqKAAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVjACAAAAAAD51NYesiO4Fo7w7iWBfqAFxEqkaDVctpvzZ28nT4SE8AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWMAIAAAAABJDtFhJ2tPbowp1UUmOCN/rqSqHRL1dtMu0c47vIlK4wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FYwAgAAAAAE6kvmXPqTnYIH4EJmNhy8OLVJZFOmdiBXLMorhELjKWAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVjACAAAAAA85AiE+bNFAYQTXQAFexgeczdVhf8FUnf16WzJlI/kmsAAAVlACAAAAAA65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+wSY20AAAAAAAAAAAAAEHBheWxvYWRJZAAAAAAAEGZpcnN0T3BlcmF0b3IAAQAAAAA=", + "subType": "06" + } + } + } + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedInt": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + } + - + { + "_id": 1, + "encryptedInt": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", + "subType": "00" + } + } + ] + } \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Int-Update.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-Int-Update.json new file mode 100644 index 00000000000..17d23b957f8 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Int-Update.json @@ -0,0 +1,516 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Int. Update.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "updateOne", + "arguments": { + "filter": { + "encryptedInt": { + "$gt": { + "$numberInt": "0" + } + } + }, + "update": { + "$set": { + "encryptedInt": { + "$numberInt": "2" + } + } + } + }, + "result": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "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": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedInt": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedInt": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command_name": "update", + "command": { + "update": "default", + "ordered": true, + "updates": [ + { + "q": { + "encryptedInt": { + "$gt": { + "$binary": { + "base64": "CnEFAAADcGF5bG9hZABBBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVjACAAAAAAKs6X8gaa+AJ6PpxVicsk7iCXpSYIkQ5zB0KnXqlfCWgAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWMAIAAAAABYTWOKsDZ7cgPhiKDR2bE5CFGz4MuEwZbzZ0z46eGzBQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFYwAgAAAAABSqgKlxv5LXoxUO3zNmqGztlZhL2S150WiR/eycv7CWAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVjACAAAAAAfsofSP7nQHv8ic8ZW0aNlWxplS46Z+mywPR4rQk+wcgAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWMAIAAAAADu3L42J/oqNLe/XNYpodf2fhmPdcLhqO4J24o0hiveXwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFYwAgAAAAAAUljtCIvjRnLB2UPTEeRIz11Snl5SOBUNaXMXig4rqKAAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVjACAAAAAAD51NYesiO4Fo7w7iWBfqAFxEqkaDVctpvzZ28nT4SE8AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWMAIAAAAABJDtFhJ2tPbowp1UUmOCN/rqSqHRL1dtMu0c47vIlK4wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FYwAgAAAAAE6kvmXPqTnYIH4EJmNhy8OLVJZFOmdiBXLMorhELjKWAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVjACAAAAAA85AiE+bNFAYQTXQAFexgeczdVhf8FUnf16WzJlI/kmsAAAVlACAAAAAA65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+wSY20AAAAAAAAAAAAAEHBheWxvYWRJZAAAAAAAEGZpcnN0T3BlcmF0b3IAAQAAAAA=", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encryptedInt": { + "$$type": "binData" + } + } + } + } + ], + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + }, + "deleteTokens": { + "default.default": { + "encryptedInt": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + }, + "$db": "default" + } + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedInt": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedInt": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hyDcE6QQjPrYJaIS/n7evEZFYcm31Tj89CpEYGF45cI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ty4cnzJdAlbQKnh7px3GEYjBnvO+jIOaKjoTRDtmh3M=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Int-Update.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-Int-Update.yml new file mode 100644 index 00000000000..8e0bdcb8dc6 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Int-Update.yml @@ -0,0 +1,260 @@ +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedInt', 'bsonType': 'int', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberInt': '0'}, 'max': {'$numberInt': '200'}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "FLE2 Range Int. Update." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedInt: { $numberInt: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedInt: { $numberInt: "1" } } + - name: updateOne + arguments: + filter: { encryptedInt: { $gt: { $numberInt: "0" } } } + update: { "$set": { "encryptedInt": { $numberInt: "2" } }} + result: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command_name: update + command: + { + "update": "default", + "ordered": true, + "updates": [ + { + "q": { + "encryptedInt": { + "$gt": { + "$binary": { + "base64": "CnEFAAADcGF5bG9hZABBBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVjACAAAAAAKs6X8gaa+AJ6PpxVicsk7iCXpSYIkQ5zB0KnXqlfCWgAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWMAIAAAAABYTWOKsDZ7cgPhiKDR2bE5CFGz4MuEwZbzZ0z46eGzBQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFYwAgAAAAABSqgKlxv5LXoxUO3zNmqGztlZhL2S150WiR/eycv7CWAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVjACAAAAAAfsofSP7nQHv8ic8ZW0aNlWxplS46Z+mywPR4rQk+wcgAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWMAIAAAAADu3L42J/oqNLe/XNYpodf2fhmPdcLhqO4J24o0hiveXwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFYwAgAAAAAAUljtCIvjRnLB2UPTEeRIz11Snl5SOBUNaXMXig4rqKAAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVjACAAAAAAD51NYesiO4Fo7w7iWBfqAFxEqkaDVctpvzZ28nT4SE8AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWMAIAAAAABJDtFhJ2tPbowp1UUmOCN/rqSqHRL1dtMu0c47vIlK4wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FYwAgAAAAAE6kvmXPqTnYIH4EJmNhy8OLVJZFOmdiBXLMorhELjKWAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVjACAAAAAA85AiE+bNFAYQTXQAFexgeczdVhf8FUnf16WzJlI/kmsAAAVlACAAAAAA65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+wSY20AAAAAAAAAAAAAEHBheWxvYWRJZAAAAAAAEGZpcnN0T3BlcmF0b3IAAQAAAAA=", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encryptedInt": { $$type: "binData" } + } + } + } + ], + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": *encrypted_fields + }, + "deleteTokens": { + "default.default": { + "encryptedInt": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + }, + "$db": "default" + } + + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedInt": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + } + - + { + "_id": 1, + "encryptedInt": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hyDcE6QQjPrYJaIS/n7evEZFYcm31Tj89CpEYGF45cI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ty4cnzJdAlbQKnh7px3GEYjBnvO+jIOaKjoTRDtmh3M=", + "subType": "00" + } + } + ] + } \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Long-Aggregate.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-Long-Aggregate.json new file mode 100644 index 00000000000..3f1c723bd29 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Long-Aggregate.json @@ -0,0 +1,490 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Long. Aggregate.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$gt": { + "$numberLong": "0" + } + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + } + ], + "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": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedLong": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedLong": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "default", + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$gt": { + "$binary": { + "base64": "CnEFAAADcGF5bG9hZABBBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVjACAAAAAAKs6X8gaa+AJ6PpxVicsk7iCXpSYIkQ5zB0KnXqlfCWgAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWMAIAAAAABYTWOKsDZ7cgPhiKDR2bE5CFGz4MuEwZbzZ0z46eGzBQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFYwAgAAAAABSqgKlxv5LXoxUO3zNmqGztlZhL2S150WiR/eycv7CWAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVjACAAAAAAfsofSP7nQHv8ic8ZW0aNlWxplS46Z+mywPR4rQk+wcgAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWMAIAAAAADu3L42J/oqNLe/XNYpodf2fhmPdcLhqO4J24o0hiveXwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFYwAgAAAAAAUljtCIvjRnLB2UPTEeRIz11Snl5SOBUNaXMXig4rqKAAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVjACAAAAAAD51NYesiO4Fo7w7iWBfqAFxEqkaDVctpvzZ28nT4SE8AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWMAIAAAAABJDtFhJ2tPbowp1UUmOCN/rqSqHRL1dtMu0c47vIlK4wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FYwAgAAAAAE6kvmXPqTnYIH4EJmNhy8OLVJZFOmdiBXLMorhELjKWAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVjACAAAAAA85AiE+bNFAYQTXQAFexgeczdVhf8FUnf16WzJlI/kmsAAAVlACAAAAAA65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+wSY20AAAAAAAAAAAAAEHBheWxvYWRJZAAAAAAAEGZpcnN0T3BlcmF0b3IAAQAAAAA=", + "subType": "06" + } + } + } + } + } + ], + "cursor": {}, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "command_name": "aggregate" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedLong": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedLong": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Long-Aggregate.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-Long-Aggregate.yml new file mode 100644 index 00000000000..8064fbeddd1 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Long-Aggregate.yml @@ -0,0 +1,227 @@ +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedLong', 'bsonType': 'long', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberLong': '0'}, 'max': {'$numberLong': '200'}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "FLE2 Range Long. Aggregate." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedLong: { $numberLong: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedLong: { $numberLong: "1" } } + - name: aggregate + arguments: + pipeline: [{ $match: { "encryptedLong": { $gt: {$numberLong: "0" }} } }] + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + aggregate: *collection_name + pipeline: [ + { + "$match": { + "encryptedLong": { + "$gt": { + "$binary": { + "base64": "CnEFAAADcGF5bG9hZABBBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVjACAAAAAAKs6X8gaa+AJ6PpxVicsk7iCXpSYIkQ5zB0KnXqlfCWgAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWMAIAAAAABYTWOKsDZ7cgPhiKDR2bE5CFGz4MuEwZbzZ0z46eGzBQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFYwAgAAAAABSqgKlxv5LXoxUO3zNmqGztlZhL2S150WiR/eycv7CWAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVjACAAAAAAfsofSP7nQHv8ic8ZW0aNlWxplS46Z+mywPR4rQk+wcgAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWMAIAAAAADu3L42J/oqNLe/XNYpodf2fhmPdcLhqO4J24o0hiveXwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFYwAgAAAAAAUljtCIvjRnLB2UPTEeRIz11Snl5SOBUNaXMXig4rqKAAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVjACAAAAAAD51NYesiO4Fo7w7iWBfqAFxEqkaDVctpvzZ28nT4SE8AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWMAIAAAAABJDtFhJ2tPbowp1UUmOCN/rqSqHRL1dtMu0c47vIlK4wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FYwAgAAAAAE6kvmXPqTnYIH4EJmNhy8OLVJZFOmdiBXLMorhELjKWAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVjACAAAAAA85AiE+bNFAYQTXQAFexgeczdVhf8FUnf16WzJlI/kmsAAAVlACAAAAAA65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+wSY20AAAAAAAAAAAAAEHBheWxvYWRJZAAAAAAAEGZpcnN0T3BlcmF0b3IAAQAAAAA=", + "subType": "06" + } + } + } + } + } + ] + cursor: {} + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: aggregate + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedLong": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + } + - + { + "_id": 1, + "encryptedLong": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", + "subType": "00" + } + } + ] + } \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Long-Correctness.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-Long-Correctness.json new file mode 100644 index 00000000000..972388c6c43 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Long-Correctness.json @@ -0,0 +1,1644 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "Find with $gt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$gt": { + "$numberLong": "0" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + } + ] + }, + { + "description": "Find with $gte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$gte": { + "$numberLong": "0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + }, + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + } + ] + }, + { + "description": "Find with $gt with no results", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$gt": { + "$numberLong": "1" + } + } + } + }, + "result": [] + } + ] + }, + { + "description": "Find with $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$lt": { + "$numberLong": "1" + } + } + } + }, + "result": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + ] + } + ] + }, + { + "description": "Find with $lte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$lte": { + "$numberLong": "1" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + }, + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + } + ] + }, + { + "description": "Find with $lt below min", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$lt": { + "$numberLong": "0" + } + } + } + }, + "result": { + "errorContains": "must be greater than the range minimum" + } + } + ] + }, + { + "description": "Find with $gt above max", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$gt": { + "$numberLong": "200" + } + } + } + }, + "result": { + "errorContains": "must be less than the range maximum" + } + } + ] + }, + { + "description": "Find with $gt and $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$gt": { + "$numberLong": "0" + }, + "$lt": { + "$numberLong": "2" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + } + ] + }, + { + "description": "Find with equality", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$numberLong": "0" + } + } + }, + "result": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + ] + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$numberLong": "1" + } + } + }, + "result": [ + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + } + ] + }, + { + "description": "Find with full range", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$gte": { + "$numberLong": "0" + }, + "$lte": { + "$numberLong": "200" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + }, + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + } + ] + }, + { + "description": "Find with $in", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$in": [ + { + "$numberLong": "0" + } + ] + } + } + }, + "result": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + ] + } + ] + }, + { + "description": "Insert out of range", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "-1" + } + } + }, + "result": { + "errorContains": "value must be greater than or equal to the minimum value" + } + } + ] + }, + { + "description": "Insert min and max", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 200, + "encryptedLong": { + "$numberLong": "200" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + }, + { + "_id": 200, + "encryptedLong": { + "$numberLong": "200" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$gte": { + "$numberLong": "0" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + }, + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gt with no results", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$gt": { + "$numberLong": "1" + } + } + } + } + ] + }, + "result": [] + } + ] + }, + { + "description": "Aggregate with $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$lt": { + "$numberLong": "1" + } + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $lte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$lte": { + "$numberLong": "1" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + }, + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $lt below min", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$lt": { + "$numberLong": "0" + } + } + } + } + ] + }, + "result": { + "errorContains": "must be greater than the range minimum" + } + } + ] + }, + { + "description": "Aggregate with $gt above max", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$gt": { + "$numberLong": "200" + } + } + } + } + ] + }, + "result": { + "errorContains": "must be less than the range maximum" + } + } + ] + }, + { + "description": "Aggregate with $gt and $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$gt": { + "$numberLong": "0" + }, + "$lt": { + "$numberLong": "2" + } + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + } + ] + }, + { + "description": "Aggregate with equality", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$numberLong": "0" + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + ] + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$numberLong": "1" + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + } + ] + }, + { + "description": "Aggregate with full range", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$gte": { + "$numberLong": "0" + }, + "$lte": { + "$numberLong": "200" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + }, + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $in", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$in": [ + { + "$numberLong": "0" + } + ] + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + ] + } + ] + }, + { + "description": "Wrong type: Insert Double", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberDouble": "0" + } + } + }, + "result": { + "errorContains": "cannot encrypt element" + } + } + ] + }, + { + "description": "Wrong type: Find Double", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$gte": { + "$numberDouble": "0" + } + } + } + }, + "result": { + "errorContains": "field type is not supported" + } + } + ] + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Long-Correctness.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-Long-Correctness.yml new file mode 100644 index 00000000000..696a09fa10d --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Long-Correctness.yml @@ -0,0 +1,420 @@ +# Test correctness results. +# Does not include command monitoring expectations or outcome assertions to make tests more readable. +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedLong', 'bsonType': 'long', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberLong': '0'}, 'max': {'$numberLong': '200'}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "Find with $gt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedLong: { $numberLong: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedLong: { $numberLong: "1" } } + - name: find + arguments: + filter: { encryptedLong: { $gt: { $numberLong: "0" } }} + result: [*doc1] + + - description: "Find with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedLong: { $gte: { $numberLong: "0" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedLong: { $gt: { $numberLong: "1" } }} + result: [] + + - description: "Find with $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedLong: { $lt: { $numberLong: "1" } }} + result: [*doc0] + + - description: "Find with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedLong: { $lte: { $numberLong: "1" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $lt below min" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedLong: { $lt: { $numberLong: "0" } }} + result: + errorContains: must be greater than the range minimum + + - description: "Find with $gt above max" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedLong: { $gt: { $numberLong: "200" } }} + result: + errorContains: must be less than the range maximum + + - description: "Find with $gt and $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedLong: { $gt: { $numberLong: "0" }, $lt: { $numberLong: "2"} }} + result: [*doc1] + + - description: "Find with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedLong: { $numberLong: "0" } } + result: [*doc0] + - name: find + arguments: + filter: { encryptedLong: { $numberLong: "1" } } + result: [*doc1] + + - description: "Find with full range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedLong: { $gte: {$numberLong: "0"}, $lte: {$numberLong: "200"} } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedLong: { $in: [ {$numberLong: "0"} ] } } + result: [*doc0] + + - description: "Insert out of range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedLong: { $numberLong: "-1" }} + result: + errorContains: value must be greater than or equal to the minimum value + + - description: "Insert min and max" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: *doc0 + - name: insertOne + arguments: + document: &doc200 { _id: 200, encryptedLong: { $numberLong: "200" }} + - name: find + arguments: + filter: {} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc200] + + - description: "Aggregate with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $gte: { $numberLong: "0" } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $gt: { $numberLong: "1" } }} } + result: [] + + - description: "Aggregate with $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $lt: { $numberLong: "1" } }} } + result: [*doc0] + + - description: "Aggregate with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $lte: { $numberLong: "1" } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $lt below min" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $lt: { $numberLong: "0" } }} } + result: + errorContains: must be greater than the range minimum + + - description: "Aggregate with $gt above max" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $gt: { $numberLong: "200" } }} } + result: + errorContains: must be less than the range maximum + + - description: "Aggregate with $gt and $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $gt: { $numberLong: "0" }, $lt: { $numberLong: "2"} }} } + result: [*doc1] + + - description: "Aggregate with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $numberLong: "0" } } } + result: [*doc0] + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $numberLong: "1" } } } + result: [*doc1] + + - description: "Aggregate with full range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $gte: {$numberLong: "0"}, $lte: {$numberLong: "200"} } } } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $in: [ {$numberLong: "0"} ] } } } + result: [*doc0] + + - description: "Wrong type: Insert Double" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: { _id: 0, encryptedLong: { $numberDouble: "0" }} } + result: + # Expect an error from mongocryptd. + errorContains: "cannot encrypt element" + + - description: "Wrong type: Find Double" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: find + arguments: + filter: { encryptedLong: { $gte: { $numberDouble: "0" } }} + result: + # expect an error from libmongocrypt. + errorContains: "field type is not supported" \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Long-Delete.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-Long-Delete.json new file mode 100644 index 00000000000..89e18984065 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Long-Delete.json @@ -0,0 +1,437 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Long. Delete.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "deleteOne", + "arguments": { + "filter": { + "encryptedLong": { + "$gt": { + "$numberLong": "0" + } + } + } + }, + "result": { + "deletedCount": 1 + } + } + ], + "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": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedLong": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedLong": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "delete": "default", + "deletes": [ + { + "q": { + "encryptedLong": { + "$gt": { + "$binary": { + "base64": "CnEFAAADcGF5bG9hZABBBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVjACAAAAAAKs6X8gaa+AJ6PpxVicsk7iCXpSYIkQ5zB0KnXqlfCWgAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWMAIAAAAABYTWOKsDZ7cgPhiKDR2bE5CFGz4MuEwZbzZ0z46eGzBQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFYwAgAAAAABSqgKlxv5LXoxUO3zNmqGztlZhL2S150WiR/eycv7CWAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVjACAAAAAAfsofSP7nQHv8ic8ZW0aNlWxplS46Z+mywPR4rQk+wcgAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWMAIAAAAADu3L42J/oqNLe/XNYpodf2fhmPdcLhqO4J24o0hiveXwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFYwAgAAAAAAUljtCIvjRnLB2UPTEeRIz11Snl5SOBUNaXMXig4rqKAAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVjACAAAAAAD51NYesiO4Fo7w7iWBfqAFxEqkaDVctpvzZ28nT4SE8AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWMAIAAAAABJDtFhJ2tPbowp1UUmOCN/rqSqHRL1dtMu0c47vIlK4wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FYwAgAAAAAE6kvmXPqTnYIH4EJmNhy8OLVJZFOmdiBXLMorhELjKWAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVjACAAAAAA85AiE+bNFAYQTXQAFexgeczdVhf8FUnf16WzJlI/kmsAAAVlACAAAAAA65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+wSY20AAAAAAAAAAAAAEHBheWxvYWRJZAAAAAAAEGZpcnN0T3BlcmF0b3IAAQAAAAA=", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ], + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + }, + "deleteTokens": { + "default.default": { + "encryptedLong": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + } + }, + "command_name": "delete" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedLong": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Long-Delete.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-Long-Delete.yml new file mode 100644 index 00000000000..12080d60aef --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Long-Delete.yml @@ -0,0 +1,185 @@ +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedLong', 'bsonType': 'long', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberLong': '0'}, 'max': {'$numberLong': '200'}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "FLE2 Range Long. Delete." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedLong: { $numberLong: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedLong: { $numberLong: "1" } } + - name: deleteOne + arguments: + filter: { "encryptedLong": { $gt: {$numberLong: "0" }} } + result: + deletedCount: 1 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + delete: *collection_name + deletes: [ + { + "q": { + "encryptedLong": { + "$gt": { + "$binary": { + "base64": "CnEFAAADcGF5bG9hZABBBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVjACAAAAAAKs6X8gaa+AJ6PpxVicsk7iCXpSYIkQ5zB0KnXqlfCWgAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWMAIAAAAABYTWOKsDZ7cgPhiKDR2bE5CFGz4MuEwZbzZ0z46eGzBQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFYwAgAAAAABSqgKlxv5LXoxUO3zNmqGztlZhL2S150WiR/eycv7CWAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVjACAAAAAAfsofSP7nQHv8ic8ZW0aNlWxplS46Z+mywPR4rQk+wcgAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWMAIAAAAADu3L42J/oqNLe/XNYpodf2fhmPdcLhqO4J24o0hiveXwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFYwAgAAAAAAUljtCIvjRnLB2UPTEeRIz11Snl5SOBUNaXMXig4rqKAAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVjACAAAAAAD51NYesiO4Fo7w7iWBfqAFxEqkaDVctpvzZ28nT4SE8AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWMAIAAAAABJDtFhJ2tPbowp1UUmOCN/rqSqHRL1dtMu0c47vIlK4wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FYwAgAAAAAE6kvmXPqTnYIH4EJmNhy8OLVJZFOmdiBXLMorhELjKWAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVjACAAAAAA85AiE+bNFAYQTXQAFexgeczdVhf8FUnf16WzJlI/kmsAAAVlACAAAAAA65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+wSY20AAAAAAAAAAAAAEHBheWxvYWRJZAAAAAAAEGZpcnN0T3BlcmF0b3IAAQAAAAA=", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ] + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + "deleteTokens": { + "default.default": { + "encryptedLong": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + command_name: delete + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedLong": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + } \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Long-FindOneAndUpdate.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-Long-FindOneAndUpdate.json new file mode 100644 index 00000000000..59342a343ad --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Long-FindOneAndUpdate.json @@ -0,0 +1,512 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Long. FindOneAndUpdate.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "encryptedLong": { + "$gt": { + "$numberLong": "0" + } + } + }, + "update": { + "$set": { + "encryptedLong": { + "$numberLong": "2" + } + } + }, + "returnDocument": "Before" + }, + "result": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + ], + "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": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedLong": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedLong": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "findAndModify": "default", + "query": { + "encryptedLong": { + "$gt": { + "$binary": { + "base64": "CnEFAAADcGF5bG9hZABBBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVjACAAAAAAKs6X8gaa+AJ6PpxVicsk7iCXpSYIkQ5zB0KnXqlfCWgAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWMAIAAAAABYTWOKsDZ7cgPhiKDR2bE5CFGz4MuEwZbzZ0z46eGzBQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFYwAgAAAAABSqgKlxv5LXoxUO3zNmqGztlZhL2S150WiR/eycv7CWAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVjACAAAAAAfsofSP7nQHv8ic8ZW0aNlWxplS46Z+mywPR4rQk+wcgAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWMAIAAAAADu3L42J/oqNLe/XNYpodf2fhmPdcLhqO4J24o0hiveXwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFYwAgAAAAAAUljtCIvjRnLB2UPTEeRIz11Snl5SOBUNaXMXig4rqKAAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVjACAAAAAAD51NYesiO4Fo7w7iWBfqAFxEqkaDVctpvzZ28nT4SE8AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWMAIAAAAABJDtFhJ2tPbowp1UUmOCN/rqSqHRL1dtMu0c47vIlK4wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FYwAgAAAAAE6kvmXPqTnYIH4EJmNhy8OLVJZFOmdiBXLMorhELjKWAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVjACAAAAAA85AiE+bNFAYQTXQAFexgeczdVhf8FUnf16WzJlI/kmsAAAVlACAAAAAA65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+wSY20AAAAAAAAAAAAAEHBheWxvYWRJZAAAAAAAEGZpcnN0T3BlcmF0b3IAAQAAAAA=", + "subType": "06" + } + } + } + }, + "update": { + "$set": { + "encryptedLong": { + "$$type": "binData" + } + } + }, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + }, + "deleteTokens": { + "default.default": { + "encryptedLong": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + } + }, + "command_name": "findAndModify" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedLong": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedLong": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hyDcE6QQjPrYJaIS/n7evEZFYcm31Tj89CpEYGF45cI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ty4cnzJdAlbQKnh7px3GEYjBnvO+jIOaKjoTRDtmh3M=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Long-FindOneAndUpdate.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-Long-FindOneAndUpdate.yml new file mode 100644 index 00000000000..a90e09c47c4 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Long-FindOneAndUpdate.yml @@ -0,0 +1,243 @@ +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedLong', 'bsonType': 'long', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberLong': '0'}, 'max': {'$numberLong': '200'}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "FLE2 Range Long. FindOneAndUpdate." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedLong: { $numberLong: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedLong: { $numberLong: "1" } } + - name: findOneAndUpdate + arguments: + filter: { encryptedLong: { $gt: {$numberLong: "0"}} } + update: { "$set": { "encryptedLong": {$numberLong: "2"}}} + returnDocument: Before + result: *doc1 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + findAndModify: *collection_name + query: { + "encryptedLong": { + "$gt": { + "$binary": { + "base64": "CnEFAAADcGF5bG9hZABBBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVjACAAAAAAKs6X8gaa+AJ6PpxVicsk7iCXpSYIkQ5zB0KnXqlfCWgAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWMAIAAAAABYTWOKsDZ7cgPhiKDR2bE5CFGz4MuEwZbzZ0z46eGzBQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFYwAgAAAAABSqgKlxv5LXoxUO3zNmqGztlZhL2S150WiR/eycv7CWAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVjACAAAAAAfsofSP7nQHv8ic8ZW0aNlWxplS46Z+mywPR4rQk+wcgAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWMAIAAAAADu3L42J/oqNLe/XNYpodf2fhmPdcLhqO4J24o0hiveXwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFYwAgAAAAAAUljtCIvjRnLB2UPTEeRIz11Snl5SOBUNaXMXig4rqKAAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVjACAAAAAAD51NYesiO4Fo7w7iWBfqAFxEqkaDVctpvzZ28nT4SE8AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWMAIAAAAABJDtFhJ2tPbowp1UUmOCN/rqSqHRL1dtMu0c47vIlK4wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FYwAgAAAAAE6kvmXPqTnYIH4EJmNhy8OLVJZFOmdiBXLMorhELjKWAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVjACAAAAAA85AiE+bNFAYQTXQAFexgeczdVhf8FUnf16WzJlI/kmsAAAVlACAAAAAA65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+wSY20AAAAAAAAAAAAAEHBheWxvYWRJZAAAAAAAEGZpcnN0T3BlcmF0b3IAAQAAAAA=", + "subType": "06" + } + } + } + } + update: { "$set": {"encryptedLong": { $$type: "binData" }} } + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + "deleteTokens": { + "default.default": { + "encryptedLong": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + command_name: findAndModify + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedLong": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + } + - + { + "_id": 1, + "encryptedLong": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hyDcE6QQjPrYJaIS/n7evEZFYcm31Tj89CpEYGF45cI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ty4cnzJdAlbQKnh7px3GEYjBnvO+jIOaKjoTRDtmh3M=", + "subType": "00" + } + } + ] + } \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Long-InsertFind.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-Long-InsertFind.json new file mode 100644 index 00000000000..882e52170d2 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Long-InsertFind.json @@ -0,0 +1,481 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Long. Insert and Find.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$gt": { + "$numberLong": "0" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + } + ], + "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": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedLong": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedLong": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "encryptedLong": { + "$gt": { + "$binary": { + "base64": "CnEFAAADcGF5bG9hZABBBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVjACAAAAAAKs6X8gaa+AJ6PpxVicsk7iCXpSYIkQ5zB0KnXqlfCWgAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWMAIAAAAABYTWOKsDZ7cgPhiKDR2bE5CFGz4MuEwZbzZ0z46eGzBQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFYwAgAAAAABSqgKlxv5LXoxUO3zNmqGztlZhL2S150WiR/eycv7CWAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVjACAAAAAAfsofSP7nQHv8ic8ZW0aNlWxplS46Z+mywPR4rQk+wcgAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWMAIAAAAADu3L42J/oqNLe/XNYpodf2fhmPdcLhqO4J24o0hiveXwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFYwAgAAAAAAUljtCIvjRnLB2UPTEeRIz11Snl5SOBUNaXMXig4rqKAAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVjACAAAAAAD51NYesiO4Fo7w7iWBfqAFxEqkaDVctpvzZ28nT4SE8AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWMAIAAAAABJDtFhJ2tPbowp1UUmOCN/rqSqHRL1dtMu0c47vIlK4wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FYwAgAAAAAE6kvmXPqTnYIH4EJmNhy8OLVJZFOmdiBXLMorhELjKWAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVjACAAAAAA85AiE+bNFAYQTXQAFexgeczdVhf8FUnf16WzJlI/kmsAAAVlACAAAAAA65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+wSY20AAAAAAAAAAAAAEHBheWxvYWRJZAAAAAAAEGZpcnN0T3BlcmF0b3IAAQAAAAA=", + "subType": "06" + } + } + } + }, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedLong": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedLong": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Long-InsertFind.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-Long-InsertFind.yml new file mode 100644 index 00000000000..f0c79b21dc7 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Long-InsertFind.yml @@ -0,0 +1,221 @@ +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedLong', 'bsonType': 'long', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberLong': '0'}, 'max': {'$numberLong': '200'}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "FLE2 Range Long. Insert and Find." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedLong: { $numberLong: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedLong: { $numberLong: "1" } } + - name: find + arguments: + filter: { encryptedLong: { $gt: { $numberLong: "0" } } } + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: + "encryptedLong": { + "$gt": { + "$binary": { + "base64": "CnEFAAADcGF5bG9hZABBBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVjACAAAAAAKs6X8gaa+AJ6PpxVicsk7iCXpSYIkQ5zB0KnXqlfCWgAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWMAIAAAAABYTWOKsDZ7cgPhiKDR2bE5CFGz4MuEwZbzZ0z46eGzBQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFYwAgAAAAABSqgKlxv5LXoxUO3zNmqGztlZhL2S150WiR/eycv7CWAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVjACAAAAAAfsofSP7nQHv8ic8ZW0aNlWxplS46Z+mywPR4rQk+wcgAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWMAIAAAAADu3L42J/oqNLe/XNYpodf2fhmPdcLhqO4J24o0hiveXwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFYwAgAAAAAAUljtCIvjRnLB2UPTEeRIz11Snl5SOBUNaXMXig4rqKAAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVjACAAAAAAD51NYesiO4Fo7w7iWBfqAFxEqkaDVctpvzZ28nT4SE8AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWMAIAAAAABJDtFhJ2tPbowp1UUmOCN/rqSqHRL1dtMu0c47vIlK4wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FYwAgAAAAAE6kvmXPqTnYIH4EJmNhy8OLVJZFOmdiBXLMorhELjKWAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVjACAAAAAA85AiE+bNFAYQTXQAFexgeczdVhf8FUnf16WzJlI/kmsAAAVlACAAAAAA65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+wSY20AAAAAAAAAAAAAEHBheWxvYWRJZAAAAAAAEGZpcnN0T3BlcmF0b3IAAQAAAAA=", + "subType": "06" + } + } + } + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedLong": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + } + - + { + "_id": 1, + "encryptedLong": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", + "subType": "00" + } + } + ] + } \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Long-Update.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-Long-Update.json new file mode 100644 index 00000000000..92e3e390a5f --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Long-Update.json @@ -0,0 +1,516 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Long. Update.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "updateOne", + "arguments": { + "filter": { + "encryptedLong": { + "$gt": { + "$numberLong": "0" + } + } + }, + "update": { + "$set": { + "encryptedLong": { + "$numberLong": "2" + } + } + } + }, + "result": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "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": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedLong": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedLong": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command_name": "update", + "command": { + "update": "default", + "ordered": true, + "updates": [ + { + "q": { + "encryptedLong": { + "$gt": { + "$binary": { + "base64": "CnEFAAADcGF5bG9hZABBBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVjACAAAAAAKs6X8gaa+AJ6PpxVicsk7iCXpSYIkQ5zB0KnXqlfCWgAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWMAIAAAAABYTWOKsDZ7cgPhiKDR2bE5CFGz4MuEwZbzZ0z46eGzBQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFYwAgAAAAABSqgKlxv5LXoxUO3zNmqGztlZhL2S150WiR/eycv7CWAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVjACAAAAAAfsofSP7nQHv8ic8ZW0aNlWxplS46Z+mywPR4rQk+wcgAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWMAIAAAAADu3L42J/oqNLe/XNYpodf2fhmPdcLhqO4J24o0hiveXwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFYwAgAAAAAAUljtCIvjRnLB2UPTEeRIz11Snl5SOBUNaXMXig4rqKAAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVjACAAAAAAD51NYesiO4Fo7w7iWBfqAFxEqkaDVctpvzZ28nT4SE8AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWMAIAAAAABJDtFhJ2tPbowp1UUmOCN/rqSqHRL1dtMu0c47vIlK4wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FYwAgAAAAAE6kvmXPqTnYIH4EJmNhy8OLVJZFOmdiBXLMorhELjKWAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVjACAAAAAA85AiE+bNFAYQTXQAFexgeczdVhf8FUnf16WzJlI/kmsAAAVlACAAAAAA65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+wSY20AAAAAAAAAAAAAEHBheWxvYWRJZAAAAAAAEGZpcnN0T3BlcmF0b3IAAQAAAAA=", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encryptedLong": { + "$$type": "binData" + } + } + } + } + ], + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + }, + "deleteTokens": { + "default.default": { + "encryptedLong": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + }, + "$db": "default" + } + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedLong": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedLong": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hyDcE6QQjPrYJaIS/n7evEZFYcm31Tj89CpEYGF45cI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ty4cnzJdAlbQKnh7px3GEYjBnvO+jIOaKjoTRDtmh3M=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-Long-Update.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-Long-Update.yml new file mode 100644 index 00000000000..20783a7cdd2 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-Long-Update.yml @@ -0,0 +1,260 @@ +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedLong', 'bsonType': 'long', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberLong': '0'}, 'max': {'$numberLong': '200'}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "FLE2 Range Long. Update." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedLong: { $numberLong: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedLong: { $numberLong: "1" } } + - name: updateOne + arguments: + filter: { encryptedLong: { $gt: { $numberLong: "0" } } } + update: { "$set": { "encryptedLong": { $numberLong: "2" } }} + result: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: *encrypted_fields + command_name: insert + - command_started_event: + command_name: update + command: + { + "update": "default", + "ordered": true, + "updates": [ + { + "q": { + "encryptedLong": { + "$gt": { + "$binary": { + "base64": "CnEFAAADcGF5bG9hZABBBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVjACAAAAAAKs6X8gaa+AJ6PpxVicsk7iCXpSYIkQ5zB0KnXqlfCWgAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWMAIAAAAABYTWOKsDZ7cgPhiKDR2bE5CFGz4MuEwZbzZ0z46eGzBQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFYwAgAAAAABSqgKlxv5LXoxUO3zNmqGztlZhL2S150WiR/eycv7CWAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVjACAAAAAAfsofSP7nQHv8ic8ZW0aNlWxplS46Z+mywPR4rQk+wcgAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWMAIAAAAADu3L42J/oqNLe/XNYpodf2fhmPdcLhqO4J24o0hiveXwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFYwAgAAAAAAUljtCIvjRnLB2UPTEeRIz11Snl5SOBUNaXMXig4rqKAAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVjACAAAAAAD51NYesiO4Fo7w7iWBfqAFxEqkaDVctpvzZ28nT4SE8AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWMAIAAAAABJDtFhJ2tPbowp1UUmOCN/rqSqHRL1dtMu0c47vIlK4wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FYwAgAAAAAE6kvmXPqTnYIH4EJmNhy8OLVJZFOmdiBXLMorhELjKWAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVjACAAAAAA85AiE+bNFAYQTXQAFexgeczdVhf8FUnf16WzJlI/kmsAAAVlACAAAAAA65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+wSY20AAAAAAAAAAAAAEHBheWxvYWRJZAAAAAAAEGZpcnN0T3BlcmF0b3IAAQAAAAA=", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encryptedLong": { $$type: "binData" } + } + } + } + ], + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": *encrypted_fields + }, + "deleteTokens": { + "default.default": { + "encryptedLong": { + "e": { + "$binary": { + "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", + "subType": "00" + } + }, + "o": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + } + } + } + } + }, + "$db": "default" + } + + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedLong": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + } + - + { + "_id": 1, + "encryptedLong": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hyDcE6QQjPrYJaIS/n7evEZFYcm31Tj89CpEYGF45cI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ty4cnzJdAlbQKnh7px3GEYjBnvO+jIOaKjoTRDtmh3M=", + "subType": "00" + } + } + ] + } \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-WrongType.json b/specifications/client-side-encryption/tests/legacy/fle2-Range-WrongType.json new file mode 100644 index 00000000000..9eddf1c99c1 --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-WrongType.json @@ -0,0 +1,162 @@ +{ + "runOn": [ + { + "minServerVersion": "6.2.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "escCollection": "enxcol_.default.esc", + "eccCollection": "enxcol_.default.ecc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "Wrong type: Insert Double", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberDouble": "0" + } + } + }, + "result": { + "errorContains": "cannot encrypt element" + } + } + ] + }, + { + "description": "Wrong type: Find Double", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$gte": { + "$numberDouble": "0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": { + "errorContains": "field type is not supported" + } + } + ] + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/fle2-Range-WrongType.yml b/specifications/client-side-encryption/tests/legacy/fle2-Range-WrongType.yml new file mode 100644 index 00000000000..9bc2c53dfdf --- /dev/null +++ b/specifications/client-side-encryption/tests/legacy/fle2-Range-WrongType.yml @@ -0,0 +1,41 @@ +# Test correctness results. +# Does not include command monitoring expectations or outcome assertions to make tests more readable. +# Requires libmongocrypt 1.7.0. +runOn: + - minServerVersion: "6.2.0" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'escCollection': 'enxcol_.default.esc', 'eccCollection': 'enxcol_.default.ecc', 'ecocCollection': 'enxcol_.default.ecoc', 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedInt', 'bsonType': 'int', 'queries': {'queryType': 'rangePreview', 'contention': {'$numberLong': '0'}, 'sparsity': {'$numberLong': '1'}, 'min': {'$numberInt': '0'}, 'max': {'$numberInt': '200'}}}]} +key_vault_data: [ {'_id': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1648914851981'}}, 'updateDate': {'$date': {'$numberLong': '1648914851981'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}} ] +tests: + - description: "Wrong type: Insert Double" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: { _id: 0, encryptedInt: { $numberDouble: "0" }} } + result: + # Expect an error from mongocryptd. + errorContains: "cannot encrypt element" + + - description: "Wrong type: Find Double" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: { _id: 0, encryptedInt: { $numberInt: "0" }} } + - name: find + arguments: + filter: { encryptedInt: { $gte: { $numberDouble: "0" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: + # expect an error from libmongocrypt. + errorContains: "field type is not supported" \ No newline at end of file diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-Update.json b/specifications/client-side-encryption/tests/legacy/fle2-Update.json similarity index 99% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-Update.json rename to specifications/client-side-encryption/tests/legacy/fle2-Update.json index 87830af32dd..090f44f9acd 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-Update.json +++ b/specifications/client-side-encryption/tests/legacy/fle2-Update.json @@ -4,7 +4,8 @@ "minServerVersion": "6.0.0", "topology": [ "replicaset", - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-Update.yml b/specifications/client-side-encryption/tests/legacy/fle2-Update.yml similarity index 99% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-Update.yml rename to specifications/client-side-encryption/tests/legacy/fle2-Update.yml index 19bb549b23b..929f4a3a38a 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-Update.yml +++ b/specifications/client-side-encryption/tests/legacy/fle2-Update.yml @@ -1,7 +1,7 @@ runOn: - minServerVersion: "6.0.0" # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded" ] + topology: [ "replicaset", "sharded", "load-balanced" ] database_name: &database_name "default" collection_name: &collection_name "default" data: [] diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-validatorAndPartialFieldExpression.json b/specifications/client-side-encryption/tests/legacy/fle2-validatorAndPartialFieldExpression.json similarity index 99% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-validatorAndPartialFieldExpression.json rename to specifications/client-side-encryption/tests/legacy/fle2-validatorAndPartialFieldExpression.json index fab36f75a1e..e70ca7c72d5 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-validatorAndPartialFieldExpression.json +++ b/specifications/client-side-encryption/tests/legacy/fle2-validatorAndPartialFieldExpression.json @@ -4,7 +4,8 @@ "minServerVersion": "6.0.0", "topology": [ "replicaset", - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-validatorAndPartialFieldExpression.yml b/specifications/client-side-encryption/tests/legacy/fle2-validatorAndPartialFieldExpression.yml similarity index 99% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-validatorAndPartialFieldExpression.yml rename to specifications/client-side-encryption/tests/legacy/fle2-validatorAndPartialFieldExpression.yml index b85a7c876d0..9463ddecad1 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/fle2-validatorAndPartialFieldExpression.yml +++ b/specifications/client-side-encryption/tests/legacy/fle2-validatorAndPartialFieldExpression.yml @@ -3,7 +3,7 @@ runOn: # Require server version 6.0.0 to get behavior added in SERVER-64911. - minServerVersion: "6.0.0" # FLE 2 Encrypted collections are not supported on standalone. - topology: [ "replicaset", "sharded" ] + topology: [ "replicaset", "sharded", "load-balanced" ] database_name: &database_name "default" collection_name: &collection_name "default" diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/gcpKMS.json b/specifications/client-side-encryption/tests/legacy/gcpKMS.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/gcpKMS.json rename to specifications/client-side-encryption/tests/legacy/gcpKMS.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/gcpKMS.yml b/specifications/client-side-encryption/tests/legacy/gcpKMS.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/gcpKMS.yml rename to specifications/client-side-encryption/tests/legacy/gcpKMS.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/getMore.json b/specifications/client-side-encryption/tests/legacy/getMore.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/getMore.json rename to specifications/client-side-encryption/tests/legacy/getMore.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/getMore.yml b/specifications/client-side-encryption/tests/legacy/getMore.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/getMore.yml rename to specifications/client-side-encryption/tests/legacy/getMore.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/insert.json b/specifications/client-side-encryption/tests/legacy/insert.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/insert.json rename to specifications/client-side-encryption/tests/legacy/insert.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/insert.yml b/specifications/client-side-encryption/tests/legacy/insert.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/insert.yml rename to specifications/client-side-encryption/tests/legacy/insert.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/keyAltName.json b/specifications/client-side-encryption/tests/legacy/keyAltName.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/keyAltName.json rename to specifications/client-side-encryption/tests/legacy/keyAltName.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/keyAltName.yml b/specifications/client-side-encryption/tests/legacy/keyAltName.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/keyAltName.yml rename to specifications/client-side-encryption/tests/legacy/keyAltName.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/kmipKMS.json b/specifications/client-side-encryption/tests/legacy/kmipKMS.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/kmipKMS.json rename to specifications/client-side-encryption/tests/legacy/kmipKMS.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/kmipKMS.yml b/specifications/client-side-encryption/tests/legacy/kmipKMS.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/kmipKMS.yml rename to specifications/client-side-encryption/tests/legacy/kmipKMS.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/localKMS.json b/specifications/client-side-encryption/tests/legacy/localKMS.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/localKMS.json rename to specifications/client-side-encryption/tests/legacy/localKMS.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/localKMS.yml b/specifications/client-side-encryption/tests/legacy/localKMS.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/localKMS.yml rename to specifications/client-side-encryption/tests/legacy/localKMS.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/localSchema.json b/specifications/client-side-encryption/tests/legacy/localSchema.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/localSchema.json rename to specifications/client-side-encryption/tests/legacy/localSchema.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/localSchema.yml b/specifications/client-side-encryption/tests/legacy/localSchema.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/localSchema.yml rename to specifications/client-side-encryption/tests/legacy/localSchema.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/malformedCiphertext.json b/specifications/client-side-encryption/tests/legacy/malformedCiphertext.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/malformedCiphertext.json rename to specifications/client-side-encryption/tests/legacy/malformedCiphertext.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/malformedCiphertext.yml b/specifications/client-side-encryption/tests/legacy/malformedCiphertext.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/malformedCiphertext.yml rename to specifications/client-side-encryption/tests/legacy/malformedCiphertext.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/maxWireVersion.json b/specifications/client-side-encryption/tests/legacy/maxWireVersion.json similarity index 98% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/maxWireVersion.json rename to specifications/client-side-encryption/tests/legacy/maxWireVersion.json index c1088a0ecf4..f04f58dffde 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/maxWireVersion.json +++ b/specifications/client-side-encryption/tests/legacy/maxWireVersion.json @@ -1,7 +1,7 @@ { "runOn": [ { - "maxServerVersion": "4.0" + "maxServerVersion": "4.0.99" } ], "database_name": "default", diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/maxWireVersion.yml b/specifications/client-side-encryption/tests/legacy/maxWireVersion.yml similarity index 97% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/maxWireVersion.yml rename to specifications/client-side-encryption/tests/legacy/maxWireVersion.yml index 8df590d809a..87c4c993f9b 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/maxWireVersion.yml +++ b/specifications/client-side-encryption/tests/legacy/maxWireVersion.yml @@ -1,5 +1,5 @@ runOn: - - maxServerVersion: "4.0" + - maxServerVersion: "4.0.99" database_name: &database_name "default" collection_name: &collection_name "default" diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/missingKey.json b/specifications/client-side-encryption/tests/legacy/missingKey.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/missingKey.json rename to specifications/client-side-encryption/tests/legacy/missingKey.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/missingKey.yml b/specifications/client-side-encryption/tests/legacy/missingKey.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/missingKey.yml rename to specifications/client-side-encryption/tests/legacy/missingKey.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/noSchema.json b/specifications/client-side-encryption/tests/legacy/noSchema.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/noSchema.json rename to specifications/client-side-encryption/tests/legacy/noSchema.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/noSchema.yml b/specifications/client-side-encryption/tests/legacy/noSchema.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/noSchema.yml rename to specifications/client-side-encryption/tests/legacy/noSchema.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/replaceOne.json b/specifications/client-side-encryption/tests/legacy/replaceOne.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/replaceOne.json rename to specifications/client-side-encryption/tests/legacy/replaceOne.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/replaceOne.yml b/specifications/client-side-encryption/tests/legacy/replaceOne.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/replaceOne.yml rename to specifications/client-side-encryption/tests/legacy/replaceOne.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/types.json b/specifications/client-side-encryption/tests/legacy/types.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/types.json rename to specifications/client-side-encryption/tests/legacy/types.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/types.yml b/specifications/client-side-encryption/tests/legacy/types.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/types.yml rename to specifications/client-side-encryption/tests/legacy/types.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/unsupportedCommand.json b/specifications/client-side-encryption/tests/legacy/unsupportedCommand.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/unsupportedCommand.json rename to specifications/client-side-encryption/tests/legacy/unsupportedCommand.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/unsupportedCommand.yml b/specifications/client-side-encryption/tests/legacy/unsupportedCommand.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/unsupportedCommand.yml rename to specifications/client-side-encryption/tests/legacy/unsupportedCommand.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/updateMany.json b/specifications/client-side-encryption/tests/legacy/updateMany.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/updateMany.json rename to specifications/client-side-encryption/tests/legacy/updateMany.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/updateMany.yml b/specifications/client-side-encryption/tests/legacy/updateMany.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/updateMany.yml rename to specifications/client-side-encryption/tests/legacy/updateMany.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/updateOne.json b/specifications/client-side-encryption/tests/legacy/updateOne.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/updateOne.json rename to specifications/client-side-encryption/tests/legacy/updateOne.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/updateOne.yml b/specifications/client-side-encryption/tests/legacy/updateOne.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/updateOne.yml rename to specifications/client-side-encryption/tests/legacy/updateOne.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/validatorAndPartialFieldExpression.json b/specifications/client-side-encryption/tests/legacy/validatorAndPartialFieldExpression.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/validatorAndPartialFieldExpression.json rename to specifications/client-side-encryption/tests/legacy/validatorAndPartialFieldExpression.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/validatorAndPartialFieldExpression.yml b/specifications/client-side-encryption/tests/legacy/validatorAndPartialFieldExpression.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/legacy/validatorAndPartialFieldExpression.yml rename to specifications/client-side-encryption/tests/legacy/validatorAndPartialFieldExpression.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/addKeyAltName.json b/specifications/client-side-encryption/tests/unified/addKeyAltName.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/addKeyAltName.json rename to specifications/client-side-encryption/tests/unified/addKeyAltName.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/addKeyAltName.yml b/specifications/client-side-encryption/tests/unified/addKeyAltName.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/addKeyAltName.yml rename to specifications/client-side-encryption/tests/unified/addKeyAltName.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/createDataKey-kms_providers-invalid.json b/specifications/client-side-encryption/tests/unified/createDataKey-kms_providers-invalid.json similarity index 97% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/createDataKey-kms_providers-invalid.json rename to specifications/client-side-encryption/tests/unified/createDataKey-kms_providers-invalid.json index 16cf6ca70dd..2344a61a95b 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/createDataKey-kms_providers-invalid.json +++ b/specifications/client-side-encryption/tests/unified/createDataKey-kms_providers-invalid.json @@ -1,5 +1,5 @@ { - "description": "createDataKey-provider-invalid", + "description": "createDataKey-kms_providers-invalid", "schemaVersion": "1.8", "runOnRequirements": [ { diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/createDataKey-kms_providers-invalid.yml b/specifications/client-side-encryption/tests/unified/createDataKey-kms_providers-invalid.yml similarity index 97% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/createDataKey-kms_providers-invalid.yml rename to specifications/client-side-encryption/tests/unified/createDataKey-kms_providers-invalid.yml index 70e51acbdc7..f692a090752 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/createDataKey-kms_providers-invalid.yml +++ b/specifications/client-side-encryption/tests/unified/createDataKey-kms_providers-invalid.yml @@ -1,4 +1,4 @@ -description: createDataKey-provider-invalid +description: createDataKey-kms_providers-invalid schemaVersion: "1.8" diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/createDataKey.json b/specifications/client-side-encryption/tests/unified/createDataKey.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/createDataKey.json rename to specifications/client-side-encryption/tests/unified/createDataKey.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/createDataKey.yml b/specifications/client-side-encryption/tests/unified/createDataKey.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/createDataKey.yml rename to specifications/client-side-encryption/tests/unified/createDataKey.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/deleteKey.json b/specifications/client-side-encryption/tests/unified/deleteKey.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/deleteKey.json rename to specifications/client-side-encryption/tests/unified/deleteKey.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/deleteKey.yml b/specifications/client-side-encryption/tests/unified/deleteKey.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/deleteKey.yml rename to specifications/client-side-encryption/tests/unified/deleteKey.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/getKey.json b/specifications/client-side-encryption/tests/unified/getKey.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/getKey.json rename to specifications/client-side-encryption/tests/unified/getKey.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/getKey.yml b/specifications/client-side-encryption/tests/unified/getKey.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/getKey.yml rename to specifications/client-side-encryption/tests/unified/getKey.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/getKeyByAltName.json b/specifications/client-side-encryption/tests/unified/getKeyByAltName.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/getKeyByAltName.json rename to specifications/client-side-encryption/tests/unified/getKeyByAltName.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/getKeyByAltName.yml b/specifications/client-side-encryption/tests/unified/getKeyByAltName.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/getKeyByAltName.yml rename to specifications/client-side-encryption/tests/unified/getKeyByAltName.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/getKeys.json b/specifications/client-side-encryption/tests/unified/getKeys.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/getKeys.json rename to specifications/client-side-encryption/tests/unified/getKeys.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/getKeys.yml b/specifications/client-side-encryption/tests/unified/getKeys.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/getKeys.yml rename to specifications/client-side-encryption/tests/unified/getKeys.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/removeKeyAltName.json b/specifications/client-side-encryption/tests/unified/removeKeyAltName.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/removeKeyAltName.json rename to specifications/client-side-encryption/tests/unified/removeKeyAltName.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/removeKeyAltName.yml b/specifications/client-side-encryption/tests/unified/removeKeyAltName.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/removeKeyAltName.yml rename to specifications/client-side-encryption/tests/unified/removeKeyAltName.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/rewrapManyDataKey-decrypt_failure.json b/specifications/client-side-encryption/tests/unified/rewrapManyDataKey-decrypt_failure.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/rewrapManyDataKey-decrypt_failure.json rename to specifications/client-side-encryption/tests/unified/rewrapManyDataKey-decrypt_failure.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/rewrapManyDataKey-decrypt_failure.yml b/specifications/client-side-encryption/tests/unified/rewrapManyDataKey-decrypt_failure.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/rewrapManyDataKey-decrypt_failure.yml rename to specifications/client-side-encryption/tests/unified/rewrapManyDataKey-decrypt_failure.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/rewrapManyDataKey-encrypt_failure.json b/specifications/client-side-encryption/tests/unified/rewrapManyDataKey-encrypt_failure.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/rewrapManyDataKey-encrypt_failure.json rename to specifications/client-side-encryption/tests/unified/rewrapManyDataKey-encrypt_failure.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/rewrapManyDataKey-encrypt_failure.yml b/specifications/client-side-encryption/tests/unified/rewrapManyDataKey-encrypt_failure.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/rewrapManyDataKey-encrypt_failure.yml rename to specifications/client-side-encryption/tests/unified/rewrapManyDataKey-encrypt_failure.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/rewrapManyDataKey.json b/specifications/client-side-encryption/tests/unified/rewrapManyDataKey.json similarity index 98% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/rewrapManyDataKey.json rename to specifications/client-side-encryption/tests/unified/rewrapManyDataKey.json index 7e3abb12743..89860de0c07 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/rewrapManyDataKey.json +++ b/specifications/client-side-encryption/tests/unified/rewrapManyDataKey.json @@ -1,5 +1,5 @@ { - "description": "rewrapManyDataKey-kms_providers", + "description": "rewrapManyDataKey", "schemaVersion": "1.8", "runOnRequirements": [ { @@ -128,7 +128,7 @@ ], "keyMaterial": { "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEGkNTybTc7Eyif0f+qqE0lAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDB2j78AeuIQxcRh8cQIBEIB7vj9buHEaT7XHFIsKBJiyzZRmNnjvqMK5LSdzonKdx97jlqauvPvTDXSsdQDcspUs5oLrGmAXpbFResscxmbwZoKgUtWiuIOpeAcYuszCiMKt15s1WIMLDXUhYtfCmhRhekvgHnRAaK4HJMlGE+lKJXYI84E0b86Cd/g+", + "base64": "pr01l7qDygUkFE/0peFwpnNlv3iIy8zrQK38Q9i12UCN2jwZHDmfyx8wokiIKMb9kAleeY+vnt3Cf1MKu9kcDmI+KxbNDd+V3ytAAGzOVLDJr77CiWjF9f8ntkXRHrAY9WwnVDANYkDwXlyU0Y2GQFTiW65jiQhUtYLYH63Tk48SsJuQvnWw1Q+PzY8ga+QeVec8wbcThwtm+r2IHsCFnc72Gv73qq7weISw+O4mN08z3wOp5FOS2ZM3MK7tBGmPdBcktW7F8ODGsOQ1FU53OrWUnyX2aTi2ftFFFMWVHqQo7EYuBZHru8RRODNKMyQk0BFfKovAeTAVRv9WH9QU7g==", "subType": "00" } }, @@ -196,7 +196,7 @@ ], "keyMaterial": { "$binary": { - "base64": "VoI9J8HusQ3u2gT9i8Awgg/6W4/igvLwRzn3SRDGx0Dl/1ayDMubphOw0ONPVKfuvS6HL3e4gAoCJ/uEz2KLFTVsEqYCpMhfAhgXxm8Ena8vDcOkCzFX+euvN/N2ES3wpzAD18b3qIH0MbBwKJP82d5GQ4pVfGnPW8Ujp9aO1qC/s0EqNqYyzJ1SyzhV9lAjHHGIENYJx+bBrekg2EeZBA==", + "base64": "CklVctHzke4mcytd0TxGqvepkdkQN8NUF4+jV7aZQITAKdz6WjdDpq3lMt9nSzWGG2vAEfvRb3mFEVjV57qqGqxjq2751gmiMRHXz0btStbIK3mQ5xbY9kdye4tsixlCryEwQONr96gwlwKKI9Nubl9/8+uRF6tgYjje7Q7OjauEf1SrJwKcoQ3WwnjZmEqAug0kImCpJ/irhdqPzivRiA==", "subType": "00" } }, diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/rewrapManyDataKey.yml b/specifications/client-side-encryption/tests/unified/rewrapManyDataKey.yml similarity index 96% rename from tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/rewrapManyDataKey.yml rename to specifications/client-side-encryption/tests/unified/rewrapManyDataKey.yml index 28009f54733..51415586838 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/tests/unified/rewrapManyDataKey.yml +++ b/specifications/client-side-encryption/tests/unified/rewrapManyDataKey.yml @@ -2,7 +2,7 @@ # 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-kms_providers +description: rewrapManyDataKey schemaVersion: "1.8" @@ -50,7 +50,7 @@ initialData: region: us-east-1 - _id: &azure_key_id { $binary: { base64: YXp1cmVhenVyZWF6dXJlYQ==, subType: "04" } } keyAltNames: ["azure_key"] - keyMaterial: { $binary: { base64: AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEGkNTybTc7Eyif0f+qqE0lAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDB2j78AeuIQxcRh8cQIBEIB7vj9buHEaT7XHFIsKBJiyzZRmNnjvqMK5LSdzonKdx97jlqauvPvTDXSsdQDcspUs5oLrGmAXpbFResscxmbwZoKgUtWiuIOpeAcYuszCiMKt15s1WIMLDXUhYtfCmhRhekvgHnRAaK4HJMlGE+lKJXYI84E0b86Cd/g+, subType: "00" } } + keyMaterial: { $binary: { base64: pr01l7qDygUkFE/0peFwpnNlv3iIy8zrQK38Q9i12UCN2jwZHDmfyx8wokiIKMb9kAleeY+vnt3Cf1MKu9kcDmI+KxbNDd+V3ytAAGzOVLDJr77CiWjF9f8ntkXRHrAY9WwnVDANYkDwXlyU0Y2GQFTiW65jiQhUtYLYH63Tk48SsJuQvnWw1Q+PzY8ga+QeVec8wbcThwtm+r2IHsCFnc72Gv73qq7weISw+O4mN08z3wOp5FOS2ZM3MK7tBGmPdBcktW7F8ODGsOQ1FU53OrWUnyX2aTi2ftFFFMWVHqQo7EYuBZHru8RRODNKMyQk0BFfKovAeTAVRv9WH9QU7g==, subType: "00" } } creationDate: { $date: { $numberLong: "1641024000000" } } updateDate: { $date: { $numberLong: "1641024000000" } } status: 1 @@ -72,7 +72,7 @@ initialData: keyName: key-name-csfle - _id: &kmip_key_id { $binary: { base64: a21pcGttaXBrbWlwa21pcA==, subType: "04" } } keyAltNames: ["kmip_key"] - keyMaterial: { $binary: { base64: VoI9J8HusQ3u2gT9i8Awgg/6W4/igvLwRzn3SRDGx0Dl/1ayDMubphOw0ONPVKfuvS6HL3e4gAoCJ/uEz2KLFTVsEqYCpMhfAhgXxm8Ena8vDcOkCzFX+euvN/N2ES3wpzAD18b3qIH0MbBwKJP82d5GQ4pVfGnPW8Ujp9aO1qC/s0EqNqYyzJ1SyzhV9lAjHHGIENYJx+bBrekg2EeZBA==, subType: "00" } } + keyMaterial: { $binary: { base64: CklVctHzke4mcytd0TxGqvepkdkQN8NUF4+jV7aZQITAKdz6WjdDpq3lMt9nSzWGG2vAEfvRb3mFEVjV57qqGqxjq2751gmiMRHXz0btStbIK3mQ5xbY9kdye4tsixlCryEwQONr96gwlwKKI9Nubl9/8+uRF6tgYjje7Q7OjauEf1SrJwKcoQ3WwnjZmEqAug0kImCpJ/irhdqPzivRiA==, subType: "00" } } creationDate: { $date: { $numberLong: "1641024000000" } } updateDate: { $date: { $numberLong: "1641024000000" } } status: 1 diff --git a/tests/MongoDB.Driver.Tests/Specifications/collection-management/tests/README.rst b/specifications/collection-management/tests/README.rst similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/collection-management/tests/README.rst rename to specifications/collection-management/tests/README.rst diff --git a/tests/MongoDB.Driver.Tests/Specifications/collection-management/tests/clustered-indexes.json b/specifications/collection-management/tests/clustered-indexes.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/collection-management/tests/clustered-indexes.json rename to specifications/collection-management/tests/clustered-indexes.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/collection-management/tests/clustered-indexes.yml b/specifications/collection-management/tests/clustered-indexes.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/collection-management/tests/clustered-indexes.yml rename to specifications/collection-management/tests/clustered-indexes.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/collection-management/tests/createCollection-pre_and_post_images.json b/specifications/collection-management/tests/createCollection-pre_and_post_images.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/collection-management/tests/createCollection-pre_and_post_images.json rename to specifications/collection-management/tests/createCollection-pre_and_post_images.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/collection-management/tests/createCollection-pre_and_post_images.yml b/specifications/collection-management/tests/createCollection-pre_and_post_images.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/collection-management/tests/createCollection-pre_and_post_images.yml rename to specifications/collection-management/tests/createCollection-pre_and_post_images.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/collection-management/tests/timeseries-collection.json b/specifications/collection-management/tests/timeseries-collection.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/collection-management/tests/timeseries-collection.json rename to specifications/collection-management/tests/timeseries-collection.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/collection-management/tests/timeseries-collection.yml b/specifications/collection-management/tests/timeseries-collection.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/collection-management/tests/timeseries-collection.yml rename to specifications/collection-management/tests/timeseries-collection.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/README.rst b/specifications/command-logging-and-monitoring/tests/legacy/README.rst similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/README.rst rename to specifications/command-logging-and-monitoring/tests/legacy/README.rst diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/bulkWrite.json b/specifications/command-logging-and-monitoring/tests/legacy/bulkWrite.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/bulkWrite.json rename to specifications/command-logging-and-monitoring/tests/legacy/bulkWrite.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/bulkWrite.yml b/specifications/command-logging-and-monitoring/tests/legacy/bulkWrite.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/bulkWrite.yml rename to specifications/command-logging-and-monitoring/tests/legacy/bulkWrite.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/command.json b/specifications/command-logging-and-monitoring/tests/legacy/command.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/command.json rename to specifications/command-logging-and-monitoring/tests/legacy/command.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/command.yml b/specifications/command-logging-and-monitoring/tests/legacy/command.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/command.yml rename to specifications/command-logging-and-monitoring/tests/legacy/command.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/deleteMany.json b/specifications/command-logging-and-monitoring/tests/legacy/deleteMany.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/deleteMany.json rename to specifications/command-logging-and-monitoring/tests/legacy/deleteMany.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/deleteMany.yml b/specifications/command-logging-and-monitoring/tests/legacy/deleteMany.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/deleteMany.yml rename to specifications/command-logging-and-monitoring/tests/legacy/deleteMany.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/deleteOne.json b/specifications/command-logging-and-monitoring/tests/legacy/deleteOne.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/deleteOne.json rename to specifications/command-logging-and-monitoring/tests/legacy/deleteOne.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/deleteOne.yml b/specifications/command-logging-and-monitoring/tests/legacy/deleteOne.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/deleteOne.yml rename to specifications/command-logging-and-monitoring/tests/legacy/deleteOne.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/find.json b/specifications/command-logging-and-monitoring/tests/legacy/find.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/find.json rename to specifications/command-logging-and-monitoring/tests/legacy/find.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/find.yml b/specifications/command-logging-and-monitoring/tests/legacy/find.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/find.yml rename to specifications/command-logging-and-monitoring/tests/legacy/find.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/insertMany.json b/specifications/command-logging-and-monitoring/tests/legacy/insertMany.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/insertMany.json rename to specifications/command-logging-and-monitoring/tests/legacy/insertMany.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/insertMany.yml b/specifications/command-logging-and-monitoring/tests/legacy/insertMany.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/insertMany.yml rename to specifications/command-logging-and-monitoring/tests/legacy/insertMany.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/insertOne.json b/specifications/command-logging-and-monitoring/tests/legacy/insertOne.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/insertOne.json rename to specifications/command-logging-and-monitoring/tests/legacy/insertOne.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/insertOne.yml b/specifications/command-logging-and-monitoring/tests/legacy/insertOne.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/insertOne.yml rename to specifications/command-logging-and-monitoring/tests/legacy/insertOne.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/unacknowledgedBulkWrite.json b/specifications/command-logging-and-monitoring/tests/legacy/unacknowledgedBulkWrite.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/unacknowledgedBulkWrite.json rename to specifications/command-logging-and-monitoring/tests/legacy/unacknowledgedBulkWrite.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/unacknowledgedBulkWrite.yml b/specifications/command-logging-and-monitoring/tests/legacy/unacknowledgedBulkWrite.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/unacknowledgedBulkWrite.yml rename to specifications/command-logging-and-monitoring/tests/legacy/unacknowledgedBulkWrite.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/updateMany.json b/specifications/command-logging-and-monitoring/tests/legacy/updateMany.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/updateMany.json rename to specifications/command-logging-and-monitoring/tests/legacy/updateMany.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/updateMany.yml b/specifications/command-logging-and-monitoring/tests/legacy/updateMany.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/updateMany.yml rename to specifications/command-logging-and-monitoring/tests/legacy/updateMany.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/updateOne.json b/specifications/command-logging-and-monitoring/tests/legacy/updateOne.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/updateOne.json rename to specifications/command-logging-and-monitoring/tests/legacy/updateOne.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/updateOne.yml b/specifications/command-logging-and-monitoring/tests/legacy/updateOne.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/legacy/updateOne.yml rename to specifications/command-logging-and-monitoring/tests/legacy/updateOne.yml diff --git a/specifications/command-logging-and-monitoring/tests/logging/command.json b/specifications/command-logging-and-monitoring/tests/logging/command.json new file mode 100644 index 00000000000..3d5c2570be2 --- /dev/null +++ b/specifications/command-logging-and-monitoring/tests/logging/command.json @@ -0,0 +1,213 @@ +{ + "description": "command-logging", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client", + "observeLogMessages": { + "command": "debug" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "logging-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "logging-tests-collection" + } + } + ], + "initialData": [ + { + "collectionName": "logging-tests-collection", + "databaseName": "logging-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "A successful command", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "command": { + "ping": 1 + }, + "commandName": "ping" + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-tests", + "commandName": "ping", + "command": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "ping": 1, + "$db": "logging-tests" + } + } + }, + "requestId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "commandName": "ping", + "reply": { + "$$type": "string" + }, + "requestId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "durationMS": { + "$$type": [ + "double", + "int", + "long" + ] + } + } + } + ] + } + ] + }, + { + "description": "A failed command", + "operations": [ + { + "name": "find", + "object": "collection", + "arguments": { + "filter": { + "$or": true + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-tests", + "commandName": "find", + "command": { + "$$type": "string" + }, + "requestId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command failed", + "commandName": "find", + "failure": { + "$$exists": true + }, + "requestId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "durationMS": { + "$$type": [ + "double", + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] +} diff --git a/specifications/command-logging-and-monitoring/tests/logging/command.yml b/specifications/command-logging-and-monitoring/tests/logging/command.yml new file mode 100644 index 00000000000..b21a4c60905 --- /dev/null +++ b/specifications/command-logging-and-monitoring/tests/logging/command.yml @@ -0,0 +1,94 @@ +description: "command-logging" + +schemaVersion: "1.13" + +createEntities: + - client: + id: &client client + observeLogMessages: + command: debug + - database: + id: &database database + client: *client + databaseName: &databaseName logging-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName logging-tests-collection + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + +tests: + - description: "A successful command" + operations: + - name: runCommand + object: *database + arguments: + command: { ping: 1 } + commandName: &commandName ping + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: *commandName + command: + $$matchAsDocument: + $$matchAsRoot: + ping: 1 + $db: *databaseName + requestId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - level: debug + component: command + data: + message: "Command succeeded" + commandName: *commandName + reply: { $$type: string } + requestId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + durationMS: { $$type: [double, int, long] } + + - description: "A failed command" + operations: + - name: &commandName find + object: *collection + arguments: + filter: { $or: true } + expectError: + isClientError: false + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: *commandName + command: { $$type: string } + requestId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - level: debug + component: command + data: + message: "Command failed" + commandName: *commandName + failure: { $$exists: true } + requestId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + durationMS: { $$type: [double, int, long] } + diff --git a/specifications/command-logging-and-monitoring/tests/logging/driver-connection-id.json b/specifications/command-logging-and-monitoring/tests/logging/driver-connection-id.json new file mode 100644 index 00000000000..40db98d6fae --- /dev/null +++ b/specifications/command-logging-and-monitoring/tests/logging/driver-connection-id.json @@ -0,0 +1,146 @@ +{ + "description": "driver-connection-id", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client", + "observeLogMessages": { + "command": "debug" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "logging-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "logging-tests-collection" + } + } + ], + "initialData": [ + { + "collectionName": "logging-tests-collection", + "databaseName": "logging-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "A successful command", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "command": { + "ping": 1 + }, + "commandName": "ping" + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-tests", + "commandName": "ping", + "driverConnectionId": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "commandName": "ping", + "driverConnectionId": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + }, + { + "description": "A failed command", + "operations": [ + { + "name": "find", + "object": "collection", + "arguments": { + "filter": { + "$or": true + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-tests", + "commandName": "find", + "driverConnectionId": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command failed", + "commandName": "find", + "driverConnectionId": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] +} diff --git a/specifications/command-logging-and-monitoring/tests/logging/driver-connection-id.yml b/specifications/command-logging-and-monitoring/tests/logging/driver-connection-id.yml new file mode 100644 index 00000000000..f299edabaf2 --- /dev/null +++ b/specifications/command-logging-and-monitoring/tests/logging/driver-connection-id.yml @@ -0,0 +1,76 @@ +# This is a separate test so that drivers that do not implement CMAP can easily skip it. +description: "driver-connection-id" + +schemaVersion: "1.13" + +createEntities: + - client: + id: &client client + observeLogMessages: + command: debug + - database: + id: &database database + client: *client + databaseName: &databaseName logging-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName logging-tests-collection + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + +tests: + - description: "A successful command" + operations: + - name: runCommand + object: *database + arguments: + command: { ping: 1 } + commandName: &commandName ping + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: *commandName + driverConnectionId: { $$type: [int, long] } + + - level: debug + component: command + data: + message: "Command succeeded" + commandName: *commandName + driverConnectionId: { $$type: [int, long] } + + - description: "A failed command" + operations: + - name: &commandName find + object: *collection + arguments: + filter: { $or: true } + expectError: + isClientError: false + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: *commandName + driverConnectionId: { $$type: [int, long] } + + - level: debug + component: command + data: + message: "Command failed" + commandName: *commandName + driverConnectionId: { $$type: [int, long] } diff --git a/specifications/command-logging-and-monitoring/tests/logging/no-handshake-messages.json b/specifications/command-logging-and-monitoring/tests/logging/no-handshake-messages.json new file mode 100644 index 00000000000..a61e208798c --- /dev/null +++ b/specifications/command-logging-and-monitoring/tests/logging/no-handshake-messages.json @@ -0,0 +1,94 @@ +{ + "description": "no-handshake-command-logs", + "schemaVersion": "1.13", + "tests": [ + { + "description": "Handshake commands should not generate log messages", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeLogMessages": { + "command": "debug" + }, + "observeEvents": [ + "connectionCreatedEvent", + "connectionReadyEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "logging-tests" + } + } + ] + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "command": { + "ping": 1 + }, + "commandName": "ping" + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "connectionCreatedEvent": {} + }, + "count": 1 + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "connectionReadyEvent": {} + }, + "count": 1 + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-tests", + "commandName": "ping" + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "commandName": "ping" + } + } + ] + } + ] + } + ] +} diff --git a/specifications/command-logging-and-monitoring/tests/logging/no-handshake-messages.yml b/specifications/command-logging-and-monitoring/tests/logging/no-handshake-messages.yml new file mode 100644 index 00000000000..bb7dd18e09a --- /dev/null +++ b/specifications/command-logging-and-monitoring/tests/logging/no-handshake-messages.yml @@ -0,0 +1,58 @@ +description: "no-handshake-command-logs" + +schemaVersion: "1.13" + +tests: + - description: "Handshake commands should not generate log messages" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + observeLogMessages: + command: debug + observeEvents: + - connectionCreatedEvent + - connectionReadyEvent + - database: + id: &database database + client: *client + databaseName: &databaseName logging-tests + - name: runCommand + object: *database + arguments: + command: { ping: 1 } + commandName: &commandName ping + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + connectionCreatedEvent: {} + count: 1 + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + connectionReadyEvent: {} + count: 1 + expectLogMessages: + # since the ping happens after the handshake, seeing events for only the ping + # implies the driver did not emit any log messages for the handshake. + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: *commandName + + - level: debug + component: command + data: + message: "Command succeeded" + commandName: *commandName diff --git a/specifications/command-logging-and-monitoring/tests/logging/no-heartbeat-messages.json b/specifications/command-logging-and-monitoring/tests/logging/no-heartbeat-messages.json new file mode 100644 index 00000000000..525be9171df --- /dev/null +++ b/specifications/command-logging-and-monitoring/tests/logging/no-heartbeat-messages.json @@ -0,0 +1,91 @@ +{ + "description": "no-heartbeat-command-logs", + "schemaVersion": "1.13", + "runOnRequirements": [ + { + "topologies": [ + "single", + "replicaset", + "sharded" + ] + } + ], + "tests": [ + { + "description": "Heartbeat commands should not generate log messages", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeLogMessages": { + "command": "debug" + }, + "observeEvents": [ + "serverDescriptionChangedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "logging-tests" + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": {} + }, + "count": 1 + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "command": { + "ping": 1 + }, + "commandName": "ping" + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-tests", + "commandName": "ping" + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "commandName": "ping" + } + } + ] + } + ] + } + ] +} diff --git a/specifications/command-logging-and-monitoring/tests/logging/no-heartbeat-messages.yml b/specifications/command-logging-and-monitoring/tests/logging/no-heartbeat-messages.yml new file mode 100644 index 00000000000..7d35fbe003a --- /dev/null +++ b/specifications/command-logging-and-monitoring/tests/logging/no-heartbeat-messages.yml @@ -0,0 +1,58 @@ +description: "no-heartbeat-command-logs" + +schemaVersion: "1.13" + +# no heartbeats in load balanced mode. +runOnRequirements: + - topologies: + - single + - replicaset + - sharded + +tests: + - description: "Heartbeat commands should not generate log messages" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + observeLogMessages: + command: debug + observeEvents: + - serverDescriptionChangedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName logging-tests + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + # a server description change implies that a heartbeat has happened. + serverDescriptionChangedEvent: {} + count: 1 + - name: runCommand + object: *database + arguments: + command: { ping: 1 } + commandName: &commandName ping + expectLogMessages: + # since the ping happens after the heartbeat, seeing events for only the ping + # implies the driver did not emit a log message for the heartbeat. + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: *commandName + + - level: debug + component: command + data: + message: "Command succeeded" + commandName: *commandName diff --git a/specifications/command-logging-and-monitoring/tests/logging/operation-id.json b/specifications/command-logging-and-monitoring/tests/logging/operation-id.json new file mode 100644 index 00000000000..b1a3cec3d91 --- /dev/null +++ b/specifications/command-logging-and-monitoring/tests/logging/operation-id.json @@ -0,0 +1,198 @@ +{ + "description": "operation-id", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client", + "observeLogMessages": { + "command": "debug" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "logging-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "logging-tests-collection" + } + } + ], + "initialData": [ + { + "collectionName": "logging-tests-collection", + "databaseName": "logging-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "Successful bulk write command log messages include operationIds", + "operations": [ + { + "name": "bulkWrite", + "object": "collection", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "x": 1 + } + } + }, + { + "deleteOne": { + "filter": { + "x": 1 + } + } + } + ] + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-tests", + "commandName": "insert", + "operationId": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "commandName": "insert", + "operationId": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-tests", + "commandName": "delete", + "operationId": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "commandName": "delete", + "operationId": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + }, + { + "description": "Failed bulk write command log message includes operationId", + "operations": [ + { + "name": "bulkWrite", + "object": "collection", + "arguments": { + "requests": [ + { + "updateOne": { + "filter": { + "x": 1 + }, + "update": [ + { + "$invalidOperator": true + } + ] + } + } + ] + }, + "expectError": { + "isClientError": false + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-tests", + "commandName": "update", + "operationId": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command failed", + "commandName": "update", + "operationId": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] +} diff --git a/specifications/command-logging-and-monitoring/tests/logging/operation-id.yml b/specifications/command-logging-and-monitoring/tests/logging/operation-id.yml new file mode 100644 index 00000000000..f763e03b14e --- /dev/null +++ b/specifications/command-logging-and-monitoring/tests/logging/operation-id.yml @@ -0,0 +1,99 @@ +# This test only applies to drivers that generate operationIds to enable users to link +# together bulk writes. +description: "operation-id" + +schemaVersion: "1.13" + +createEntities: + - client: + id: &client client + observeLogMessages: + command: debug + - database: + id: &database database + client: *client + databaseName: &databaseName logging-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName logging-tests-collection + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + +tests: + - description: "Successful bulk write command log messages include operationIds" + operations: + - name: bulkWrite + object: *collection + arguments: + requests: + - insertOne: + document: { x: 1 } + - deleteOne: + filter: { x: 1 } + + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: insert + operationId: { $$type: [int, long] } + + - level: debug + component: command + data: + message: "Command succeeded" + commandName: insert + operationId: { $$type: [int, long] } + + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: delete + operationId: { $$type: [int, long] } + + - level: debug + component: command + data: + message: "Command succeeded" + commandName: delete + operationId: { $$type: [int, long] } + + - description: "Failed bulk write command log message includes operationId" + operations: + - name: bulkWrite + object: *collection + arguments: + requests: + - updateOne: + filter: { x: 1 } + update: [{ $invalidOperator: true }] + expectError: + isClientError: false + + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: update + operationId: { $$type: [int, long] } + - level: debug + component: command + data: + message: "Command failed" + commandName: update + operationId: { $$type: [int, long] } diff --git a/specifications/command-logging-and-monitoring/tests/logging/pre-42-server-connection-id.json b/specifications/command-logging-and-monitoring/tests/logging/pre-42-server-connection-id.json new file mode 100644 index 00000000000..d5ebd865906 --- /dev/null +++ b/specifications/command-logging-and-monitoring/tests/logging/pre-42-server-connection-id.json @@ -0,0 +1,119 @@ +{ + "description": "pre-42-server-connection-id", + "schemaVersion": "1.13", + "runOnRequirements": [ + { + "maxServerVersion": "4.0.99" + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "observeLogMessages": { + "command": "debug" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "logging-server-connection-id-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "logging-tests-collection" + } + } + ], + "initialData": [ + { + "databaseName": "logging-server-connection-id-tests", + "collectionName": "logging-tests-collection", + "documents": [] + } + ], + "tests": [ + { + "description": "command log messages do not include server connection id", + "operations": [ + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": { + "$or": true + } + }, + "expectError": { + "isError": true + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "commandName": "insert", + "serverConnectionId": { + "$$exists": false + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "commandName": "insert", + "serverConnectionId": { + "$$exists": false + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "commandName": "find", + "serverConnectionId": { + "$$exists": false + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command failed", + "commandName": "find", + "serverConnectionId": { + "$$exists": false + } + } + } + ] + } + ] + } + ] +} diff --git a/specifications/command-logging-and-monitoring/tests/logging/pre-42-server-connection-id.yml b/specifications/command-logging-and-monitoring/tests/logging/pre-42-server-connection-id.yml new file mode 100644 index 00000000000..7dc80eea070 --- /dev/null +++ b/specifications/command-logging-and-monitoring/tests/logging/pre-42-server-connection-id.yml @@ -0,0 +1,66 @@ +description: "pre-42-server-connection-id" + +schemaVersion: "1.13" + +runOnRequirements: + - maxServerVersion: "4.0.99" + +createEntities: + - client: + id: &client client + observeLogMessages: + command: debug + - database: + id: &database database + client: *client + databaseName: &databaseName logging-server-connection-id-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName logging-tests-collection + +initialData: + - databaseName: *databaseName + collectionName: *collectionName + documents: [] + +tests: + - description: "command log messages do not include server connection id" + operations: + - name: insertOne + object: *collection + arguments: + document: { x: 1 } + - name: find + object: *collection + arguments: + filter: { $or: true } + expectError: + isError: true + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + commandName: insert + serverConnectionId: { $$exists: false } + - level: debug + component: command + data: + message: "Command succeeded" + commandName: insert + serverConnectionId: { $$exists: false } + - level: debug + component: command + data: + message: "Command started" + commandName: find + serverConnectionId: { $$exists: false } + - level: debug + component: command + data: + message: "Command failed" + commandName: find + serverConnectionId: { $$exists: false } diff --git a/specifications/command-logging-and-monitoring/tests/logging/redacted-commands.json b/specifications/command-logging-and-monitoring/tests/logging/redacted-commands.json new file mode 100644 index 00000000000..43b9ff74f29 --- /dev/null +++ b/specifications/command-logging-and-monitoring/tests/logging/redacted-commands.json @@ -0,0 +1,1438 @@ +{ + "description": "redacted-commands", + "schemaVersion": "1.13", + "runOnRequirements": [ + { + "minServerVersion": "5.0", + "auth": false + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeLogMessages": { + "command": "debug" + } + } + }, + { + "client": { + "id": "failPointClient", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "logging-redaction-tests" + } + } + ], + "tests": [ + { + "description": "authenticate command and resulting server-generated error are redacted", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "authenticate", + "command": { + "authenticate": 1, + "mechanism": "MONGODB-X509", + "user": "CN=myName,OU=myOrgUnit,O=myOrg,L=myLocality,ST=myState,C=myCountry", + "db": "$external" + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "authenticate", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "failureIsRedacted": true, + "data": { + "message": "Command failed", + "commandName": "authenticate", + "failure": { + "$$exists": true + } + } + } + ] + } + ] + }, + { + "description": "network error in response to authenticate is not redacted", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "authenticate" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "authenticate", + "command": { + "authenticate": 1, + "mechanism": "MONGODB-X509", + "user": "CN=myName,OU=myOrgUnit,O=myOrg,L=myLocality,ST=myState,C=myCountry" + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "authenticate", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "failureIsRedacted": false, + "data": { + "message": "Command failed", + "commandName": "authenticate", + "failure": { + "$$exists": true + } + } + } + ] + } + ] + }, + { + "description": "saslStart command and resulting server-generated error are redacted", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "saslStart", + "command": { + "saslStart": 1, + "payload": "definitely-invalid-payload", + "db": "admin" + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "saslStart", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "failureIsRedacted": true, + "data": { + "message": "Command failed", + "commandName": "saslStart", + "failure": { + "$$exists": true + } + } + } + ] + } + ] + }, + { + "description": "network error in response to saslStart is not redacted", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "saslStart" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "saslStart", + "command": { + "saslStart": 1, + "payload": "ZmFrZXNhc2xwYXlsb2Fk", + "mechanism": "MONGODB-X509" + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "saslStart", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "failureIsRedacted": false, + "data": { + "message": "Command failed", + "commandName": "saslStart", + "failure": { + "$$exists": true + } + } + } + ] + } + ] + }, + { + "description": "saslContinue command and resulting server-generated error are redacted", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "saslContinue", + "command": { + "saslContinue": 1, + "conversationId": 0, + "payload": "definitely-invalid-payload" + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "saslContinue", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "failureIsRedacted": true, + "data": { + "message": "Command failed", + "commandName": "saslContinue", + "failure": { + "$$exists": true + } + } + } + ] + } + ] + }, + { + "description": "network error in response to saslContinue is not redacted", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "saslContinue", + "command": { + "saslContinue": 1, + "conversationId": 0, + "payload": "ZmFrZXNhc2xwYXlsb2Fk" + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "saslContinue", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "failureIsRedacted": false, + "data": { + "message": "Command failed", + "commandName": "saslContinue", + "failure": { + "$$exists": true + } + } + } + ] + } + ] + }, + { + "description": "getnonce command and server reply are redacted", + "runOnRequirements": [ + { + "maxServerVersion": "6.1.99" + } + ], + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "getnonce", + "command": { + "getnonce": 1 + } + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "getnonce", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "commandName": "getnonce", + "reply": { + "$$matchAsDocument": {} + } + } + } + ] + } + ] + }, + { + "description": "network error in response to getnonce is not redacted", + "runOnRequirements": [ + { + "maxServerVersion": "6.1.99" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "getnonce" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "getnonce", + "command": { + "getnonce": 1 + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "getnonce", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "failureIsRedacted": false, + "data": { + "message": "Command failed", + "commandName": "getnonce", + "failure": { + "$$exists": true + } + } + } + ] + } + ] + }, + { + "description": "createUser command and resulting server-generated error are redacted", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "createUser", + "command": { + "createUser": "private", + "pwd": {}, + "roles": [] + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "createUser", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "failureIsRedacted": true, + "data": { + "message": "Command failed", + "commandName": "createUser", + "failure": { + "$$exists": true + } + } + } + ] + } + ] + }, + { + "description": "network error in response to createUser is not redacted", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "createUser" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "createUser", + "command": { + "createUser": "private", + "pwd": "pwd", + "roles": [] + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "createUser", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "failureIsRedacted": false, + "data": { + "message": "Command failed", + "commandName": "createUser", + "failure": { + "$$exists": true + } + } + } + ] + } + ] + }, + { + "description": "updateUser command and resulting server-generated error are redacted", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "updateUser", + "command": { + "updateUser": "private", + "pwd": {}, + "roles": [] + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "updateUser", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "failureIsRedacted": true, + "data": { + "message": "Command failed", + "commandName": "updateUser", + "failure": { + "$$exists": true + } + } + } + ] + } + ] + }, + { + "description": "network error in response to updateUser is not redacted", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "updateUser" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "updateUser", + "command": { + "updateUser": "private", + "pwd": "pwd", + "roles": [] + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "updateUser", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "failureIsRedacted": false, + "data": { + "message": "Command failed", + "commandName": "updateUser", + "failure": { + "$$exists": true + } + } + } + ] + } + ] + }, + { + "description": "copydbgetnonce command and resulting server-generated error are redacted", + "runOnRequirements": [ + { + "maxServerVersion": "3.6.99" + } + ], + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "copydbgetnonce", + "command": { + "copydbgetnonce": "private" + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "copydbgetnonce", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "failureIsRedacted": true, + "data": { + "message": "Command failed", + "commandName": "copydbgetnonce", + "failure": { + "$$exists": true + } + } + } + ] + } + ] + }, + { + "description": "network error in response to copydbgetnonce is not redacted", + "runOnRequirements": [ + { + "maxServerVersion": "3.6.99" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "copydbgetnonce" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "copydbgetnonce", + "command": { + "copydbgetnonce": "private" + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "copydbgetnonce", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "failureIsRedacted": false, + "data": { + "message": "Command failed", + "commandName": "copydbgetnonce", + "failure": { + "$$exists": true + } + } + } + ] + } + ] + }, + { + "description": "copydbsaslstart command and resulting server-generated error are redacted", + "runOnRequirements": [ + { + "maxServerVersion": "4.0.99" + } + ], + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "copydbsaslstart", + "command": { + "copydbsaslstart": "private" + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "copydbsaslstart", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "failureIsRedacted": true, + "data": { + "message": "Command failed", + "commandName": "copydbsaslstart", + "failure": { + "$$exists": true + } + } + } + ] + } + ] + }, + { + "description": "network error in response to copydbsaslstart is not redacted", + "runOnRequirements": [ + { + "maxServerVersion": "4.0.99" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "copydbsaslstart" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "copydbsaslstart", + "command": { + "copydbsaslstart": "private" + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "copydbgetnonce", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "failureIsRedacted": false, + "data": { + "message": "Command failed", + "commandName": "copydbgetnonce", + "failure": { + "$$exists": true + } + } + } + ] + } + ] + }, + { + "description": "copydb command and resulting server-generated error are redacted", + "runOnRequirements": [ + { + "maxServerVersion": "4.0.99" + } + ], + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "copydb", + "command": { + "copydb": "private" + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "copydb", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "failureIsRedacted": true, + "data": { + "message": "Command failed", + "commandName": "copydb", + "failure": { + "$$exists": true + } + } + } + ] + } + ] + }, + { + "description": "network error in response to copydb is not redacted", + "runOnRequirements": [ + { + "maxServerVersion": "4.0.99" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "copydb" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "copydb", + "command": { + "copydb": "private" + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "copydb", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "failureIsRedacted": false, + "data": { + "message": "Command failed", + "commandName": "copydb", + "failure": { + "$$exists": true + } + } + } + ] + } + ] + }, + { + "description": "hello with speculative authenticate command and server reply are redacted", + "runOnRequirements": [ + { + "minServerVersion": "4.9" + } + ], + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "hello", + "command": { + "hello": 1, + "speculativeAuthenticate": { + "saslStart": 1 + } + } + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "hello", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "commandName": "hello", + "reply": { + "$$matchAsDocument": {} + } + } + } + ] + } + ] + }, + { + "description": "legacy hello with speculative authenticate command and server reply are redacted", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ismaster", + "command": { + "ismaster": 1, + "speculativeAuthenticate": { + "saslStart": 1 + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "isMaster", + "command": { + "isMaster": 1, + "speculativeAuthenticate": { + "saslStart": 1 + } + } + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "ismaster", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "commandName": "ismaster", + "reply": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "isMaster", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "commandName": "isMaster", + "reply": { + "$$matchAsDocument": {} + } + } + } + ] + } + ] + }, + { + "description": "hello without speculative authenticate command and server reply are not redacted", + "runOnRequirements": [ + { + "minServerVersion": "4.9" + } + ], + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "hello", + "command": { + "hello": 1 + } + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "hello", + "command": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "hello": 1 + } + } + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "commandName": "hello", + "reply": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "ok": 1, + "isWritablePrimary": true + } + } + } + } + } + ] + } + ] + }, + { + "description": "legacy hello without speculative authenticate command and server reply are not redacted", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ismaster", + "command": { + "ismaster": 1 + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "isMaster", + "command": { + "isMaster": 1 + } + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "ismaster", + "command": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "ismaster": 1 + } + } + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "commandName": "ismaster", + "reply": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "ok": 1, + "ismaster": true + } + } + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "isMaster", + "command": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "isMaster": 1 + } + } + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "commandName": "isMaster", + "reply": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "ok": 1, + "ismaster": true + } + } + } + } + } + ] + } + ] + } + ] +} diff --git a/specifications/command-logging-and-monitoring/tests/logging/redacted-commands.yml b/specifications/command-logging-and-monitoring/tests/logging/redacted-commands.yml new file mode 100644 index 00000000000..05d61465e5a --- /dev/null +++ b/specifications/command-logging-and-monitoring/tests/logging/redacted-commands.yml @@ -0,0 +1,850 @@ +description: "redacted-commands" + +schemaVersion: "1.13" + +runOnRequirements: + - minServerVersion: "5.0" + auth: false + +createEntities: + - client: + id: &client client + useMultipleMongoses: false + observeLogMessages: + command: debug + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: &databaseName logging-redaction-tests + +tests: + - description: "authenticate command and resulting server-generated error are redacted" + operations: + - name: runCommand + object: *database + arguments: + commandName: authenticate + command: + authenticate: 1 + mechanism: "MONGODB-X509" + user: "CN=myName,OU=myOrgUnit,O=myOrg,L=myLocality,ST=myState,C=myCountry" + db: "$external" + # An authentication error is expected, but we want to check that the + # CommandStartedEvent is redacted + expectError: + isClientError: false + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: authenticate + command: + $$matchAsDocument: {} + - level: debug + component: command + failureIsRedacted: true + data: + message: "Command failed" + commandName: authenticate + failure: { $$exists: true } + + - description: "network error in response to authenticate is not redacted" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["authenticate"] + closeConnection: true + - name: runCommand + object: *database + arguments: + commandName: authenticate + command: + authenticate: 1 + mechanism: "MONGODB-X509" + user: "CN=myName,OU=myOrgUnit,O=myOrg,L=myLocality,ST=myState,C=myCountry" + expectError: + isClientError: true + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: authenticate + command: + $$matchAsDocument: {} + - level: debug + component: command + failureIsRedacted: false + data: + message: "Command failed" + commandName: authenticate + failure: { $$exists: true } + + - description: "saslStart command and resulting server-generated error are redacted" + operations: + - name: runCommand + object: *database + arguments: + commandName: saslStart + command: + saslStart: 1 + payload: "definitely-invalid-payload" + db: "admin" + expectError: + isClientError: false + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: saslStart + command: + $$matchAsDocument: {} + - level: debug + component: command + failureIsRedacted: true + data: + message: "Command failed" + commandName: saslStart + failure: { $$exists: true } + + - description: "network error in response to saslStart is not redacted" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["saslStart"] + closeConnection: true + - name: runCommand + object: *database + arguments: + commandName: saslStart + command: + saslStart: 1 + payload: ZmFrZXNhc2xwYXlsb2Fk + mechanism: MONGODB-X509 + expectError: + isClientError: true + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: saslStart + command: + $$matchAsDocument: {} + - level: debug + component: command + failureIsRedacted: false + data: + message: "Command failed" + commandName: saslStart + failure: { $$exists: true } + + - description: "saslContinue command and resulting server-generated error are redacted" + operations: + - name: runCommand + object: *database + arguments: + commandName: saslContinue + command: + saslContinue: 1 + conversationId: 0 + payload: "definitely-invalid-payload" + expectError: + isClientError: false + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: saslContinue + command: + $$matchAsDocument: {} + - level: debug + component: command + failureIsRedacted: true + data: + message: "Command failed" + commandName: saslContinue + failure: { $$exists: true } + + - description: "network error in response to saslContinue is not redacted" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["saslContinue"] + closeConnection: true + - name: runCommand + object: *database + arguments: + commandName: saslContinue + command: + saslContinue: 1 + conversationId: 0 + payload: ZmFrZXNhc2xwYXlsb2Fk + expectError: + isClientError: true + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: saslContinue + command: + $$matchAsDocument: {} + - level: debug + component: command + failureIsRedacted: false + data: + message: "Command failed" + commandName: saslContinue + failure: { $$exists: true } + + - description: "getnonce command and server reply are redacted" + runOnRequirements: + - maxServerVersion: 6.1.99 # getnonce removed as of 6.2 via SERVER-71007 + operations: + - name: runCommand + object: *database + arguments: + commandName: getnonce + command: + getnonce: 1 + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: getnonce + command: + $$matchAsDocument: {} + - level: debug + component: command + data: + message: "Command succeeded" + commandName: getnonce + reply: + $$matchAsDocument: {} + + - description: "network error in response to getnonce is not redacted" + runOnRequirements: + - maxServerVersion: 6.1.99 # getnonce removed as of 6.2 via SERVER-71007 + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["getnonce"] + closeConnection: true + - name: runCommand + object: *database + arguments: + commandName: getnonce + command: + getnonce: 1 + expectError: + isClientError: true + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: getnonce + command: + $$matchAsDocument: {} + - level: debug + component: command + failureIsRedacted: false + data: + message: "Command failed" + commandName: getnonce + failure: { $$exists: true } + + - description: "createUser command and resulting server-generated error are redacted" + operations: + - name: runCommand + object: *database + arguments: + commandName: createUser + command: + createUser: "private" + # Passing an object is prohibited and we want to trigger a command + # failure + pwd: {} + roles: [] + expectError: + isClientError: false + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: createUser + command: + $$matchAsDocument: {} + - level: debug + component: command + failureIsRedacted: true + data: + message: "Command failed" + commandName: createUser + failure: { $$exists: true } + + - description: "network error in response to createUser is not redacted" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["createUser"] + closeConnection: true + - name: runCommand + object: *database + arguments: + commandName: createUser + command: + createUser: "private" + pwd: "pwd" + roles: [] + expectError: + isClientError: true + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: createUser + command: + $$matchAsDocument: {} + - level: debug + component: command + failureIsRedacted: false + data: + message: "Command failed" + commandName: createUser + failure: { $$exists: true } + + - description: "updateUser command and resulting server-generated error are redacted" + operations: + - name: runCommand + object: *database + arguments: + commandName: updateUser + command: + updateUser: "private" + pwd: {} + roles: [] + expectError: + isClientError: false + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: updateUser + command: + $$matchAsDocument: {} + - level: debug + component: command + failureIsRedacted: true + data: + message: "Command failed" + commandName: updateUser + failure: { $$exists: true } + + - description: "network error in response to updateUser is not redacted" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["updateUser"] + closeConnection: true + - name: runCommand + object: *database + arguments: + commandName: updateUser + command: + updateUser: "private" + pwd: "pwd" + roles: [] + expectError: + isClientError: true + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: updateUser + command: + $$matchAsDocument: {} + - level: debug + component: command + failureIsRedacted: false + data: + message: "Command failed" + commandName: updateUser + failure: { $$exists: true } + + - description: "copydbgetnonce command and resulting server-generated error are redacted" + runOnRequirements: + - maxServerVersion: 3.6.99 # copydbgetnonce was removed as of 4.0 via SERVER-32276 + operations: + - name: runCommand + object: *database + arguments: + commandName: copydbgetnonce + command: + copydbgetnonce: "private" + expectError: + isClientError: false + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: copydbgetnonce + command: + $$matchAsDocument: {} + - level: debug + component: command + failureIsRedacted: true + data: + message: "Command failed" + commandName: copydbgetnonce + failure: { $$exists: true } + + - description: "network error in response to copydbgetnonce is not redacted" + runOnRequirements: + - maxServerVersion: 3.6.99 # copydbgetnonce was removed as of 4.0 via SERVER-32276 + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["copydbgetnonce"] + closeConnection: true + - name: runCommand + object: *database + arguments: + commandName: copydbgetnonce + command: + copydbgetnonce: "private" + expectError: + isClientError: true + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: copydbgetnonce + command: + $$matchAsDocument: {} + - level: debug + component: command + failureIsRedacted: false + data: + message: "Command failed" + commandName: copydbgetnonce + failure: { $$exists: true } + + - description: "copydbsaslstart command and resulting server-generated error are redacted" + runOnRequirements: + - maxServerVersion: 4.0.99 # copydbsaslstart was removed as of 4.2 via SERVER-36211 + operations: + - name: runCommand + object: *database + arguments: + commandName: copydbsaslstart + command: + copydbsaslstart: "private" + expectError: + isClientError: false + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: copydbsaslstart + command: + $$matchAsDocument: {} + - level: debug + component: command + failureIsRedacted: true + data: + message: "Command failed" + commandName: copydbsaslstart + failure: { $$exists: true } + + - description: "network error in response to copydbsaslstart is not redacted" + runOnRequirements: + - maxServerVersion: 4.0.99 # copydbsaslstart was removed as of 4.2 via SERVER-36211 + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["copydbsaslstart"] + closeConnection: true + - name: runCommand + object: *database + arguments: + commandName: copydbsaslstart + command: + copydbsaslstart: "private" + expectError: + isClientError: true + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: copydbgetnonce + command: + $$matchAsDocument: {} + - level: debug + component: command + failureIsRedacted: false + data: + message: "Command failed" + commandName: copydbgetnonce + failure: { $$exists: true } + + - description: "copydb command and resulting server-generated error are redacted" + runOnRequirements: + - maxServerVersion: 4.0.99 # copydb was removed as of 4.2 via SERVER-36257 + operations: + - name: runCommand + object: *database + arguments: + commandName: copydb + command: + copydb: "private" + expectError: + isClientError: false + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: copydb + command: + $$matchAsDocument: {} + - level: debug + component: command + failureIsRedacted: true + data: + message: "Command failed" + commandName: copydb + failure: { $$exists: true } + + - description: "network error in response to copydb is not redacted" + runOnRequirements: + - maxServerVersion: 4.0.99 # copydb was removed as of 4.2 via SERVER-36257 + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["copydb"] + closeConnection: true + - name: runCommand + object: *database + arguments: + commandName: copydb + command: + copydb: "private" + expectError: + isClientError: true + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: copydb + command: + $$matchAsDocument: {} + - level: debug + component: command + failureIsRedacted: false + data: + message: "Command failed" + commandName: copydb + failure: { $$exists: true } + + - description: "hello with speculative authenticate command and server reply are redacted" + runOnRequirements: + - minServerVersion: "4.9" + operations: + - name: runCommand + object: *database + arguments: + commandName: hello + command: + hello: 1 + speculativeAuthenticate: + saslStart: 1 + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: hello + command: + $$matchAsDocument: {} + - level: debug + component: command + data: + message: "Command succeeded" + commandName: hello + reply: + $$matchAsDocument: {} + + + - description: "legacy hello with speculative authenticate command and server reply are redacted" + operations: + - name: runCommand + object: *database + arguments: + commandName: ismaster + command: + ismaster: 1 + speculativeAuthenticate: + saslStart: 1 + - name: runCommand + object: *database + arguments: + commandName: isMaster + command: + isMaster: 1 + speculativeAuthenticate: + saslStart: 1 + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: ismaster + command: + $$matchAsDocument: {} + - level: debug + component: command + data: + message: "Command succeeded" + commandName: ismaster + reply: + $$matchAsDocument: {} + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: isMaster + command: + $$matchAsDocument: {} + - level: debug + component: command + data: + message: "Command succeeded" + commandName: isMaster + reply: + $$matchAsDocument: {} + + - description: "hello without speculative authenticate command and server reply are not redacted" + runOnRequirements: + - minServerVersion: "4.9" + operations: + - name: runCommand + object: *database + arguments: + commandName: hello + command: + hello: 1 + + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: hello + command: + $$matchAsDocument: + $$matchAsRoot: + hello: 1 + - level: debug + component: command + data: + message: "Command succeeded" + commandName: hello + reply: + $$matchAsDocument: + $$matchAsRoot: + ok: 1 + isWritablePrimary: true + + - description: "legacy hello without speculative authenticate command and server reply are not redacted" + operations: + - name: runCommand + object: *database + arguments: + commandName: ismaster + command: + ismaster: 1 + - name: runCommand + object: *database + arguments: + commandName: isMaster + command: + isMaster: 1 + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: ismaster + command: + $$matchAsDocument: + $$matchAsRoot: + ismaster: 1 + - level: debug + component: command + data: + message: "Command succeeded" + commandName: ismaster + reply: + $$matchAsDocument: + $$matchAsRoot: + ok: 1 + ismaster: true + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: isMaster + command: + $$matchAsDocument: + $$matchAsRoot: + isMaster: 1 + - level: debug + component: command + data: + message: "Command succeeded" + commandName: isMaster + reply: + $$matchAsDocument: + $$matchAsRoot: + ok: 1 + ismaster: true diff --git a/specifications/command-logging-and-monitoring/tests/logging/server-connection-id.json b/specifications/command-logging-and-monitoring/tests/logging/server-connection-id.json new file mode 100644 index 00000000000..abbbbc74421 --- /dev/null +++ b/specifications/command-logging-and-monitoring/tests/logging/server-connection-id.json @@ -0,0 +1,131 @@ +{ + "description": "server-connection-id", + "schemaVersion": "1.13", + "runOnRequirements": [ + { + "minServerVersion": "4.2" + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "observeLogMessages": { + "command": "debug" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "logging-server-connection-id-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "logging-tests-collection" + } + } + ], + "initialData": [ + { + "databaseName": "logging-server-connection-id-tests", + "collectionName": "logging-tests-collection", + "documents": [] + } + ], + "tests": [ + { + "description": "command log messages include server connection id", + "operations": [ + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": { + "$or": true + } + }, + "expectError": { + "isError": true + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "commandName": "insert", + "serverConnectionId": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "commandName": "insert", + "serverConnectionId": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "commandName": "find", + "serverConnectionId": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command failed", + "commandName": "find", + "serverConnectionId": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] +} diff --git a/specifications/command-logging-and-monitoring/tests/logging/server-connection-id.yml b/specifications/command-logging-and-monitoring/tests/logging/server-connection-id.yml new file mode 100644 index 00000000000..4f54d1207e3 --- /dev/null +++ b/specifications/command-logging-and-monitoring/tests/logging/server-connection-id.yml @@ -0,0 +1,66 @@ +description: "server-connection-id" + +schemaVersion: "1.13" + +runOnRequirements: + - minServerVersion: "4.2" + +createEntities: + - client: + id: &client client + observeLogMessages: + command: debug + - database: + id: &database database + client: *client + databaseName: &databaseName logging-server-connection-id-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName logging-tests-collection + +initialData: + - databaseName: *databaseName + collectionName: *collectionName + documents: [] + +tests: + - description: "command log messages include server connection id" + operations: + - name: insertOne + object: *collection + arguments: + document: { x: 1 } + - name: find + object: *collection + arguments: + filter: { $or: true } + expectError: + isError: true + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + commandName: insert + serverConnectionId: { $$type: [int, long] } + - level: debug + component: command + data: + message: "Command succeeded" + commandName: insert + serverConnectionId: { $$type: [int, long] } + - level: debug + component: command + data: + message: "Command started" + commandName: find + serverConnectionId: { $$type: [int, long] } + - level: debug + component: command + data: + message: "Command failed" + commandName: find + serverConnectionId: { $$type: [int, long] } diff --git a/specifications/command-logging-and-monitoring/tests/logging/service-id.json b/specifications/command-logging-and-monitoring/tests/logging/service-id.json new file mode 100644 index 00000000000..ea39d612315 --- /dev/null +++ b/specifications/command-logging-and-monitoring/tests/logging/service-id.json @@ -0,0 +1,207 @@ +{ + "description": "service-id", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client", + "observeLogMessages": { + "command": "debug" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "logging-server-connection-id-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "logging-tests-collection" + } + } + ], + "initialData": [ + { + "databaseName": "logging-server-connection-id-tests", + "collectionName": "logging-tests-collection", + "documents": [] + } + ], + "tests": [ + { + "description": "command log messages include serviceId when in LB mode", + "runOnRequirements": [ + { + "topologies": [ + "load-balanced" + ] + } + ], + "operations": [ + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": { + "$or": true + } + }, + "expectError": { + "isError": true + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "commandName": "insert", + "serviceId": { + "$$type": "string" + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "commandName": "insert", + "serviceId": { + "$$type": "string" + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "commandName": "find", + "serviceId": { + "$$type": "string" + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command failed", + "commandName": "find", + "serviceId": { + "$$type": "string" + } + } + } + ] + } + ] + }, + { + "description": "command log messages omit serviceId when not in LB mode", + "runOnRequirements": [ + { + "topologies": [ + "single", + "replicaset", + "sharded" + ] + } + ], + "operations": [ + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": { + "$or": true + } + }, + "expectError": { + "isError": true + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "commandName": "insert", + "serviceId": { + "$$exists": false + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "commandName": "insert", + "serviceId": { + "$$exists": false + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "commandName": "find", + "serviceId": { + "$$exists": false + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command failed", + "commandName": "find", + "serviceId": { + "$$exists": false + } + } + } + ] + } + ] + } + ] +} diff --git a/specifications/command-logging-and-monitoring/tests/logging/service-id.yml b/specifications/command-logging-and-monitoring/tests/logging/service-id.yml new file mode 100644 index 00000000000..0c0f444e239 --- /dev/null +++ b/specifications/command-logging-and-monitoring/tests/logging/service-id.yml @@ -0,0 +1,111 @@ +description: "service-id" + +schemaVersion: "1.13" + +createEntities: + - client: + id: &client client + observeLogMessages: + command: debug + - database: + id: &database database + client: *client + databaseName: &databaseName logging-server-connection-id-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName logging-tests-collection + +initialData: + - databaseName: *databaseName + collectionName: *collectionName + documents: [] + +tests: + - description: "command log messages include serviceId when in LB mode" + runOnRequirements: + - topologies: + - load-balanced + operations: + - name: insertOne + object: *collection + arguments: + document: { x: 1 } + - name: find + object: *collection + arguments: + filter: { $or: true } + expectError: + isError: true + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + commandName: insert + serviceId: { $$type: string } + - level: debug + component: command + data: + message: "Command succeeded" + commandName: insert + serviceId: { $$type: string } + - level: debug + component: command + data: + message: "Command started" + commandName: find + serviceId: { $$type: string } + - level: debug + component: command + data: + message: "Command failed" + commandName: find + serviceId: { $$type: string } + + - description: "command log messages omit serviceId when not in LB mode" + runOnRequirements: + - topologies: + - single + - replicaset + - sharded + operations: + - name: insertOne + object: *collection + arguments: + document: { x: 1 } + - name: find + object: *collection + arguments: + filter: { $or: true } + expectError: + isError: true + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + commandName: insert + serviceId: { $$exists: false } + - level: debug + component: command + data: + message: "Command succeeded" + commandName: insert + serviceId: { $$exists: false } + - level: debug + component: command + data: + message: "Command started" + commandName: find + serviceId: { $$exists: false } + - level: debug + component: command + data: + message: "Command failed" + commandName: find + serviceId: { $$exists: false } diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/unified/pre-42-server-connection-id.json b/specifications/command-logging-and-monitoring/tests/unified/pre-42-server-connection-id.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/unified/pre-42-server-connection-id.json rename to specifications/command-logging-and-monitoring/tests/unified/pre-42-server-connection-id.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/unified/pre-42-server-connection-id.yml b/specifications/command-logging-and-monitoring/tests/unified/pre-42-server-connection-id.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/unified/pre-42-server-connection-id.yml rename to specifications/command-logging-and-monitoring/tests/unified/pre-42-server-connection-id.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/unified/redacted-commands.json b/specifications/command-logging-and-monitoring/tests/unified/redacted-commands.json similarity index 99% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/unified/redacted-commands.json rename to specifications/command-logging-and-monitoring/tests/unified/redacted-commands.json index 0f85dc3e94a..645348591a3 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/unified/redacted-commands.json +++ b/specifications/command-logging-and-monitoring/tests/unified/redacted-commands.json @@ -162,6 +162,11 @@ }, { "description": "getnonce", + "runOnRequirements": [ + { + "maxServerVersion": "6.1.99" + } + ], "operations": [ { "name": "runCommand", diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/unified/redacted-commands.yml b/specifications/command-logging-and-monitoring/tests/unified/redacted-commands.yml similarity index 99% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/unified/redacted-commands.yml rename to specifications/command-logging-and-monitoring/tests/unified/redacted-commands.yml index 570fb5a547e..f730785d733 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/unified/redacted-commands.yml +++ b/specifications/command-logging-and-monitoring/tests/unified/redacted-commands.yml @@ -93,6 +93,8 @@ tests: payload: { $$exists: false } - description: "getnonce" + runOnRequirements: + - maxServerVersion: "6.1.99" operations: - name: runCommand object: *database diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/unified/server-connection-id.json b/specifications/command-logging-and-monitoring/tests/unified/server-connection-id.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/unified/server-connection-id.json rename to specifications/command-logging-and-monitoring/tests/unified/server-connection-id.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/unified/server-connection-id.yml b/specifications/command-logging-and-monitoring/tests/unified/server-connection-id.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/tests/unified/server-connection-id.yml rename to specifications/command-logging-and-monitoring/tests/unified/server-connection-id.yml diff --git a/specifications/connection-monitoring-and-pooling/connection-monitoring-and-pooling.rst b/specifications/connection-monitoring-and-pooling/connection-monitoring-and-pooling.rst new file mode 100644 index 00000000000..0fe3811ae7f --- /dev/null +++ b/specifications/connection-monitoring-and-pooling/connection-monitoring-and-pooling.rst @@ -0,0 +1,1557 @@ +================================= +Connection Monitoring and Pooling +================================= + +:Status: Accepted +:Minimum Server Version: N/A + +.. contents:: + +Abstract +======== + +Drivers currently support a variety of options that allow users to configure connection pooling behavior. Users are confused by drivers supporting different subsets of these options. Additionally, drivers implement their connection pools differently, making it difficult to design cross-driver pool functionality. By unifying and codifying pooling options and behavior across all drivers, we will increase user comprehension and code base maintainability. + +META +==== + +The keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in `RFC 2119 `_. + +Definitions +=========== + +Connection +~~~~~~~~~~~~~~ + +A Connection (when linked) refers to the ``Connection`` type defined in the +`Connection Pool Members`_ section of this specification. It does not refer to an actual TCP +connection to an Endpoint. A ``Connection`` will attempt to create and wrap such +a TCP connection over the course of its existence, but it is not equivalent to +one nor does it wrap an active one at all times. + +For the purposes of testing, a mocked ``Connection`` type could be used with the +pool that never actually creates a TCP connection or performs any I/O. + +Endpoint +~~~~~~~~ + +For convenience, an Endpoint refers to either a **mongod** or **mongos** instance. + +Thread +~~~~~~ + +For convenience, a Thread refers to: + +- A shared-address-space process (a.k.a. a thread) in multi-threaded drivers +- An Execution Frame / Continuation in asynchronous drivers +- A goroutine in Go + +Behavioral Description +====================== + +Which Drivers this applies to +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This specification is solely concerned with drivers that implement a connection pool. A driver SHOULD implement a connection pool, but is not required to. + +Connection Pool Options +~~~~~~~~~~~~~~~~~~~~~~~ + +All drivers that implement a connection pool MUST implement and conform to the same MongoClient options. There can be slight deviation in naming to make the options idiomatic to the driver language. + +Connection Pool Behaviors +~~~~~~~~~~~~~~~~~~~~~~~~~ + +All driver connection pools MUST provide an API that allows the driver to check out a connection, check in a connection back to the pool, and clear all connections in the pool. This API is for internal use only, and SHOULD NOT be documented as a public API. + +Connection Pool Monitoring +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +All drivers that implement a connection pool MUST provide an API that allows users to subscribe to events emitted from the pool. + +Detailed Design +=============== + +.. _connection-pool-options-1: + +Connection Pool Options +~~~~~~~~~~~~~~~~~~~~~~~ + +Drivers that implement a Connection Pool MUST support the following ConnectionPoolOptions: + +.. code:: typescript + + interface ConnectionPoolOptions { + /** + * The maximum number of Connections that may be associated + * with a pool at a given time. This includes in use and + * available connections. + * If specified, MUST be an integer >= 0. + * A value of 0 means there is no limit. + * Defaults to 100. + */ + maxPoolSize?: number; + + /** + * The minimum number of Connections that MUST exist at any moment + * in a single connection pool. + * If specified, MUST be an integer >= 0. If maxPoolSize is > 0 + * then minPoolSize must be <= maxPoolSize + * Defaults to 0. + */ + minPoolSize?: number; + + /** + * The maximum amount of time a Connection should remain idle + * in the connection pool before being marked idle. + * If specified, MUST be a number >= 0. + * A value of 0 means there is no limit. + * Defaults to 0. + */ + maxIdleTimeMS?: number; + + /** + * The maximum number of Connections a Pool may be establishing concurrently. + * Establishment of a Connection is a part of its life cycle + * starting after a ConnectionCreatedEvent and ending before a ConnectionReadyEvent. + * If specified, MUST be a number > 0. + * Defaults to 2. + */ + maxConnecting?: number; + } + +Additionally, Drivers that implement a Connection Pool MUST support the following ConnectionPoolOptions UNLESS that driver meets ALL of the following conditions: + +- The driver/language currently has an idiomatic timeout mechanism implemented +- The timeout mechanism conforms to `the aggressive requirement of timing out a thread in the WaitQueue <#w1dcrm950sbn>`__ + +.. code:: typescript + + interface ConnectionPoolOptions { + /** + * NOTE: This option has been deprecated in favor of timeoutMS. + * + * The maximum amount of time a thread can wait for a connection + * to become available. + * If specified, MUST be a number >= 0. + * A value of 0 means there is no limit. + * Defaults to 0. + */ + waitQueueTimeoutMS?: number; + } + +These options MUST be specified at the MongoClient level, and SHOULD be named in a manner idiomatic to the driver's language. All connection pools created by a MongoClient MUST use the same ConnectionPoolOptions. + +When parsing a mongodb connection string, a user MUST be able to specify these options using the default names specified above. + +Deprecated Options +------------------ + +The following ConnectionPoolOptions are considered deprecated. They MUST NOT be implemented if they do not already exist in a driver, and they SHOULD be deprecated and removed from drivers that implement them as early as possible: + +.. code:: typescript + + interface ConnectionPoolOptions { + /** + * The maximum number of threads that can simultaneously wait + * for a Connection to become available. + */ + waitQueueSize?: number; + + /** + * An alternative way of setting waitQueueSize, it specifies + * the maximum number of threads that can wait per connection. + * waitQueueSize === waitQueueMultiple \* maxPoolSize + */ + waitQueueMultiple?: number + } + +Connection Pool Members +~~~~~~~~~~~~~~~~~~~~~~~ + +Connection +---------- + +A driver-defined wrapper around a single TCP connection to an Endpoint. A `Connection`_ has the following properties: + +- **Single Endpoint:** A `Connection`_ MUST be associated with a single Endpoint. A `Connection`_ MUST NOT be associated with multiple Endpoints. +- **Single Lifetime:** A `Connection`_ MUST NOT be used after it is closed. +- **Single Owner:** A `Connection`_ MUST belong to exactly one Pool, and MUST NOT be shared across multiple pools +- **Single Track:** A `Connection`_ MUST limit itself to one request / response at a time. A `Connection`_ MUST NOT multiplex/pipeline requests to an Endpoint. +- **Monotonically Increasing ID:** A `Connection`_ MUST have an ID number associated with it. `Connection`_ IDs within a Pool MUST be assigned in order of creation, starting at 1 and increasing by 1 for each new Connection. +- **Valid Connection:** A connection MUST NOT be checked out of the pool until it has successfully and fully completed a MongoDB Handshake and Authentication as specified in the `Handshake `__, `OP_COMPRESSED `__, and `Authentication `__ specifications. +- **Perishable**: it is possible for a `Connection`_ to become **Perished**. A `Connection`_ is considered perished if any of the following are true: + + - **Stale:** The `Connection`_ 's generation does not match the generation of the parent pool + - **Idle:** The `Connection`_ is currently "available" (as defined below) and has been for longer than **maxIdleTimeMS**. + - **Errored:** The `Connection`_ has experienced an error that indicates it is no longer recommended for use. Examples include, but are not limited to: + + - Network Error + - Network Timeout + - Endpoint closing the connection + - Driver-Side Timeout + - Wire-Protocol Error + +.. code:: typescript + + interface Connection { + /** + * An id number associated with the Connection + */ + id: number; + + /** + * The address of the pool that owns this Connection + */ + address: string; + + /** + * An integer representing the “generation” of the pool + * when this Connection was created. + */ + generation: number; + + /** + * The current state of the Connection. + * + * Possible values are the following: + * - "pending": The Connection has been created but has not yet been established. Contributes to + * totalConnectionCount and pendingConnectionCount. + * + * - "available": The Connection has been established and is waiting in the pool to be checked + * out. Contributes to both totalConnectionCount and availableConnectionCount. + * + * - "in use": The Connection has been established, checked out from the pool, and has yet + * to be checked back in. Contributes to totalConnectionCount. + * + * - "closed": The Connection has had its socket closed and cannot be used for any future + * operations. Does not contribute to any connection counts. + * + * Note: this field is mainly used for the purposes of describing state + * in this specification. It is not required that drivers + * actually include this field in their implementations of Connection. + */ + state: "pending" | "available" | "in use" | "closed"; + } + +WaitQueue +--------- + +A concept that represents pending requests for `Connections <#connection>`_. When a thread requests a `Connection <#connection>`_ from a Pool, the thread enters the Pool's WaitQueue. A thread stays in the WaitQueue until it either receives a `Connection <#connection>`_ or times out. A WaitQueue has the following traits: + +- **Thread-Safe**: When multiple threads attempt to enter or exit a WaitQueue, they do so in a thread-safe manner. +- **Ordered/Fair**: When `Connections <#connection>`_ are made available, they are issued out to threads in the order that the threads entered the WaitQueue. +- **Timeout aggressively:** Members of a WaitQueue MUST timeout if they are enqueued for longer than the computed timeout and MUST leave the WaitQueue immediately in this case. + +The implementation details of a WaitQueue are left to the driver. +Example implementations include: + +- A fair Semaphore +- A Queue of callbacks + +Connection Pool +--------------- + +A driver-defined entity that encapsulates all non-monitoring +`Connections <#connection>`_ associated with a single Endpoint. The pool +has the following properties: + +- **Thread Safe:** All Pool behaviors MUST be thread safe. +- **Not Fork-Safe:** A Pool is explicitly not fork-safe. If a Pool detects that is it being used by a forked process, it MUST immediately clear itself and update its pid +- **Single Owner:** A Pool MUST be associated with exactly one Endpoint, and MUST NOT be shared between Endpoints. +- **Emit Events and Log Messages:** A Pool MUST emit pool events and log messages when dictated by this spec (see `Connection Pool Monitoring <#connection-pool-monitoring>`__). Users MUST be able to subscribe to emitted events and log messages in a manner idiomatic to their language and driver. +- **Closeable:** A Pool MUST be able to be manually closed. When a Pool is closed, the following behaviors change: + + - Checking in a `Connection <#connection>`_ to the Pool automatically closes the `Connection <#connection>`_ + - Attempting to check out a `Connection <#connection>`_ from the Pool results in an Error + +- **Clearable:** A Pool MUST be able to be cleared. Clearing the pool marks all pooled and checked out `Connections <#connection>`_ as stale and lazily closes them as they are checkedIn or encountered in checkOut. Additionally, all requests are evicted from the WaitQueue and return errors that are considered non-timeout network errors. + +- **Pausable:** A Pool MUST be able to be paused and resumed. A Pool is paused automatically when it is cleared, and it can be resumed by being marked as "ready". While the Pool is paused, it exhibits the following behaviors: + + - Attempting to check out a `Connection <#connection>`_ from the Pool results in a non-timeout network error + - Connections are not created in the background to satisfy minPoolSize + +- **Capped:** a pool is capped if **maxPoolSize** is set to a non-zero value. If a pool is capped, then its total number of `Connections <#connection>`_ (including available and in use) MUST NOT exceed **maxPoolSize** +- **Rate-limited:** A Pool MUST limit the number of `Connections <#connection>`_ being `established <#establishing-a-connection-internal-implementation>`_ concurrently via the **maxConnecting** `pool option <#connection-pool-options-1>`_. + + +.. code:: typescript + + interface ConnectionPool { + /** + * The Queue of threads waiting for a Connection to be available + */ + waitQueue: WaitQueue; + + /** + * A generation number representing the SDAM generation of the pool. + */ + generation: number; + + /** + * A map representing the various generation numbers for various services + * when in load balancer mode. + */ + serviceGenerations: Map; + + /** + * The state of the pool. + * + * Possible values are the following: + * - "paused": The initial state of the pool. Connections may not be checked out nor can they + * be established in the background to satisfy minPoolSize. Clearing a pool + * transitions it to this state. + * + * - "ready": The healthy state of the pool. It can service checkOut requests and create + * connections in the background. The pool can be set to this state via the + * ready() method. + * + * - "closed": The pool is destroyed. No more Connections may ever be checked out nor any + * created in the background. The pool can be set to this sate via the close() + * method. The pool cannot transition to any other state after being closed. + */ + state: "paused" | "ready" | "closed"; + + // Any of the following connection counts may be computed rather than + // actually stored on the pool. + + /** + * An integer expressing how many total Connections + * ("pending" + "available" + "in use") the pool currently has + */ + totalConnectionCount: number; + + /** + * An integer expressing how many Connections are currently + * available in the pool. + */ + availableConnectionCount: number; + + /** + * An integer expressing how many Connections are currently + * being established. + */ + pendingConnectionCount: number; + + /** + * Returns a Connection for use + */ + checkOut(): Connection; + + /** + * Check in a Connection back to the Connection pool + */ + checkIn(connection: Connection): void; + + /** + * Mark all current Connections as stale, clear the WaitQueue, and mark the pool as "paused". + * No connections may be checked out or created in this pool until ready() is called again. + * interruptInUseConnections specifies whether the pool will force interrupt "in use" connections as part of the clear. + * Default false. + */ + clear(interruptInUseConnections: Optional): void; + + /** + * Mark the pool as "ready", allowing checkOuts to resume and connections to be created in the background. + * A pool can only transition from "paused" to "ready". A "closed" pool + * cannot be marked as "ready" via this method. + */ + ready(): void; + + /** + * Marks the pool as "closed", preventing the pool from creating and returning new Connections + */ + close(): void; + } + +.. _connection-pool-behaviors-1: + +Connection Pool Behaviors +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Creating a Connection Pool +-------------------------- + +This specification does not define how a pool is to be created, leaving it +up to the driver. Creation of a connection pool is generally an implementation +detail of the driver, i.e., is not a part of the public API of the driver. +The SDAM specification defines `when +`_ +the driver should create connection pools. + +When a pool is created, its state MUST initially be set to "paused". Even if +minPoolSize is set, the pool MUST NOT begin being `populated +<#populating-the-pool-with-a-connection-internal-implementation>`_ with +`Connections <#connection>`_ until it has been marked as "ready". SDAM will mark +the pool as "ready" on each successful check. See `Connection Pool Management`_ +section in the SDAM specification for more information. + +.. code:: + + set generation to 0 + set state to "paused" + emit PoolCreatedEvent and equivalent log message + +Closing a Connection Pool +------------------------- + +When a pool is closed, it MUST first close all available `Connections <#connection>`_ in that pool. This results in the following behavior changes: + +- In use `Connections <#connection>`_ MUST be closed when they are checked in to the closed pool. +- Attempting to check out a `Connection <#connection>`_ MUST result in an error. + +.. code:: + + mark pool as "closed" + for connection in availableConnections: + close connection + emit PoolClosedEvent and equivalent log message + +Marking a Connection Pool as Ready +---------------------------------- + +Connection Pools start off as "paused", and they are marked as "ready" by +monitors after they perform successful server checks. Once a pool is "ready", +it can start checking out `Connections <#connection>`_ and populating them in +the background. + +If the pool is already "ready" when this method is invoked, then this +method MUST immediately return and MUST NOT emit a PoolReadyEvent. + +.. code:: + + mark pool as "ready" + emit PoolReadyEvent and equivalent log message + allow background thread to create connections + +Note that the PoolReadyEvent MUST be emitted before the background thread is allowed to resume creating new connections, +and it must be the case that no observer is able to observe actions of the background thread +related to creating new connections before observing the PoolReadyEvent event. + +Creating a Connection (Internal Implementation) +----------------------------------------------- + +When creating a `Connection <#connection>`_, the initial `Connection <#connection>`_ is in a +“pending” state. This only creates a “virtual” `Connection <#connection>`_, and +performs no I/O. + +.. code:: + + connection = new Connection() + increment totalConnectionCount + increment pendingConnectionCount + set connection state to "pending" + emit ConnectionCreatedEvent and equivalent log message + return connection + +Establishing a Connection (Internal Implementation) +--------------------------------------------------- + +Before a `Connection <#connection>`_ can be marked as either "available" or "in use", it +must be established. This process involves performing the initial +handshake, handling OP_COMPRESSED, and performing authentication. + +.. code:: + + try: + connect connection via TCP / TLS + perform connection handshake + handle OP_COMPRESSED + perform connection authentication + emit ConnectionReadyEvent and equivalent log message + return connection + except error: + close connection + throw error # Propagate error in manner idiomatic to language. + + +Closing a Connection (Internal Implementation) +---------------------------------------------- + +When a `Connection <#connection>`_ is closed, it MUST first be marked as "closed", +removing it from being counted as "available" or "in use". Once that is +complete, the `Connection <#connection>`_ can perform whatever teardown is +necessary to close its underlying socket. The Driver SHOULD perform this +teardown in a non-blocking manner, such as via the use of a background +thread or async I/O. + +.. code:: + + original state = connection state + set connection state to "closed" + + if original state is "available": + decrement availableConnectionCount + else if original state is "pending": + decrement pendingConnectionCount + + decrement totalConnectionCount + emit ConnectionClosedEvent and equivalent log message + + # The following can happen at a later time (i.e. in background + # thread) or via non-blocking I/O. + connection.socket.close() + +Marking a Connection as Available (Internal Implementation) +----------------------------------------------------------- + +A `Connection <#connection>`_ is "available" if it is able to be checked out. A +`Connection <#connection>`_ MUST NOT be marked as "available" until it has been +established. The pool MUST keep track of the number of currently +available `Connections <#connection>`_. + +.. code:: + + increment availableConnectionCount + set connection state to "available" + add connection to availableConnections + + +Populating the Pool with a Connection (Internal Implementation) +--------------------------------------------------------------- + +"Populating" the pool involves preemptively creating and establishing a +`Connection <#connection>`_ which is marked as "available" for use in future +operations. + +Populating the pool MUST NOT block any application threads. For example, it +could be performed on a background thread or via the use of non-blocking/async +I/O. Populating the pool MUST NOT be performed unless the pool is "ready". + +If an error is encountered while populating a connection, it MUST be handled via +the SDAM machinery according to the `Application Errors`_ section in the SDAM +specification. + +If minPoolSize is set, the `Connection <#connection>`_ Pool MUST be populated +until it has at least minPoolSize total `Connections <#connection>`_. This MUST +occur only while the pool is "ready". If the pool implements a background +thread, it can be used for this. If the pool does not implement a background +thread, the checkOut method is responsible for ensuring this requirement is met. + +When populating the Pool, pendingConnectionCount has to be decremented after +establishing a `Connection`_ similarly to how it is done in +`Checking Out a Connection <#checking-out-a-connection>`_ to signal that +another `Connection`_ is allowed to be established. Such a signal MUST become +observable to any `Thread`_ after the action that +`marks the established Connection as "available" <#marking-a-connection-as-available-internal-implementation>`_ +becomes observable to the `Thread`_. +Informally, this order guarantees that no `Thread`_ tries to start +establishing a `Connection`_ when there is an "available" `Connection`_ +established as a result of populating the Pool. + +.. code:: + + wait until pendingConnectionCount < maxConnecting and pool is "ready" + create connection + try: + establish connection + mark connection as available + except error: + # Defer error handling to SDAM. + topology.handle_pre_handshake_error(error) + +Checking Out a Connection +------------------------- + +A Pool MUST have a method that allows the driver to check out a `Connection`_. +Checking out a `Connection`_ involves submitting a request to the WaitQueue and, +once that request reaches the front of the queue, having the Pool find or create +a `Connection`_ to fulfill that request. Requests MUST be subject to a timeout +which is computed per the rules in +`Client Side Operations Timeout: Server Selection +<../client-side-operations-timeout/client-side-operations-timeout.rst#server-selection>`_. + +To service a request for a `Connection`_, the Pool MUST first iterate over the +list of available `Connections <#connection>`_, searching for a non-perished one +to be returned. If a perished `Connection`_ is encountered, such a `Connection`_ +MUST be closed (as described in `Closing a Connection +<#closing-a-connection-internal-implementation>`_) and the iteration of +available `Connections <#connection>`_ MUST continue until either a non-perished +available `Connection`_ is found or the list of available `Connections +<#connection>`_ is exhausted. + +If the list is exhausted, the total number of `Connections <#connection>`_ is +less than maxPoolSize, and pendingConnectionCount < maxConnecting, the pool MUST +create a `Connection`_, establish it, mark it as "in use" and return it. If +totalConnectionCount == maxPoolSize or pendingConnectionCount == maxConnecting, +then the pool MUST wait to service the request until neither of those two +conditions are met or until a `Connection`_ becomes available, re-entering the +checkOut loop in either case. This waiting MUST NOT prevent `Connections +<#connection>`_ from being checked into the pool. Additionally, the Pool MUST +NOT service any newer checkOut requests before fulfilling the original one which +could not be fulfilled. For drivers that implement the WaitQueue via a fair +semaphore, a condition variable may also be needed to to meet this +requirement. Waiting on the condition variable SHOULD also be limited by the +WaitQueueTimeout, if the driver supports one and it was specified by the user. + +If the pool is "closed" or "paused", any attempt to check out a `Connection +<#connection>`_ MUST throw an Error. The error thrown as a result of the pool +being "paused" MUST be considered a retryable error and MUST NOT be an error +that marks the SDAM state unknown. + +If the pool does not implement a background thread, the checkOut method is +responsible for ensuring that the pool is `populated +<#populating-the-pool-with-a-connection-internal-implementation>`_ with at least minPoolSize +`Connections <#connection>`_. + +A `Connection <#connection>`_ MUST NOT be checked out until it is +established. In addition, the Pool MUST NOT prevent other threads from checking +out `Connections <#connection>`_ while establishing a `Connection +<#connection>`_. + +Before a given `Connection <#connection>`_ is returned from checkOut, it must be marked as +"in use", and the pool's availableConnectionCount MUST be decremented. + +.. code:: + + connection = Null + emit ConnectionCheckOutStartedEvent and equivalent log message + try: + enter WaitQueue + wait until at top of wait queue + # Note that in a lock-based implementation of the wait queue would + # only allow one thread in the following block at a time + while connection is Null: + if a connection is available: + while connection is Null and a connection is available: + connection = next available connection + if connection is perished: + close connection + connection = Null + else if totalConnectionCount < maxPoolSize: + if pendingConnectionCount < maxConnecting: + connection = create connection + else: + # this waiting MUST NOT prevent other threads from checking Connections + # back in to the pool. + wait until pendingConnectionCount < maxConnecting or a connection is available + continue + + except pool is "closed": + emit ConnectionCheckOutFailedEvent(reason="poolClosed") and equivalent log message + throw PoolClosedError + except pool is "paused": + emit ConnectionCheckOutFailedEvent(reason="connectionError") and equivalent log message + throw PoolClearedError + except timeout: + emit ConnectionCheckOutFailedEvent(reason="timeout") and equivalent log message + throw WaitQueueTimeoutError + finally: + # This must be done in all drivers + leave wait queue + + # If there is no background thread, the pool MUST ensure that + # there are at least minPoolSize total connections. + # This MUST be done in a non-blocking manner + while totalConnectionCount < minPoolSize: + populate the pool with a connection + + # If the Connection has not been established yet (TCP, TLS, + # handshake, compression, and auth), it must be established + # before it is returned. + # This MUST NOT block other threads from acquiring connections. + if connection state is "pending": + try: + establish connection + except connection establishment error: + emit ConnectionCheckOutFailedEvent(reason="connectionError") and equivalent log message + decrement totalConnectionCount + throw + finally: + decrement pendingConnectionCount + else: + decrement availableConnectionCount + set connection state to "in use" + emit ConnectionCheckedOutEvent and equivalent log message + return connection + +Checking In a Connection +------------------------ + +A Pool MUST have a method of allowing the driver to check in a +`Connection <#connection>`_. The driver MUST NOT be allowed to check in a +`Connection <#connection>`_ to a Pool that did not create that `Connection <#connection>`_, and +MUST throw an Error if this is attempted. + +When the `Connection <#connection>`_ is checked in, it MUST be `closed +<#closing-a-connection-internal-implementation>`_ if any of the following are +true: + +- The `Connection <#connection>`_ is perished. +- The pool has been closed. + +Otherwise, the `Connection <#connection>`_ is marked as available. + +.. code:: + + emit ConnectionCheckedInEvent and equivalent log message + if connection is perished OR pool is closed: + close connection + else: + mark connection as available + +Clearing a Connection Pool +-------------------------- + +Clearing the pool involves different steps depending on whether the pool is in +load balanced mode or not. The traditional / non-load balanced clearing behavior +MUST NOT be used by pools in load balanced mode, and the load balanced pool +clearing behavior MUST NOT be used in non-load balanced pools. + +Clearing a non-load balanced pool +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A Pool MUST have a method of clearing all `Connections <#connection>`_ when +instructed. Rather than iterating through every `Connection <#connection>`_, +this method should simply increment the generation of the Pool, implicitly +marking all current `Connections <#connection>`_ as stale. It should also +transition the pool's state to "paused" to halt the creation of new connections +until it is marked as "ready" again. The checkOut and checkIn algorithms will +handle clearing out stale `Connections <#connection>`_. If a user is subscribed +to Connection Monitoring events and/or connection log messages, a PoolClearedEvent +and log message MUST be emitted after incrementing the generation / marking the pool +as "paused". If the pool is already "paused" when it is cleared, then the pool MUST +NOT emit a PoolCleared event or log message. + +As part of clearing the pool, the WaitQueue MUST also be cleared, meaning all +requests in the WaitQueue MUST fail with errors indicating that the pool was +cleared while the checkOut was being performed. The error returned as a result +of the pool being cleared MUST be considered a retryable error and MUST NOT be +an error that marks the SDAM state unknown. Clearing the WaitQueue MUST happen +eagerly so that any operations waiting on `Connections <#connection>`_ can retry +as soon as possible. The pool MUST NOT rely on WaitQueueTimeoutMS to clear +requests from the WaitQueue. + +The clearing method MUST provide the option to interrupt any in-use connections as part +of the clearing (henceforth referred to as the interruptInUseConnections flag in this +specification). "Interrupting a Connection" is defined as canceling whatever task the +Connection is currently performing and marking the Connection as perished (e.g. by closing +its underlying socket). The interrupting of these Connections MUST be performed as soon as possible +but MUST NOT block the pool or prevent it from processing further requests. If the pool has a background +thread, and it is responsible for interrupting in-use connections, its next run MUST be scheduled as soon as +possible. + +The pool MUST only interrupt in-use Connections whose generation is less than or equal +to the generation of the pool at the moment of the clear (before the increment) +that used the interruptInUseConnections flag. Any operations that have their Connections +interrupted in this way MUST fail with a retryable error. If possible, the error SHOULD +be a PoolClearedError with the following message: "Connection to interrupted +due to server monitor timeout". + +Clearing a load balanced pool +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A Pool MUST also have a method of clearing all `Connections <#connection>`_ for +a specific ``serviceId`` for use when in load balancer mode. This method +increments the generation of the pool for that specific ``serviceId`` in the +generation map. A PoolClearedEvent and log message MUST be emitted after incrementing the +generation. Note that this method MUST NOT transition the pool to the "paused" +state and MUST NOT clear the WaitQueue. + +Load Balancer Mode +------------------ + +For load-balanced deployments, pools MUST maintain a map from ``serviceId`` to a +tuple of (generation, connection count) where the connection count refers to the +total number of connections that exist for a specific ``serviceId``. The pool MUST +remove the entry for a ``serviceId`` once the connection count reaches 0. +Once the MongoDB handshake is done, the connection MUST get the +generation number that applies to its ``serviceId`` from the map and update the +map to increment the connection count for this ``serviceId``. + +See the `Load Balancer Specification <../load-balancers/load-balancers.rst#connection-pooling>`__ for details. + + +Forking +------- + +A `Connection <#connection>`_ is explicitly not fork-safe. The proper behavior in the case of a fork is to ResetAfterFork by: + +- clear all Connection Pools in the child process +- closing all `Connections <#connection>`_ in the child-process. + +Drivers that support forking MUST document that `Connections <#connection>`_ to an Endpoint are not fork-safe, and document the proper way to ResetAfterFork in the driver. + +Drivers MAY aggressively ResetAfterFork if the driver detects it has been forked. + +Optional Behaviors +------------------ + +The following features of a Connection Pool SHOULD be implemented if they make sense in the driver and driver's language. + +Background Thread +^^^^^^^^^^^^^^^^^ + +A Pool SHOULD have a background Thread that is responsible for +monitoring the state of all available `Connections <#connection>`_. This background +thread SHOULD + +- Populate `Connections <#connection>`_ to ensure that the pool always satisfies minPoolSize. +- Remove and close perished available `Connections <#connection>`_ including "in use" connections if `interruptInUseConnections` option was set to true in the most recent pool clear. +- Apply timeouts to connection establishment per `Client Side Operations + Timeout: Background Connection Pooling + <../client-side-operations-timeout/client-side-operations-timeout.rst#background-connection-pooling>`__. + +A pool SHOULD allow immediate scheduling of the next background thread iteration after a clear is performed. + +Conceptually, the aforementioned activities are organized into sequential Background Thread Runs. +A Run MUST do as much work as readily available and then end instead of waiting for more work. +For example, instead of waiting for pendingConnectionCount to become less than maxConnecting when satisfying minPoolSize, +a Run MUST either proceed with the rest of its duties, e.g., closing available perished connections, or end. + +The duration of intervals between the end of one Run and the beginning of the next Run is not specified, +but the +`Test Format and Runner Specification `__ +may restrict this duration, or introduce other restrictions to facilitate testing. + +withConnection +^^^^^^^^^^^^^^ + +A Pool SHOULD implement a scoped resource management mechanism idiomatic to their language to prevent `Connections <#connection>`_ from not being checked in. Examples include `Python's "with" statement `__ and `C#'s "using" statement `__. If implemented, drivers SHOULD use this method as the default method of checking out and checking in `Connections <#connection>`_. + +.. _connection-pool-monitoring-1: + +Connection Pool Monitoring +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +All drivers that implement a connection pool MUST provide an API that allows users to subscribe to events emitted from the pool. If a user subscribes to Connection Monitoring events, these events MUST be emitted when specified in “Connection Pool Behaviors”. Events SHOULD be created and subscribed to in a manner idiomatic to their language and driver. + +Events +------ + +See the `Load Balancer Specification <../load-balancers/load-balancers.rst#events>`__ for details on the ``serviceId`` field. + +.. code:: typescript + + /** + * Emitted when a Connection Pool is created + */ + interface PoolCreatedEvent { + /** + * The ServerAddress of the Endpoint the pool is attempting to connect to. + */ + address: string; + + /** + * Any non-default pool options that were set on this Connection Pool. + */ + options: {...} + } + + /** + * Emitted when a Connection Pool is marked as ready. + */ + interface PoolReadyEvent { + /** + * The ServerAddress of the Endpoint the pool is attempting to connect to. + */ + address: string; + } + + /** + * Emitted when a Connection Pool is cleared + */ + interface PoolClearedEvent { + /** + * The ServerAddress of the Endpoint the pool is attempting to connect to. + */ + address: string; + + /** + * The service id for which the pool was cleared for in load balancing mode. + * See load balancer specification for more information about this field. + */ + serviceId: Optional; + + /** + * A flag whether the pool forced interrupting "in use" connections as part of the clear. + */ + interruptInUseConnections: Optional; + } + + /** + * Emitted when a Connection Pool is closed + */ + interface PoolClosedEvent { + /** + * The ServerAddress of the Endpoint the pool is attempting to connect to. + */ + address: string; + } + + /** + * Emitted when a Connection Pool creates a Connection object. + * NOTE: This does not mean that the Connection is ready for use. + */ + interface ConnectionCreatedEvent { + /** + * The ServerAddress of the Endpoint the pool is attempting to connect to. + */ + address: string; + + /** + * The ID of the Connection + */ + connectionId: number; + } + + /** + * Emitted when a Connection has finished its setup, and is now ready to use + */ + interface ConnectionReadyEvent { + /** + * The ServerAddress of the Endpoint the pool is attempting to connect to. + */ + address: string; + + /** + * The ID of the Connection + */ + connectionId: number; + } + + /** + * Emitted when a Connection Pool closes a Connection + */ + interface ConnectionClosedEvent { + /** + * The ServerAddress of the Endpoint the pool is attempting to connect to. + */ + address: string; + + /** + * The ID of the Connection + */ + connectionId: number; + + /** + * A reason explaining why this Connection was closed. + * Can be implemented as a string or enum. + * Current valid values are: + * - "stale": The pool was cleared, making the Connection no longer valid + * - "idle": The Connection became stale by being available for too long + * - "error": The Connection experienced an error, making it no longer valid + * - "poolClosed": The pool was closed, making the Connection no longer valid + */ + reason: string|Enum; + } + + /** + * Emitted when the driver starts attempting to check out a Connection + */ + interface ConnectionCheckOutStartedEvent { + /** + * The ServerAddress of the Endpoint the pool is attempting + * to connect to. + */ + address: string; + } + + /** + * Emitted when the driver's attempt to check out a Connection fails + */ + interface ConnectionCheckOutFailedEvent { + /** + * The ServerAddress of the Endpoint the pool is attempting to connect to. + */ + address: string; + + /** + * A reason explaining why Connection check out failed. + * Can be implemented as a string or enum. + * Current valid values are: + * - "poolClosed": The pool was previously closed, and cannot provide new Connections + * - "timeout": The Connection check out attempt exceeded the specified timeout + * - "connectionError": The Connection check out attempt experienced an error while setting up a new Connection + */ + reason: string|Enum; + } + + /** + * Emitted when the driver successfully checks out a Connection + */ + interface ConnectionCheckedOutEvent { + /** + * The ServerAddress of the Endpoint the pool is attempting to connect to. + */ + address: string; + + /** + * The ID of the Connection + */ + connectionId: number; + } + + /** + * Emitted when the driver checks in a Connection back to the Connection Pool + */ + interface ConnectionCheckedInEvent { + /** + * The ServerAddress of the Endpoint the pool is attempting to connect to. + */ + address: string; + + /** + * The ID of the Connection + */ + connectionId: number; + } + +Connection Pool Logging +~~~~~~~~~~~~~~~~~~~~~~~ +Please refer to the `logging specification <../logging/logging.rst>`_ for details on logging implementations in general, including log levels, log +components, handling of null values in log messages, and structured versus unstructured logging. + +Drivers MUST support logging of connection pool information via the following types of log messages. These messages MUST be logged at ``Debug`` level +and use the ``connection`` log component. These messages MUST be emitted when specified in “Connection Pool Behaviors”. + +The log messages are intended to match the information contained in the events above. Drivers MAY implement connection logging support via an event +subscriber if it is convenient to do so. + +The types used in the structured message definitions below are demonstrative, and drivers MAY use similar types instead so long as the information +is present (e.g. a double instead of an integer, or a string instead of an integer if the structured logging framework does not support numeric types). + +Common Fields +------------- +All connection log messages MUST contain the following key-value pairs: + +.. list-table:: + :header-rows: 1 + :widths: 1 1 1 + + * - Key + - Suggested Type + - Value + + * - serverHost + - String + - the hostname, IP address, or Unix domain socket path for the endpoint the pool is for. + + * - serverPort + - Int + - The port for the endpoint the pool is for. Optional; not present for Unix domain sockets. When + the user does not specify a port and the default (27017) is used, the driver SHOULD include it here. + +Pool Created Message +--------------------- +In addition to the common fields defined above, this message MUST contain the following key-value pairs: + +.. list-table:: + :header-rows: 1 + :widths: 1 1 1 + + * - Key + - Suggested Type + - Value + + * - message + - String + - "Connection pool created" + + * - maxIdleTimeMS + - Int + - The maxIdleTimeMS value for this pool. Optional; only required to include if the user specified a value. + + * - minPoolSize + - Int + - The minPoolSize value for this pool. Optional; only required to include if the user specified a value. + + * - maxPoolSize + - Int + - The maxPoolSize value for this pool. Optional; only required to include if the user specified a value. + + * - maxConnecting + - Int + - The maxConnecting value for this pool. Optional; only required to include if the driver supports this option and the user + specified a value. + + * - waitQueueTimeoutMS + - Int + - The waitQueueTimeoutMS value for this pool. Optional; only required to include if the driver supports this option and the + user specified a value. + + * - waitQueueSize + - Int + - The waitQueueSize value for this pool. Optional; only required to include if the driver supports this option and the + user specified a value. + + * - waitQueueMultiple + - Int + - The waitQueueMultiple value for this pool. Optional; only required to include if the driver supports this option and the + user specified a value. + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in placeholders as appropriate: + + Connection pool created for {{serverHost}}:{{serverPort}} using options maxIdleTimeMS={{maxIdleTimeMS}}, + minPoolSize={{minPoolSize}}, maxPoolSize={{maxPoolSize}}, maxConnecting={{maxConnecting}}, waitQueueTimeoutMS={{waitQueueTimeoutMS}}, + waitQueueSize={{waitQueueSize}}, waitQueueMultiple={{waitQueueMultiple}} + +Pool Ready Message +------------------ +In addition to the common fields defined above, this message MUST contain the following key-value pairs: + +.. list-table:: + :header-rows: 1 + :widths: 1 1 1 + + * - Key + - Suggested Type + - Value + + * - message + - String + - "Connection pool ready" + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in placeholders as appropriate: + + Connection pool ready for {{serverHost}}:{{serverPort}} + +Pool Cleared Message +-------------------- +In addition to the common fields defined above, this message MUST contain the following key-value pairs: + +.. list-table:: + :header-rows: 1 + :widths: 1 1 1 + + * - Key + - Suggested Type + - Value + + * - message + - String + - "Connection pool cleared" + + * - serviceId + - String + - The hex string representation of the service ID which the pool was cleared for. Optional; only present in load balanced mode. + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in placeholders as appropriate: + + Connection pool for {{serverHost}}:{{serverPort}} cleared for serviceId {{serviceId}} + +Pool Closed Message +------------------- +In addition to the common fields defined above, this message MUST contain the following key-value pairs: + +.. list-table:: + :header-rows: 1 + :widths: 1 1 1 + + * - Key + - Suggested Type + - Value + + * - message + - String + - "Connection pool closed" + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in placeholders as appropriate: + + Connection pool closed for {{serverHost}}:{{serverPort}} + +Connection Created Message +-------------------------- +In addition to the common fields defined above, this message MUST contain the following key-value pairs: + +.. list-table:: + :header-rows: 1 + :widths: 1 1 1 + + * - Key + - Suggested Type + - Value + + * - message + - String + - "Connection created" + + * - driverConnectionId + - Int + - The driver-generated ID for the connection as defined in `Connection <#connection>`_. + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in placeholders as appropriate: + + Connection created: address={{serverHost}}:{{serverPort}}, driver-generated ID={{driverConnectionId}} + +Connection Ready Message +------------------------ +In addition to the common fields defined above, this message MUST contain the following key-value pairs: + +.. list-table:: + :header-rows: 1 + :widths: 1 1 1 + + * - Key + - Suggested Type + - Value + + * - message + - String + - "Connection ready" + + * - driverConnectionId + - Int + - The driver-generated ID for the connection as defined in `Connection <#connection>`_. + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in placeholders as appropriate: + + Connection ready: address={{serverHost}}:{{serverPort}}, driver-generated ID={{driverConnectionId}} + +Connection Closed Message +------------------------- +In addition to the common fields defined above, this message MUST contain the following key-value pairs: + +.. list-table:: + :header-rows: 1 + :widths: 1 1 1 + + * - Key + - Suggested Type + - Value + + * - message + - String + - "Connection closed" + + * - driverConnectionId + - Int + - The driver-generated ID for the connection as defined in `Connection <#connection>`_. + + * - reason + - String + - A string describing the reason the connection was closed. The following strings MUST be used for each possible reason + as defined in `Events <#events>`_ above: + + - Stale: "Connection became stale because the pool was cleared" + - Idle: "Connection has been available but unused for longer than the configured max idle time" + - Error: "An error occurred while using the connection" + - Pool closed: "Connection pool was closed" + + * - error + - Flexible + - If ``reason`` is ``Error``, the associated error. The type and format of this value is flexible; see the + `logging specification <../logging/logging.rst#representing-errors-in-log-messages>`_ for details on representing errors in log messages. + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in placeholders as appropriate: + + Connection closed: address={{serverHost}}:{{serverPort}}, driver-generated ID={{driverConnectionId}}. Reason: {{reason}}. Error: {{error}} + +Connection Checkout Started Message +----------------------------------- +In addition to the common fields defined above, this message MUST contain the following key-value pairs: + +.. list-table:: + :header-rows: 1 + :widths: 1 1 1 + + * - Key + - Suggested Type + - Value + + * - message + - String + - "Connection checkout started" + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in placeholders as appropriate: + + Checkout started for connection to {{serverHost}}:{{serverPort}} + +Connection Checkout Failed Message +----------------------------------- +In addition to the common fields defined above, this message MUST contain the following key-value pairs: + +.. list-table:: + :header-rows: 1 + :widths: 1 1 1 + + * - Key + - Suggested Type + - Value + + * - message + - String + - "Connection checkout failed" + + * - reason + - String + - A string describing the reason checkout. The following strings MUST be used for each possible reason + as defined in `Events <#events>`_ above: + + - Timeout: "Wait queue timeout elapsed without a connection becoming available" + - ConnectionError: "An error occurred while trying to establish a new connection" + - Pool closed: "Connection pool was closed" + + * - error + - Flexible + - If ``reason`` is ``ConnectionError``, the associated error. The type and format of this value is flexible; see the + `logging specification <../logging/logging.rst#representing-errors-in-log-messages>`_ for details on representing errors in log messages. + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in placeholders as appropriate: + + Checkout failed for connection to {{serverHost}}:{{serverPort}}. Reason: {{reason}}. Error: {{error}} + +Connection Checked Out +----------------------- +In addition to the common fields defined above, this message MUST contain the following key-value pairs: + +.. list-table:: + :header-rows: 1 + :widths: 1 1 1 + + * - Key + - Suggested Type + - Value + + * - message + - String + - "Connection checked out" + + * - driverConnectionId + - Int + - The driver-generated ID for the connection as defined in `Connection <#connection>`_. + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in placeholders as appropriate: + + Connection checked out: address={serverHost}}:{{serverPort}}, driver-generated ID={{driverConnectionId}} + +Connection Checked In +--------------------- +In addition to the common fields defined above, this message MUST contain the following key-value pairs: + +.. list-table:: + :header-rows: 1 + :widths: 1 1 1 + + * - Key + - Suggested Type + - Value + + * - message + - String + - "Connection checked in" + + * - driverConnectionId + - Int + - The driver-generated ID for the connection as defined in `Connection <#connection>`_. + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in placeholders as appropriate: + + Connection checked in: address={{serverHost}}:{{serverPort}}, driver-generated ID={{driverConnectionId}} + +Connection Pool Errors +~~~~~~~~~~~~~~~~~~~~~~ + +A connection pool throws errors in specific circumstances. These Errors +MUST be emitted by the pool. Errors SHOULD be created and dispatched in +a manner idiomatic to the Driver and Language. + +.. code:: typescript + + /** + * Thrown when the driver attempts to check out a + * Connection from a closed Connection Pool + */ + interface PoolClosedError { + message: 'Attempted to check out a Connection from closed connection pool'; + address: ; + } + + /** + * Thrown when the driver attempts to check out a + * Connection from a paused Connection Pool + */ + interface PoolClearedError extends RetryableError { + message: 'Connection pool for was cleared because another operation failed with: '; + address: ; + } + + /** + * Thrown when a driver times out when attempting to check out + * a Connection from a Pool + */ + interface WaitQueueTimeoutError { + message: 'Timed out while checking out a Connection from connection pool'; + address: ; + } + +Test Plan +========= + +See `tests/README.rst `_ + +Design Rationale +================ + +Why do we set minPoolSize across all members of a replicaSet, when most traffic will be against a Primary? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Currently, we are attempting to codify our current pooling behavior with minimal changes, and minPoolSize is currently uniform across all members of a replicaSet. This has the benefit of offsetting connection swarming during a Primary Step-Down, which will be further addressed in our `Advanced Pooling Behaviors <#advanced-pooling-behaviors>`__. + +Why do we have separate ConnectionCreated and ConnectionReady events, but only one ConnectionClosed event? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +ConnectionCreated and ConnectionReady each involve different state changes in the pool. + +- ConnectionCreated adds a new “pending” `Connection <#connection>`_, meaning + the totalConnectionCount and pendingConnectionCount increase by one +- ConnectionReady establishes that the `Connection <#connection>`_ is ready for use, meaning the availableConnectionCount increases by one + +ConnectionClosed indicates that the `Connection <#connection>`_ is no longer a member of the pool, decrementing totalConnectionCount and potentially availableConnectionCount. After this point, the `Connection <#connection>`_ is no longer a part of the pool. Further hypothetical events would not indicate a change to the state of the pool, so they are not specified here. + +Why are waitQueueSize and waitQueueMultiple deprecated? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +These options were originally only implemented in three drivers (Java, C#, and Python), and provided little value. While these fields would allow for faster diagnosis of issues in the connection pool, they would not actually prevent an error from occurring. + +Additionally, these options have the effect of prioritizing older requests over newer requests, which is not necessarily the behavior that users want. They can also result in cases where queue access oscillates back and forth between full and not full. If a driver has a full waitQueue, then all requests for `Connections <#connection>`_ will be rejected. If the client is continually spammed with requests, you could wind up with a scenario where as soon as the waitQueue is no longer full, it is immediately filled. It is not a favorable situation to be in, partially b/c it violates the fairness guarantee that the waitQueue normally provides. + +Because of these issues, it does not make sense to `go against driver mantras and provide an additional knob <../../README.rst#>`__. We may eventually pursue an alternative configurations to address wait queue size in `Advanced Pooling Behaviors <#advanced-pooling-behaviors>`__. + +Users that wish to have this functionality can achieve similar results by utilizing other methods to limit concurrency. Examples include implementing either a thread pool or an operation queue with a capped size in the user application. Drivers that need to deprecate ``waitQueueSize`` and/or ``waitQueueMultiple`` SHOULD refer users to these examples. + +Why is waitQueueTimeoutMS optional for some drivers? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We are anticipating eventually introducing a single client-side timeout mechanism, making us hesitant to introduce another granular timeout control. Therefore, if a driver/language already has an idiomatic way to implement their timeouts, they should leverage that mechanism over implementing waitQueueTimeoutMS. + +Why must populating the pool require the use of a background thread or async I/O? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Without the use of a background thread, the pool is `populated +<#populating-the-pool-with-a-connection-internal-implementation>`_ with enough +connections to satisfy minPoolSize during checkOut. `Connections <#connection>`_ +are established as part of populating the pool though, so if `Connection +<#connection>`_ establishment were done in a blocking fashion, the first +operations after a clearing of the pool would experience unacceptably high +latency, especially for larger values of minPoolSize. Thus, populating the pool +must occur on a background thread (which is acceptable to block) or via the +usage of non-blocking (async) I/O. + +Why should closing a connection be non-blocking? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Because idle and perished `Connections <#connection>`_ are cleaned up as part of +checkOut, performing blocking I/O while closing such `Connections <#connection>`_ +would block application threads, introducing unnecessary latency. Once +a `Connection <#connection>`_ is marked as "closed", it will not be checked out +again, so ensuring the socket is torn down does not need to happen +immediately and can happen at a later time, either via async I/O or a +background thread. + +Why can the pool be paused? +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The distinction between the "paused" state and the "ready" state allows the pool +to determine whether or not the endpoint it is associated with is available or +not. This enables the following behaviors: + +1. The pool can halt the creation of background connection establishments until + the endpoint becomes available again. Without the "paused" state, the pool + would have no way of determining when to begin establishing background + connections again, so it would just continually attempt, and often fail, to + create connections until minPoolSize was satisfied, even after repeated + failures. This could unnecessarily waste resources both server and driver side. + +2. The pool can evict requests that enter the WaitQueue after the pool was + cleared but before the server was in a known state again. Such requests can + occur when a server is selected at the same time as it becomes marked as + Unknown in highly concurrent workloads. Without the "paused" state, the pool + would attempt to service these requests, since it would assume they were + routed to the pool because its endpoint was available, not because of a race + between SDAM and Server Selection. These requests would then likely fail with + potentially high latency, again wasting resources both server and driver side. + +Why not emit PoolCleared events and log messages when clearing a paused pool? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If a pool is already paused when it is cleared, that means it was previously +cleared and no new connections have been created since then. Thus, clearing the +pool in this case is essentially a no-op, so there is no need to notify any +listeners that it has occurred. The generation is still incremented, however, to +ensure future errors that caused the duplicate clear will stop attempting to +clear the pool again. This situation is possible if the pool is cleared by the +background thread after it encounters an error establishing a connection, but +the ServerDescription for the endpoint was not updated accordingly yet. + +Why does the pool need to support interrupting in use connections as part of its clear logic? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +If a SDAM monitor has observed a network timeout, we assume that all connections +including "in use" connections are no longer healthy. In some cases connections +will fail to detect the network timeout fast enough. For example, a server request +can hang at the OS level in TCP retry loop up for 17 minutes before failing. Therefore +these connections MUST be proactively interrupted in the case of a server monitor network timeout. +Requesting an immediate backround thread run will speed up this process. + +Why don't we configure TCP_USER_TIMEOUT? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Ideally, a reasonable TCP_USER_TIMEOUT can help with detecting stale connections as an +alternative to `interruptInUseConnections` in Clear. +Unfortunately this approach is platform dependent and not each driver allows easily configuring it. +For example, C# driver can configure this socket option on linux only with target frameworks +higher or equal to .net 5.0. On macOS, there is no straight equavalent for this option, +it's possible that we can find some equavalent configuration, but this configuration will also +require target frameworks higher than or equal to .net 5.0. The advantage of using Background Thread to +manage perished connections is that it will work regardless of environment setup. + +Backwards Compatibility +======================= + +As mentioned in `Deprecated Options <#deprecated-options>`__, some drivers currently implement the options ``waitQueueSize`` and/or ``waitQueueMultiple``. These options will need to be deprecated and phased out of the drivers that have implemented them. + + +Reference Implementations +========================= + +- JAVA (JAVA-3079) +- RUBY (RUBY-1560) + +Future Development +================== + +SDAM +~~~~ + +This specification does not dictate how SDAM Monitoring connections are managed. SDAM specifies that “A monitor SHOULD NOT use the client's regular Connection pool”. Some possible solutions for this include: + +- Having each Endpoint representation in the driver create and manage a separate dedicated `Connection <#connection>`_ for monitoring purposes +- Having each Endpoint representation in the driver maintain a separate pool of maxPoolSize 1 for monitoring purposes. +- Having each Pool maintain a dedicated `Connection <#connection>`_ for monitoring purposes, with an API to expose that Connection. + +Advanced Pooling Behaviors +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This spec does not address all advanced pooling behaviors like predictive pooling or aggressive `Connection <#connection>`_ creation. Future work may address this. + +Add support for OP_MSG exhaustAllowed +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Exhaust Cursors may require changes to how we close `Connections <#connection>`_ in the future, specifically to add a way to close and remove from its pool a `Connection <#connection>`_ which has unread exhaust messages. + + +Changelog +========= + +:2019-06-06: Add "connectionError" as a valid reason for ConnectionCheckOutFailedEvent +:2020-09-03: Clarify Connection states and definition. Require the use of a + background thread and/or async I/O. Add tests to ensure + ConnectionReadyEvents are fired after ConnectionCreatedEvents. +:2020-09-24: Introduce maxConnecting requirement +:2020-12-17: Introduce "paused" and "ready" states. Clear WaitQueue on pool clear. +:2021-01-12: Clarify "clear" method behavior in load balancer mode. +:2021-01-19: Require that timeouts be applied per the client-side operations + timeout specification. +:2021-04-12: Adding in behaviour for load balancer mode. +:2021-06-02: Formalize the behavior of a `Background Thread <#background-thread>`__. +:2021-11-08: Make maxConnecting configurable. +:2022-04-05: Preemptively cancel in progress operations when SDAM heartbeats timeout. +:2022-10-05: Remove spec front matter and reformat changelog. +:2022-10-14: Add connection pool log messages and associated tests. + +---- + +.. Section for links. + +.. _Application Errors: /source/server-discovery-and-monitoring/server-discovery-and-monitoring.rst#application-errors +.. _Connection Pool Management: /source/server-discovery-and-monitoring/server-discovery-and-monitoring.rst#connection-pool-management diff --git a/specifications/connection-monitoring-and-pooling/tests/README.rst b/specifications/connection-monitoring-and-pooling/tests/README.rst new file mode 100644 index 00000000000..ae4af543f20 --- /dev/null +++ b/specifications/connection-monitoring-and-pooling/tests/README.rst @@ -0,0 +1,36 @@ +.. role:: javascript(code) + :language: javascript + +======================================== +Connection Monitoring and Pooling (CMAP) +======================================== + +.. contents:: + +-------- + +Introduction +============ +Drivers MUST implement all of the following types of CMAP tests: + +* Pool unit and integration tests as described in `cmap-format/README.rst <./cmap-format/README.rst>`__ +* Pool prose tests as described below in `Prose Tests`_ +* Logging tests as described below in `Logging Tests`_ + +Prose Tests +=========== + +The following tests have not yet been automated, but MUST still be tested: + +#. All ConnectionPoolOptions MUST be specified at the MongoClient level +#. All ConnectionPoolOptions MUST be the same for all pools created by a MongoClient +#. A user MUST be able to specify all ConnectionPoolOptions via a URI string +#. A user MUST be able to subscribe to Connection Monitoring Events in a manner idiomatic to their language and driver +#. When a check out attempt fails because connection set up throws an error, + assert that a ConnectionCheckOutFailedEvent with reason="connectionError" is emitted. + +Logging Tests +============= + +Tests for connection pool logging can be found in the `/logging <./logging>`__ subdirectory and are written in the +`Unified Test Format <../../unified-test-format/unified-test-format.rst>`__. \ No newline at end of file diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/README.rst b/specifications/connection-monitoring-and-pooling/tests/cmap-format/README.rst similarity index 89% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/README.rst rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/README.rst index 85c6232955f..5bb72dd0fe6 100644 --- a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/README.rst +++ b/specifications/connection-monitoring-and-pooling/tests/cmap-format/README.rst @@ -1,9 +1,9 @@ .. role:: javascript(code) :language: javascript -======================================== -Connection Monitoring and Pooling (CMAP) -======================================== +=================================================================== +Connection Monitoring and Pooling (CMAP) Unit and Integration Tests +=================================================================== .. contents:: @@ -15,9 +15,6 @@ Introduction The YAML and JSON files in this directory are platform-independent tests that drivers can use to prove their conformance to the Connection Monitoring and Pooling (CMAP) Spec. -Several prose tests, which are not easily expressed in YAML, are also presented -in this file. Those tests will need to be manually implemented by each driver. - Common Test Format ================== @@ -101,7 +98,7 @@ Valid Unit Test Operations are the following: - ``pool.clear()``: call ``clear`` on Pool - - ``closeInUseConnections``: Determines whether "in use" connections should be also closed + - ``interruptInUseConnections``: Determines whether "in use" connections should be also interrupted - ``pool.close()``: call ``close`` on Pool - ``pool.ready()``: call ``ready`` on Pool @@ -216,16 +213,3 @@ unit tests with the following modifications: configureFailPoint: , mode: "off" }); - - -Prose Tests -=========== - -The following tests have not yet been automated, but MUST still be tested - -#. All ConnectionPoolOptions MUST be specified at the MongoClient level -#. All ConnectionPoolOptions MUST be the same for all pools created by a MongoClient -#. A user MUST be able to specify all ConnectionPoolOptions via a URI string -#. A user MUST be able to subscribe to Connection Monitoring Events in a manner idiomatic to their language and driver -#. When a check out attempt fails because connection set up throws an error, - assert that a ConnectionCheckOutFailedEvent with reason="connectionError" is emitted. diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/connection-must-have-id.json b/specifications/connection-monitoring-and-pooling/tests/cmap-format/connection-must-have-id.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/connection-must-have-id.json rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/connection-must-have-id.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/connection-must-have-id.yml b/specifications/connection-monitoring-and-pooling/tests/cmap-format/connection-must-have-id.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/connection-must-have-id.yml rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/connection-must-have-id.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/connection-must-order-ids.json b/specifications/connection-monitoring-and-pooling/tests/cmap-format/connection-must-order-ids.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/connection-must-order-ids.json rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/connection-must-order-ids.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/connection-must-order-ids.yml b/specifications/connection-monitoring-and-pooling/tests/cmap-format/connection-must-order-ids.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/connection-must-order-ids.yml rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/connection-must-order-ids.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkin-destroy-closed.json b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin-destroy-closed.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkin-destroy-closed.json rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin-destroy-closed.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkin-destroy-closed.yml b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin-destroy-closed.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkin-destroy-closed.yml rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin-destroy-closed.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkin-destroy-stale.json b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin-destroy-stale.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkin-destroy-stale.json rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin-destroy-stale.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkin-destroy-stale.yml b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin-destroy-stale.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkin-destroy-stale.yml rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin-destroy-stale.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkin-make-available.json b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin-make-available.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkin-make-available.json rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin-make-available.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkin-make-available.yml b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin-make-available.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkin-make-available.yml rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin-make-available.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkin.json b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkin.json rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkin.yml b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkin.yml rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkout-connection.json b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-connection.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkout-connection.json rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-connection.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkout-connection.yml b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-connection.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkout-connection.yml rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-connection.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkout-custom-maxConnecting-is-enforced.json b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-custom-maxConnecting-is-enforced.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkout-custom-maxConnecting-is-enforced.json rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-custom-maxConnecting-is-enforced.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkout-custom-maxConnecting-is-enforced.yml b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-custom-maxConnecting-is-enforced.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkout-custom-maxConnecting-is-enforced.yml rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-custom-maxConnecting-is-enforced.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkout-error-closed.json b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-error-closed.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkout-error-closed.json rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-error-closed.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkout-error-closed.yml b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-error-closed.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkout-error-closed.yml rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-error-closed.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkout-maxConnecting-is-enforced.json b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-maxConnecting-is-enforced.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkout-maxConnecting-is-enforced.json rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-maxConnecting-is-enforced.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkout-maxConnecting-is-enforced.yml b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-maxConnecting-is-enforced.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkout-maxConnecting-is-enforced.yml rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-maxConnecting-is-enforced.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkout-maxConnecting-timeout.json b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-maxConnecting-timeout.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkout-maxConnecting-timeout.json rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-maxConnecting-timeout.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkout-maxConnecting-timeout.yml b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-maxConnecting-timeout.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkout-maxConnecting-timeout.yml rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-maxConnecting-timeout.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkout-multiple.json b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-multiple.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkout-multiple.json rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-multiple.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkout-multiple.yml b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-multiple.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkout-multiple.yml rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-multiple.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkout-no-idle.json b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-no-idle.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkout-no-idle.json rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-no-idle.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkout-no-idle.yml b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-no-idle.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkout-no-idle.yml rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-no-idle.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkout-no-stale.json b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-no-stale.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkout-no-stale.json rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-no-stale.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkout-no-stale.yml b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-no-stale.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkout-no-stale.yml rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-no-stale.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkout-returned-connection-maxConnecting.json b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-returned-connection-maxConnecting.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkout-returned-connection-maxConnecting.json rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-returned-connection-maxConnecting.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkout-returned-connection-maxConnecting.yml b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-returned-connection-maxConnecting.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-checkout-returned-connection-maxConnecting.yml rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-returned-connection-maxConnecting.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-clear-clears-waitqueue.json b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-clears-waitqueue.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-clear-clears-waitqueue.json rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-clears-waitqueue.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-clear-clears-waitqueue.yml b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-clears-waitqueue.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-clear-clears-waitqueue.yml rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-clears-waitqueue.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-clear-close-in-use.json b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-close-in-use.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-clear-close-in-use.json rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-close-in-use.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-clear-close-in-use.yml b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-close-in-use.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-clear-close-in-use.yml rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-close-in-use.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-clear-min-size.json b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-min-size.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-clear-min-size.json rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-min-size.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-clear-min-size.yml b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-min-size.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-clear-min-size.yml rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-min-size.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-clear-paused.json b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-paused.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-clear-paused.json rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-paused.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-clear-paused.yml b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-paused.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-clear-paused.yml rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-paused.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-clear-ready.json b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-ready.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-clear-ready.json rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-ready.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-clear-ready.yml b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-ready.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-clear-ready.yml rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-ready.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-clear-schedule-run-closeInUseConnections-false.json b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-schedule-run-closeInUseConnections-false.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-clear-schedule-run-closeInUseConnections-false.json rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-schedule-run-closeInUseConnections-false.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-clear-schedule-run-closeInUseConnections-false.yml b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-schedule-run-closeInUseConnections-false.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-clear-schedule-run-closeInUseConnections-false.yml rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-schedule-run-closeInUseConnections-false.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-clear-schedule-run.json b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-schedule-run.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-clear-schedule-run.json rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-schedule-run.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-clear-schedule-run.yml b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-schedule-run.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-clear-schedule-run.yml rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-schedule-run.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-close-destroy-conns.json b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-close-destroy-conns.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-close-destroy-conns.json rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-close-destroy-conns.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-close-destroy-conns.yml b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-close-destroy-conns.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-close-destroy-conns.yml rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-close-destroy-conns.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-close.json b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-close.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-close.json rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-close.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-close.yml b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-close.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-close.yml rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-close.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-create-max-size.json b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-create-max-size.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-create-max-size.json rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-create-max-size.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-create-max-size.yml b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-create-max-size.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-create-max-size.yml rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-create-max-size.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-create-min-size-error.json b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-create-min-size-error.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-create-min-size-error.json rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-create-min-size-error.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-create-min-size-error.yml b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-create-min-size-error.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-create-min-size-error.yml rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-create-min-size-error.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-create-min-size.json b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-create-min-size.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-create-min-size.json rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-create-min-size.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-create-min-size.yml b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-create-min-size.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-create-min-size.yml rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-create-min-size.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-create-with-options.json b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-create-with-options.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-create-with-options.json rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-create-with-options.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-create-with-options.yml b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-create-with-options.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-create-with-options.yml rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-create-with-options.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-create.json b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-create.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-create.json rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-create.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-create.yml b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-create.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-create.yml rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-create.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-ready-ready.json b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-ready-ready.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-ready-ready.json rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-ready-ready.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-ready-ready.yml b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-ready-ready.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-ready-ready.yml rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-ready-ready.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-ready.json b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-ready.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-ready.json rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-ready.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-ready.yml b/specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-ready.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/pool-ready.yml rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/pool-ready.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/wait-queue-fairness.json b/specifications/connection-monitoring-and-pooling/tests/cmap-format/wait-queue-fairness.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/wait-queue-fairness.json rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/wait-queue-fairness.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/wait-queue-fairness.yml b/specifications/connection-monitoring-and-pooling/tests/cmap-format/wait-queue-fairness.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/wait-queue-fairness.yml rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/wait-queue-fairness.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/wait-queue-timeout.json b/specifications/connection-monitoring-and-pooling/tests/cmap-format/wait-queue-timeout.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/wait-queue-timeout.json rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/wait-queue-timeout.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/wait-queue-timeout.yml b/specifications/connection-monitoring-and-pooling/tests/cmap-format/wait-queue-timeout.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/tests/wait-queue-timeout.yml rename to specifications/connection-monitoring-and-pooling/tests/cmap-format/wait-queue-timeout.yml diff --git a/specifications/connection-monitoring-and-pooling/tests/logging/connection-logging.json b/specifications/connection-monitoring-and-pooling/tests/logging/connection-logging.json new file mode 100644 index 00000000000..c1e25f92b3f --- /dev/null +++ b/specifications/connection-monitoring-and-pooling/tests/logging/connection-logging.json @@ -0,0 +1,432 @@ +{ + "description": "connection-logging", + "schemaVersion": "1.13", + "runOnRequirements": [ + { + "topologies": [ + "single" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "failPointClient" + } + } + ], + "tests": [ + { + "description": "Create a client, run a command, and close the client", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeLogMessages": { + "connection": "debug" + } + } + } + ] + } + }, + { + "name": "listDatabases", + "object": "client", + "arguments": { + "filter": {} + } + }, + { + "name": "close", + "object": "client" + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection pool created", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection pool ready", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection checkout started", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection created", + "driverConnectionId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection ready", + "driverConnectionId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection checked out", + "driverConnectionId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection checked in", + "driverConnectionId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection closed", + "driverConnectionId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "reason": "Unknown" + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection pool closed", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + }, + { + "description": "Connection checkout fails due to error establishing connection", + "runOnRequirements": [ + { + "auth": true, + "minServerVersion": "4.0" + } + ], + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "retryReads": false, + "appname": "clientAppName", + "heartbeatFrequencyMS": 10000 + }, + "observeLogMessages": { + "connection": "debug" + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "saslContinue" + ], + "closeConnection": true, + "appName": "clientAppName" + } + } + } + }, + { + "name": "listDatabases", + "object": "client", + "arguments": { + "filter": {} + }, + "expectError": { + "isClientError": true + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection pool created", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection pool ready", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection checkout started", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection created", + "driverConnectionId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection pool cleared", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection closed", + "driverConnectionId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "reason": "Unknown" + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection checkout failed", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "reason": "An error occurred while trying to establish a new connection", + "error": { + "$$exists": true + } + } + } + ] + } + ] + } + ] +} diff --git a/specifications/connection-monitoring-and-pooling/tests/logging/connection-logging.yml b/specifications/connection-monitoring-and-pooling/tests/logging/connection-logging.yml new file mode 100644 index 00000000000..58ac7ec3413 --- /dev/null +++ b/specifications/connection-monitoring-and-pooling/tests/logging/connection-logging.yml @@ -0,0 +1,196 @@ +description: "connection-logging" + +schemaVersion: "1.13" + +runOnRequirements: + - topologies: + - single # The number of log messages is different for each topology since there is a connection pool per host. + +createEntities: + - client: + id: &failPointClient failPointClient + +tests: + - description: "Create a client, run a command, and close the client" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + observeLogMessages: + connection: debug + - name: listDatabases + object: *client + arguments: + filter: {} + - name: close + object: *client + expectLogMessages: + - client: *client + messages: + - level: debug + component: connection + data: + message: "Connection pool created" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - level: debug + component: connection + data: + message: "Connection pool ready" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - level: debug + component: connection + data: + message: "Connection checkout started" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - level: debug + component: connection + data: + message: "Connection created" + driverConnectionId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - level: debug + component: connection + data: + message: "Connection ready" + driverConnectionId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - level: debug + component: connection + data: + message: "Connection checked out" + driverConnectionId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - level: debug + component: connection + data: + message: "Connection checked in" + driverConnectionId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - level: debug + component: connection + data: + message: "Connection closed" + driverConnectionId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + reason: "Connection pool was closed" + + - level: debug + component: connection + data: + message: "Connection pool closed" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + # This test exists to provide coverage of checkout failed and pool cleared events. + - description: "Connection checkout fails due to error establishing connection" + runOnRequirements: + - auth: true + minServerVersion: "4.0" # failCommand was added to mongod in 4.0 + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + retryReads: false + appname: &clientAppName clientAppName + # use a high heartbeatFrequencyMS to avoid a successful monitor check marking the pool as + # ready (and emitting another event) during the course of test execution. + heartbeatFrequencyMS: 10000 + observeLogMessages: + connection: debug + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["saslContinue"] + closeConnection: true + appName: *clientAppName + - name: listDatabases + object: *client + arguments: + filter: {} + expectError: + isClientError: true + + expectLogMessages: + - client: *client + messages: + - level: debug + component: connection + data: + message: "Connection pool created" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - level: debug + component: connection + data: + message: "Connection pool ready" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - level: debug + component: connection + data: + message: "Connection checkout started" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - level: debug + component: connection + data: + message: "Connection created" + driverConnectionId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - level: debug + component: connection + data: + message: "Connection pool cleared" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - level: debug + component: connection + data: + message: "Connection closed" + driverConnectionId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + reason: "An error occurred while using the connection" + error: { $$exists: true } + + - level: debug + component: connection + data: + message: "Connection checkout failed" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + reason: "An error occurred while trying to establish a new connection" + error: { $$exists: true } diff --git a/specifications/connection-monitoring-and-pooling/tests/logging/connection-pool-options.json b/specifications/connection-monitoring-and-pooling/tests/logging/connection-pool-options.json new file mode 100644 index 00000000000..e67804915c3 --- /dev/null +++ b/specifications/connection-monitoring-and-pooling/tests/logging/connection-pool-options.json @@ -0,0 +1,451 @@ +{ + "description": "connection-logging", + "schemaVersion": "1.13", + "runOnRequirements": [ + { + "topologies": [ + "single" + ] + } + ], + "tests": [ + { + "description": "Options should be included in connection pool created message when specified", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "connectionReadyEvent" + ], + "observeLogMessages": { + "connection": "debug" + }, + "uriOptions": { + "minPoolSize": 1, + "maxPoolSize": 5, + "maxIdleTimeMS": 10000 + } + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "connectionReadyEvent": {} + }, + "count": 1 + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection pool created", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "minPoolSize": 1, + "maxPoolSize": 5, + "maxIdleTimeMS": 10000 + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection pool ready", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection created", + "driverConnectionId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection ready", + "driverConnectionId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + }, + { + "description": "maxConnecting should be included in connection pool created message when specified", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "poolReadyEvent" + ], + "observeLogMessages": { + "connection": "debug" + }, + "uriOptions": { + "maxConnecting": 5 + } + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolReadyEvent": {} + }, + "count": 1 + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection pool created", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "maxConnecting": 5 + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection pool ready", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + }, + { + "description": "waitQueueTimeoutMS should be included in connection pool created message when specified", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "poolReadyEvent" + ], + "observeLogMessages": { + "connection": "debug" + }, + "uriOptions": { + "waitQueueTimeoutMS": 10000 + } + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolReadyEvent": {} + }, + "count": 1 + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection pool created", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "waitQueueTimeoutMS": 10000 + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection pool ready", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + }, + { + "description": "waitQueueSize should be included in connection pool created message when specified", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "poolReadyEvent" + ], + "observeLogMessages": { + "connection": "debug" + }, + "uriOptions": { + "waitQueueSize": 100 + } + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolReadyEvent": {} + }, + "count": 1 + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection pool created", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "waitQueueSize": 100 + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection pool ready", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + }, + { + "description": "waitQueueMultiple should be included in connection pool created message when specified", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "poolReadyEvent" + ], + "observeLogMessages": { + "connection": "debug" + }, + "uriOptions": { + "waitQueueSize": 5 + } + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolReadyEvent": {} + }, + "count": 1 + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection pool created", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "waitQueueMultiple": 5 + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection pool ready", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] +} diff --git a/specifications/connection-monitoring-and-pooling/tests/logging/connection-pool-options.yml b/specifications/connection-monitoring-and-pooling/tests/logging/connection-pool-options.yml new file mode 100644 index 00000000000..b22693a92b8 --- /dev/null +++ b/specifications/connection-monitoring-and-pooling/tests/logging/connection-pool-options.yml @@ -0,0 +1,253 @@ +description: "connection-logging" + +schemaVersion: "1.13" + +runOnRequirements: + - topologies: + - single # The number of log messages is different for each topology since there is a connection pool per host. + +tests: + - description: "Options should be included in connection pool created message when specified" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + # Observe and wait on a connection ready event for the connection created in the background. + # This is to avoid raciness around whether the background thread has created the connection + # (and whether corresponding log messages have been generated) by the time log message assertions + # are made. + observeEvents: + - connectionReadyEvent + observeLogMessages: + connection: debug + uriOptions: + minPoolSize: 1 + maxPoolSize: 5 + maxIdleTimeMS: 10000 + + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + connectionReadyEvent: {} + count: 1 + + expectLogMessages: + - client: *client + messages: + - level: debug + component: connection + data: + message: "Connection pool created" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + minPoolSize: 1 + maxPoolSize: 5 + maxIdleTimeMS: 10000 + + - level: debug + component: connection + data: + message: "Connection pool ready" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - level: debug + component: connection + data: + message: "Connection created" + driverConnectionId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - level: debug + component: connection + data: + message: "Connection ready" + driverConnectionId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + # Drivers who have not done DRIVERS-1943 will need to skip this test. + - description: "maxConnecting should be included in connection pool created message when specified" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + # Observe and wait for a poolReadyEvent so we can ensure the pool has been created and is + # ready by the time we assert on log messages, in order to avoid raciness around which messages + # are emitted. + observeEvents: + - poolReadyEvent + observeLogMessages: + connection: debug + uriOptions: + maxConnecting: 5 + + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + poolReadyEvent: {} + count: 1 + + expectLogMessages: + - client: *client + messages: + - level: debug + component: connection + data: + message: "Connection pool created" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + maxConnecting: 5 + + - level: debug + component: connection + data: + message: "Connection pool ready" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + # Drivers that do not support waitQueueTimeoutMS will need to skip this test. + - description: "waitQueueTimeoutMS should be included in connection pool created message when specified" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + # Observe and wait for a poolReadyEvent so we can ensure the pool has been created and is + # ready by the time we assert on log messages, in order to avoid raciness around which messages + # are emitted. + observeEvents: + - poolReadyEvent + observeLogMessages: + connection: debug + uriOptions: + waitQueueTimeoutMS: 10000 + + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + poolReadyEvent: {} + count: 1 + + expectLogMessages: + - client: *client + messages: + - level: debug + component: connection + data: + message: "Connection pool created" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + waitQueueTimeoutMS: 10000 + + - level: debug + component: connection + data: + message: "Connection pool ready" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + # Drivers that do not support waitQueueSize will need to skip this test. + - description: "waitQueueSize should be included in connection pool created message when specified" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + # Observe and wait for a poolReadyEvent so we can ensure the pool has been created and is + # ready by the time we assert on log messages, in order to avoid raciness around which messages + # are emitted. + observeEvents: + - poolReadyEvent + observeLogMessages: + connection: debug + uriOptions: + waitQueueSize: 100 + + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + poolReadyEvent: {} + count: 1 + + expectLogMessages: + - client: *client + messages: + - level: debug + component: connection + data: + message: "Connection pool created" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + waitQueueSize: 100 + + - level: debug + component: connection + data: + message: "Connection pool ready" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + # Drivers that do not support waitQueueMultiple will need to skip this test. + - description: "waitQueueMultiple should be included in connection pool created message when specified" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + # Observe and wait for a poolReadyEvent so we can ensure the pool has been created and is + # ready by the time we assert on log messages, in order to avoid raciness around which messages + # are emitted. + observeEvents: + - poolReadyEvent + observeLogMessages: + connection: debug + uriOptions: + waitQueueSize: 5 + + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + poolReadyEvent: {} + count: 1 + + expectLogMessages: + - client: *client + messages: + - level: debug + component: connection + data: + message: "Connection pool created" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + waitQueueMultiple: 5 + + - level: debug + component: connection + data: + message: "Connection pool ready" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/tests/Makefile b/specifications/connection-string/tests/Makefile similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/tests/Makefile rename to specifications/connection-string/tests/Makefile diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/tests/README.rst b/specifications/connection-string/tests/README.rst similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/tests/README.rst rename to specifications/connection-string/tests/README.rst diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/tests/invalid-uris.json b/specifications/connection-string/tests/invalid-uris.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/tests/invalid-uris.json rename to specifications/connection-string/tests/invalid-uris.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/tests/invalid-uris.yml b/specifications/connection-string/tests/invalid-uris.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/tests/invalid-uris.yml rename to specifications/connection-string/tests/invalid-uris.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/tests/valid-auth.json b/specifications/connection-string/tests/valid-auth.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/tests/valid-auth.json rename to specifications/connection-string/tests/valid-auth.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/tests/valid-auth.yml b/specifications/connection-string/tests/valid-auth.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/tests/valid-auth.yml rename to specifications/connection-string/tests/valid-auth.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/tests/valid-db-with-dotted-name.json b/specifications/connection-string/tests/valid-db-with-dotted-name.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/tests/valid-db-with-dotted-name.json rename to specifications/connection-string/tests/valid-db-with-dotted-name.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/tests/valid-db-with-dotted-name.yml b/specifications/connection-string/tests/valid-db-with-dotted-name.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/tests/valid-db-with-dotted-name.yml rename to specifications/connection-string/tests/valid-db-with-dotted-name.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/tests/valid-host_identifiers.json b/specifications/connection-string/tests/valid-host_identifiers.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/tests/valid-host_identifiers.json rename to specifications/connection-string/tests/valid-host_identifiers.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/tests/valid-host_identifiers.yml b/specifications/connection-string/tests/valid-host_identifiers.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/tests/valid-host_identifiers.yml rename to specifications/connection-string/tests/valid-host_identifiers.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/tests/valid-options.json b/specifications/connection-string/tests/valid-options.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/tests/valid-options.json rename to specifications/connection-string/tests/valid-options.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/tests/valid-options.yml b/specifications/connection-string/tests/valid-options.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/tests/valid-options.yml rename to specifications/connection-string/tests/valid-options.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/tests/valid-unix_socket-absolute.json b/specifications/connection-string/tests/valid-unix_socket-absolute.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/tests/valid-unix_socket-absolute.json rename to specifications/connection-string/tests/valid-unix_socket-absolute.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/tests/valid-unix_socket-absolute.yml b/specifications/connection-string/tests/valid-unix_socket-absolute.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/tests/valid-unix_socket-absolute.yml rename to specifications/connection-string/tests/valid-unix_socket-absolute.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/tests/valid-unix_socket-relative.json b/specifications/connection-string/tests/valid-unix_socket-relative.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/tests/valid-unix_socket-relative.json rename to specifications/connection-string/tests/valid-unix_socket-relative.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/tests/valid-unix_socket-relative.yml b/specifications/connection-string/tests/valid-unix_socket-relative.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/tests/valid-unix_socket-relative.yml rename to specifications/connection-string/tests/valid-unix_socket-relative.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/tests/valid-warnings.json b/specifications/connection-string/tests/valid-warnings.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/tests/valid-warnings.json rename to specifications/connection-string/tests/valid-warnings.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/tests/valid-warnings.yml b/specifications/connection-string/tests/valid-warnings.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/tests/valid-warnings.yml rename to specifications/connection-string/tests/valid-warnings.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/README.rst b/specifications/crud/tests/README.rst similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/README.rst rename to specifications/crud/tests/README.rst diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/aggregate-allowdiskuse.json b/specifications/crud/tests/unified/aggregate-allowdiskuse.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/aggregate-allowdiskuse.json rename to specifications/crud/tests/unified/aggregate-allowdiskuse.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/aggregate-allowdiskuse.yml b/specifications/crud/tests/unified/aggregate-allowdiskuse.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/aggregate-allowdiskuse.yml rename to specifications/crud/tests/unified/aggregate-allowdiskuse.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/aggregate-let.json b/specifications/crud/tests/unified/aggregate-let.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/aggregate-let.json rename to specifications/crud/tests/unified/aggregate-let.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/aggregate-let.yml b/specifications/crud/tests/unified/aggregate-let.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/aggregate-let.yml rename to specifications/crud/tests/unified/aggregate-let.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/aggregate-merge.json b/specifications/crud/tests/unified/aggregate-merge.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/aggregate-merge.json rename to specifications/crud/tests/unified/aggregate-merge.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/aggregate-merge.yml b/specifications/crud/tests/unified/aggregate-merge.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/aggregate-merge.yml rename to specifications/crud/tests/unified/aggregate-merge.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/aggregate-out-readConcern.json b/specifications/crud/tests/unified/aggregate-out-readConcern.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/aggregate-out-readConcern.json rename to specifications/crud/tests/unified/aggregate-out-readConcern.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/aggregate-out-readConcern.yml b/specifications/crud/tests/unified/aggregate-out-readConcern.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/aggregate-out-readConcern.yml rename to specifications/crud/tests/unified/aggregate-out-readConcern.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/aggregate-write-readPreference.json b/specifications/crud/tests/unified/aggregate-write-readPreference.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/aggregate-write-readPreference.json rename to specifications/crud/tests/unified/aggregate-write-readPreference.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/aggregate-write-readPreference.yml b/specifications/crud/tests/unified/aggregate-write-readPreference.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/aggregate-write-readPreference.yml rename to specifications/crud/tests/unified/aggregate-write-readPreference.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/aggregate.json b/specifications/crud/tests/unified/aggregate.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/aggregate.json rename to specifications/crud/tests/unified/aggregate.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/aggregate.yml b/specifications/crud/tests/unified/aggregate.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/aggregate.yml rename to specifications/crud/tests/unified/aggregate.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-arrayFilters-clientError.json b/specifications/crud/tests/unified/bulkWrite-arrayFilters-clientError.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-arrayFilters-clientError.json rename to specifications/crud/tests/unified/bulkWrite-arrayFilters-clientError.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-arrayFilters-clientError.yml b/specifications/crud/tests/unified/bulkWrite-arrayFilters-clientError.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-arrayFilters-clientError.yml rename to specifications/crud/tests/unified/bulkWrite-arrayFilters-clientError.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-arrayFilters.json b/specifications/crud/tests/unified/bulkWrite-arrayFilters.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-arrayFilters.json rename to specifications/crud/tests/unified/bulkWrite-arrayFilters.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-arrayFilters.yml b/specifications/crud/tests/unified/bulkWrite-arrayFilters.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-arrayFilters.yml rename to specifications/crud/tests/unified/bulkWrite-arrayFilters.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-comment.json b/specifications/crud/tests/unified/bulkWrite-comment.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-comment.json rename to specifications/crud/tests/unified/bulkWrite-comment.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-comment.yml b/specifications/crud/tests/unified/bulkWrite-comment.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-comment.yml rename to specifications/crud/tests/unified/bulkWrite-comment.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-delete-hint-clientError.json b/specifications/crud/tests/unified/bulkWrite-delete-hint-clientError.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-delete-hint-clientError.json rename to specifications/crud/tests/unified/bulkWrite-delete-hint-clientError.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-delete-hint-clientError.yml b/specifications/crud/tests/unified/bulkWrite-delete-hint-clientError.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-delete-hint-clientError.yml rename to specifications/crud/tests/unified/bulkWrite-delete-hint-clientError.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-delete-hint-serverError.json b/specifications/crud/tests/unified/bulkWrite-delete-hint-serverError.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-delete-hint-serverError.json rename to specifications/crud/tests/unified/bulkWrite-delete-hint-serverError.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-delete-hint-serverError.yml b/specifications/crud/tests/unified/bulkWrite-delete-hint-serverError.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-delete-hint-serverError.yml rename to specifications/crud/tests/unified/bulkWrite-delete-hint-serverError.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-delete-hint.json b/specifications/crud/tests/unified/bulkWrite-delete-hint.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-delete-hint.json rename to specifications/crud/tests/unified/bulkWrite-delete-hint.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-delete-hint.yml b/specifications/crud/tests/unified/bulkWrite-delete-hint.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-delete-hint.yml rename to specifications/crud/tests/unified/bulkWrite-delete-hint.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-deleteMany-let.json b/specifications/crud/tests/unified/bulkWrite-deleteMany-let.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-deleteMany-let.json rename to specifications/crud/tests/unified/bulkWrite-deleteMany-let.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-deleteMany-let.yml b/specifications/crud/tests/unified/bulkWrite-deleteMany-let.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-deleteMany-let.yml rename to specifications/crud/tests/unified/bulkWrite-deleteMany-let.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-deleteOne-let.json b/specifications/crud/tests/unified/bulkWrite-deleteOne-let.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-deleteOne-let.json rename to specifications/crud/tests/unified/bulkWrite-deleteOne-let.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-deleteOne-let.yml b/specifications/crud/tests/unified/bulkWrite-deleteOne-let.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-deleteOne-let.yml rename to specifications/crud/tests/unified/bulkWrite-deleteOne-let.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-insertOne-dots_and_dollars.json b/specifications/crud/tests/unified/bulkWrite-insertOne-dots_and_dollars.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-insertOne-dots_and_dollars.json rename to specifications/crud/tests/unified/bulkWrite-insertOne-dots_and_dollars.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-insertOne-dots_and_dollars.yml b/specifications/crud/tests/unified/bulkWrite-insertOne-dots_and_dollars.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-insertOne-dots_and_dollars.yml rename to specifications/crud/tests/unified/bulkWrite-insertOne-dots_and_dollars.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-replaceOne-dots_and_dollars.json b/specifications/crud/tests/unified/bulkWrite-replaceOne-dots_and_dollars.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-replaceOne-dots_and_dollars.json rename to specifications/crud/tests/unified/bulkWrite-replaceOne-dots_and_dollars.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-replaceOne-dots_and_dollars.yml b/specifications/crud/tests/unified/bulkWrite-replaceOne-dots_and_dollars.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-replaceOne-dots_and_dollars.yml rename to specifications/crud/tests/unified/bulkWrite-replaceOne-dots_and_dollars.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-replaceOne-let.json b/specifications/crud/tests/unified/bulkWrite-replaceOne-let.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-replaceOne-let.json rename to specifications/crud/tests/unified/bulkWrite-replaceOne-let.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-replaceOne-let.yml b/specifications/crud/tests/unified/bulkWrite-replaceOne-let.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-replaceOne-let.yml rename to specifications/crud/tests/unified/bulkWrite-replaceOne-let.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-update-hint-clientError.json b/specifications/crud/tests/unified/bulkWrite-update-hint-clientError.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-update-hint-clientError.json rename to specifications/crud/tests/unified/bulkWrite-update-hint-clientError.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-update-hint-clientError.yml b/specifications/crud/tests/unified/bulkWrite-update-hint-clientError.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-update-hint-clientError.yml rename to specifications/crud/tests/unified/bulkWrite-update-hint-clientError.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-update-hint-serverError.json b/specifications/crud/tests/unified/bulkWrite-update-hint-serverError.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-update-hint-serverError.json rename to specifications/crud/tests/unified/bulkWrite-update-hint-serverError.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-update-hint-serverError.yml b/specifications/crud/tests/unified/bulkWrite-update-hint-serverError.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-update-hint-serverError.yml rename to specifications/crud/tests/unified/bulkWrite-update-hint-serverError.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-update-hint.json b/specifications/crud/tests/unified/bulkWrite-update-hint.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-update-hint.json rename to specifications/crud/tests/unified/bulkWrite-update-hint.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-update-hint.yml b/specifications/crud/tests/unified/bulkWrite-update-hint.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-update-hint.yml rename to specifications/crud/tests/unified/bulkWrite-update-hint.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-update-validation.json b/specifications/crud/tests/unified/bulkWrite-update-validation.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-update-validation.json rename to specifications/crud/tests/unified/bulkWrite-update-validation.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-update-validation.yml b/specifications/crud/tests/unified/bulkWrite-update-validation.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-update-validation.yml rename to specifications/crud/tests/unified/bulkWrite-update-validation.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-updateMany-dots_and_dollars.json b/specifications/crud/tests/unified/bulkWrite-updateMany-dots_and_dollars.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-updateMany-dots_and_dollars.json rename to specifications/crud/tests/unified/bulkWrite-updateMany-dots_and_dollars.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-updateMany-dots_and_dollars.yml b/specifications/crud/tests/unified/bulkWrite-updateMany-dots_and_dollars.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-updateMany-dots_and_dollars.yml rename to specifications/crud/tests/unified/bulkWrite-updateMany-dots_and_dollars.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-updateMany-let.json b/specifications/crud/tests/unified/bulkWrite-updateMany-let.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-updateMany-let.json rename to specifications/crud/tests/unified/bulkWrite-updateMany-let.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-updateMany-let.yml b/specifications/crud/tests/unified/bulkWrite-updateMany-let.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-updateMany-let.yml rename to specifications/crud/tests/unified/bulkWrite-updateMany-let.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-updateOne-dots_and_dollars.json b/specifications/crud/tests/unified/bulkWrite-updateOne-dots_and_dollars.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-updateOne-dots_and_dollars.json rename to specifications/crud/tests/unified/bulkWrite-updateOne-dots_and_dollars.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-updateOne-dots_and_dollars.yml b/specifications/crud/tests/unified/bulkWrite-updateOne-dots_and_dollars.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-updateOne-dots_and_dollars.yml rename to specifications/crud/tests/unified/bulkWrite-updateOne-dots_and_dollars.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-updateOne-let.json b/specifications/crud/tests/unified/bulkWrite-updateOne-let.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-updateOne-let.json rename to specifications/crud/tests/unified/bulkWrite-updateOne-let.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-updateOne-let.yml b/specifications/crud/tests/unified/bulkWrite-updateOne-let.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/bulkWrite-updateOne-let.yml rename to specifications/crud/tests/unified/bulkWrite-updateOne-let.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/countDocuments-comment.json b/specifications/crud/tests/unified/countDocuments-comment.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/countDocuments-comment.json rename to specifications/crud/tests/unified/countDocuments-comment.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/countDocuments-comment.yml b/specifications/crud/tests/unified/countDocuments-comment.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/countDocuments-comment.yml rename to specifications/crud/tests/unified/countDocuments-comment.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/db-aggregate-write-readPreference.json b/specifications/crud/tests/unified/db-aggregate-write-readPreference.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/db-aggregate-write-readPreference.json rename to specifications/crud/tests/unified/db-aggregate-write-readPreference.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/db-aggregate-write-readPreference.yml b/specifications/crud/tests/unified/db-aggregate-write-readPreference.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/db-aggregate-write-readPreference.yml rename to specifications/crud/tests/unified/db-aggregate-write-readPreference.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/db-aggregate.json b/specifications/crud/tests/unified/db-aggregate.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/db-aggregate.json rename to specifications/crud/tests/unified/db-aggregate.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/db-aggregate.yml b/specifications/crud/tests/unified/db-aggregate.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/db-aggregate.yml rename to specifications/crud/tests/unified/db-aggregate.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteMany-comment.json b/specifications/crud/tests/unified/deleteMany-comment.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteMany-comment.json rename to specifications/crud/tests/unified/deleteMany-comment.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteMany-comment.yml b/specifications/crud/tests/unified/deleteMany-comment.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteMany-comment.yml rename to specifications/crud/tests/unified/deleteMany-comment.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteMany-hint-clientError.json b/specifications/crud/tests/unified/deleteMany-hint-clientError.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteMany-hint-clientError.json rename to specifications/crud/tests/unified/deleteMany-hint-clientError.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteMany-hint-clientError.yml b/specifications/crud/tests/unified/deleteMany-hint-clientError.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteMany-hint-clientError.yml rename to specifications/crud/tests/unified/deleteMany-hint-clientError.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteMany-hint-serverError.json b/specifications/crud/tests/unified/deleteMany-hint-serverError.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteMany-hint-serverError.json rename to specifications/crud/tests/unified/deleteMany-hint-serverError.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteMany-hint-serverError.yml b/specifications/crud/tests/unified/deleteMany-hint-serverError.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteMany-hint-serverError.yml rename to specifications/crud/tests/unified/deleteMany-hint-serverError.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteMany-hint.json b/specifications/crud/tests/unified/deleteMany-hint.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteMany-hint.json rename to specifications/crud/tests/unified/deleteMany-hint.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteMany-hint.yml b/specifications/crud/tests/unified/deleteMany-hint.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteMany-hint.yml rename to specifications/crud/tests/unified/deleteMany-hint.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteMany-let.json b/specifications/crud/tests/unified/deleteMany-let.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteMany-let.json rename to specifications/crud/tests/unified/deleteMany-let.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteMany-let.yml b/specifications/crud/tests/unified/deleteMany-let.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteMany-let.yml rename to specifications/crud/tests/unified/deleteMany-let.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteOne-comment.json b/specifications/crud/tests/unified/deleteOne-comment.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteOne-comment.json rename to specifications/crud/tests/unified/deleteOne-comment.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteOne-comment.yml b/specifications/crud/tests/unified/deleteOne-comment.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteOne-comment.yml rename to specifications/crud/tests/unified/deleteOne-comment.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteOne-hint-clientError.json b/specifications/crud/tests/unified/deleteOne-hint-clientError.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteOne-hint-clientError.json rename to specifications/crud/tests/unified/deleteOne-hint-clientError.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteOne-hint-clientError.yml b/specifications/crud/tests/unified/deleteOne-hint-clientError.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteOne-hint-clientError.yml rename to specifications/crud/tests/unified/deleteOne-hint-clientError.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteOne-hint-serverError.json b/specifications/crud/tests/unified/deleteOne-hint-serverError.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteOne-hint-serverError.json rename to specifications/crud/tests/unified/deleteOne-hint-serverError.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteOne-hint-serverError.yml b/specifications/crud/tests/unified/deleteOne-hint-serverError.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteOne-hint-serverError.yml rename to specifications/crud/tests/unified/deleteOne-hint-serverError.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteOne-hint.json b/specifications/crud/tests/unified/deleteOne-hint.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteOne-hint.json rename to specifications/crud/tests/unified/deleteOne-hint.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteOne-hint.yml b/specifications/crud/tests/unified/deleteOne-hint.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteOne-hint.yml rename to specifications/crud/tests/unified/deleteOne-hint.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteOne-let.json b/specifications/crud/tests/unified/deleteOne-let.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteOne-let.json rename to specifications/crud/tests/unified/deleteOne-let.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteOne-let.yml b/specifications/crud/tests/unified/deleteOne-let.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/deleteOne-let.yml rename to specifications/crud/tests/unified/deleteOne-let.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/distinct-comment.json b/specifications/crud/tests/unified/distinct-comment.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/distinct-comment.json rename to specifications/crud/tests/unified/distinct-comment.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/distinct-comment.yml b/specifications/crud/tests/unified/distinct-comment.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/distinct-comment.yml rename to specifications/crud/tests/unified/distinct-comment.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/estimatedDocumentCount-comment.json b/specifications/crud/tests/unified/estimatedDocumentCount-comment.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/estimatedDocumentCount-comment.json rename to specifications/crud/tests/unified/estimatedDocumentCount-comment.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/estimatedDocumentCount-comment.yml b/specifications/crud/tests/unified/estimatedDocumentCount-comment.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/estimatedDocumentCount-comment.yml rename to specifications/crud/tests/unified/estimatedDocumentCount-comment.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/estimatedDocumentCount.json b/specifications/crud/tests/unified/estimatedDocumentCount.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/estimatedDocumentCount.json rename to specifications/crud/tests/unified/estimatedDocumentCount.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/estimatedDocumentCount.yml b/specifications/crud/tests/unified/estimatedDocumentCount.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/estimatedDocumentCount.yml rename to specifications/crud/tests/unified/estimatedDocumentCount.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/find-allowdiskuse-clientError.json b/specifications/crud/tests/unified/find-allowdiskuse-clientError.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/find-allowdiskuse-clientError.json rename to specifications/crud/tests/unified/find-allowdiskuse-clientError.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/find-allowdiskuse-clientError.yml b/specifications/crud/tests/unified/find-allowdiskuse-clientError.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/find-allowdiskuse-clientError.yml rename to specifications/crud/tests/unified/find-allowdiskuse-clientError.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/find-allowdiskuse-serverError.json b/specifications/crud/tests/unified/find-allowdiskuse-serverError.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/find-allowdiskuse-serverError.json rename to specifications/crud/tests/unified/find-allowdiskuse-serverError.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/find-allowdiskuse-serverError.yml b/specifications/crud/tests/unified/find-allowdiskuse-serverError.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/find-allowdiskuse-serverError.yml rename to specifications/crud/tests/unified/find-allowdiskuse-serverError.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/find-allowdiskuse.json b/specifications/crud/tests/unified/find-allowdiskuse.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/find-allowdiskuse.json rename to specifications/crud/tests/unified/find-allowdiskuse.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/find-allowdiskuse.yml b/specifications/crud/tests/unified/find-allowdiskuse.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/find-allowdiskuse.yml rename to specifications/crud/tests/unified/find-allowdiskuse.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/find-comment.json b/specifications/crud/tests/unified/find-comment.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/find-comment.json rename to specifications/crud/tests/unified/find-comment.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/find-comment.yml b/specifications/crud/tests/unified/find-comment.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/find-comment.yml rename to specifications/crud/tests/unified/find-comment.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/find-let.json b/specifications/crud/tests/unified/find-let.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/find-let.json rename to specifications/crud/tests/unified/find-let.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/find-let.yml b/specifications/crud/tests/unified/find-let.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/find-let.yml rename to specifications/crud/tests/unified/find-let.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/find.json b/specifications/crud/tests/unified/find.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/find.json rename to specifications/crud/tests/unified/find.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/find.yml b/specifications/crud/tests/unified/find.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/find.yml rename to specifications/crud/tests/unified/find.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndDelete-comment.json b/specifications/crud/tests/unified/findOneAndDelete-comment.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndDelete-comment.json rename to specifications/crud/tests/unified/findOneAndDelete-comment.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndDelete-comment.yml b/specifications/crud/tests/unified/findOneAndDelete-comment.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndDelete-comment.yml rename to specifications/crud/tests/unified/findOneAndDelete-comment.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndDelete-hint-clientError.json b/specifications/crud/tests/unified/findOneAndDelete-hint-clientError.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndDelete-hint-clientError.json rename to specifications/crud/tests/unified/findOneAndDelete-hint-clientError.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndDelete-hint-clientError.yml b/specifications/crud/tests/unified/findOneAndDelete-hint-clientError.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndDelete-hint-clientError.yml rename to specifications/crud/tests/unified/findOneAndDelete-hint-clientError.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndDelete-hint-serverError.json b/specifications/crud/tests/unified/findOneAndDelete-hint-serverError.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndDelete-hint-serverError.json rename to specifications/crud/tests/unified/findOneAndDelete-hint-serverError.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndDelete-hint-serverError.yml b/specifications/crud/tests/unified/findOneAndDelete-hint-serverError.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndDelete-hint-serverError.yml rename to specifications/crud/tests/unified/findOneAndDelete-hint-serverError.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndDelete-hint.json b/specifications/crud/tests/unified/findOneAndDelete-hint.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndDelete-hint.json rename to specifications/crud/tests/unified/findOneAndDelete-hint.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndDelete-hint.yml b/specifications/crud/tests/unified/findOneAndDelete-hint.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndDelete-hint.yml rename to specifications/crud/tests/unified/findOneAndDelete-hint.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndDelete-let.json b/specifications/crud/tests/unified/findOneAndDelete-let.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndDelete-let.json rename to specifications/crud/tests/unified/findOneAndDelete-let.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndDelete-let.yml b/specifications/crud/tests/unified/findOneAndDelete-let.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndDelete-let.yml rename to specifications/crud/tests/unified/findOneAndDelete-let.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndReplace-comment.json b/specifications/crud/tests/unified/findOneAndReplace-comment.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndReplace-comment.json rename to specifications/crud/tests/unified/findOneAndReplace-comment.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndReplace-comment.yml b/specifications/crud/tests/unified/findOneAndReplace-comment.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndReplace-comment.yml rename to specifications/crud/tests/unified/findOneAndReplace-comment.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndReplace-dots_and_dollars.json b/specifications/crud/tests/unified/findOneAndReplace-dots_and_dollars.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndReplace-dots_and_dollars.json rename to specifications/crud/tests/unified/findOneAndReplace-dots_and_dollars.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndReplace-dots_and_dollars.yml b/specifications/crud/tests/unified/findOneAndReplace-dots_and_dollars.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndReplace-dots_and_dollars.yml rename to specifications/crud/tests/unified/findOneAndReplace-dots_and_dollars.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndReplace-hint-clientError.json b/specifications/crud/tests/unified/findOneAndReplace-hint-clientError.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndReplace-hint-clientError.json rename to specifications/crud/tests/unified/findOneAndReplace-hint-clientError.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndReplace-hint-clientError.yml b/specifications/crud/tests/unified/findOneAndReplace-hint-clientError.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndReplace-hint-clientError.yml rename to specifications/crud/tests/unified/findOneAndReplace-hint-clientError.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndReplace-hint-serverError.json b/specifications/crud/tests/unified/findOneAndReplace-hint-serverError.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndReplace-hint-serverError.json rename to specifications/crud/tests/unified/findOneAndReplace-hint-serverError.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndReplace-hint-serverError.yml b/specifications/crud/tests/unified/findOneAndReplace-hint-serverError.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndReplace-hint-serverError.yml rename to specifications/crud/tests/unified/findOneAndReplace-hint-serverError.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndReplace-hint.json b/specifications/crud/tests/unified/findOneAndReplace-hint.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndReplace-hint.json rename to specifications/crud/tests/unified/findOneAndReplace-hint.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndReplace-hint.yml b/specifications/crud/tests/unified/findOneAndReplace-hint.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndReplace-hint.yml rename to specifications/crud/tests/unified/findOneAndReplace-hint.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndReplace-let.json b/specifications/crud/tests/unified/findOneAndReplace-let.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndReplace-let.json rename to specifications/crud/tests/unified/findOneAndReplace-let.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndReplace-let.yml b/specifications/crud/tests/unified/findOneAndReplace-let.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndReplace-let.yml rename to specifications/crud/tests/unified/findOneAndReplace-let.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndUpdate-comment.json b/specifications/crud/tests/unified/findOneAndUpdate-comment.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndUpdate-comment.json rename to specifications/crud/tests/unified/findOneAndUpdate-comment.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndUpdate-comment.yml b/specifications/crud/tests/unified/findOneAndUpdate-comment.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndUpdate-comment.yml rename to specifications/crud/tests/unified/findOneAndUpdate-comment.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndUpdate-dots_and_dollars.json b/specifications/crud/tests/unified/findOneAndUpdate-dots_and_dollars.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndUpdate-dots_and_dollars.json rename to specifications/crud/tests/unified/findOneAndUpdate-dots_and_dollars.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndUpdate-dots_and_dollars.yml b/specifications/crud/tests/unified/findOneAndUpdate-dots_and_dollars.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndUpdate-dots_and_dollars.yml rename to specifications/crud/tests/unified/findOneAndUpdate-dots_and_dollars.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndUpdate-hint-clientError.json b/specifications/crud/tests/unified/findOneAndUpdate-hint-clientError.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndUpdate-hint-clientError.json rename to specifications/crud/tests/unified/findOneAndUpdate-hint-clientError.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndUpdate-hint-clientError.yml b/specifications/crud/tests/unified/findOneAndUpdate-hint-clientError.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndUpdate-hint-clientError.yml rename to specifications/crud/tests/unified/findOneAndUpdate-hint-clientError.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndUpdate-hint-serverError.json b/specifications/crud/tests/unified/findOneAndUpdate-hint-serverError.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndUpdate-hint-serverError.json rename to specifications/crud/tests/unified/findOneAndUpdate-hint-serverError.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndUpdate-hint-serverError.yml b/specifications/crud/tests/unified/findOneAndUpdate-hint-serverError.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndUpdate-hint-serverError.yml rename to specifications/crud/tests/unified/findOneAndUpdate-hint-serverError.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndUpdate-hint.json b/specifications/crud/tests/unified/findOneAndUpdate-hint.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndUpdate-hint.json rename to specifications/crud/tests/unified/findOneAndUpdate-hint.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndUpdate-hint.yml b/specifications/crud/tests/unified/findOneAndUpdate-hint.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndUpdate-hint.yml rename to specifications/crud/tests/unified/findOneAndUpdate-hint.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndUpdate-let.json b/specifications/crud/tests/unified/findOneAndUpdate-let.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndUpdate-let.json rename to specifications/crud/tests/unified/findOneAndUpdate-let.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndUpdate-let.yml b/specifications/crud/tests/unified/findOneAndUpdate-let.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/findOneAndUpdate-let.yml rename to specifications/crud/tests/unified/findOneAndUpdate-let.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/insertMany-comment.json b/specifications/crud/tests/unified/insertMany-comment.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/insertMany-comment.json rename to specifications/crud/tests/unified/insertMany-comment.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/insertMany-comment.yml b/specifications/crud/tests/unified/insertMany-comment.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/insertMany-comment.yml rename to specifications/crud/tests/unified/insertMany-comment.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/insertMany-dots_and_dollars.json b/specifications/crud/tests/unified/insertMany-dots_and_dollars.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/insertMany-dots_and_dollars.json rename to specifications/crud/tests/unified/insertMany-dots_and_dollars.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/insertMany-dots_and_dollars.yml b/specifications/crud/tests/unified/insertMany-dots_and_dollars.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/insertMany-dots_and_dollars.yml rename to specifications/crud/tests/unified/insertMany-dots_and_dollars.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/insertOne-comment.json b/specifications/crud/tests/unified/insertOne-comment.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/insertOne-comment.json rename to specifications/crud/tests/unified/insertOne-comment.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/insertOne-comment.yml b/specifications/crud/tests/unified/insertOne-comment.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/insertOne-comment.yml rename to specifications/crud/tests/unified/insertOne-comment.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/insertOne-dots_and_dollars.json b/specifications/crud/tests/unified/insertOne-dots_and_dollars.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/insertOne-dots_and_dollars.json rename to specifications/crud/tests/unified/insertOne-dots_and_dollars.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/insertOne-dots_and_dollars.yml b/specifications/crud/tests/unified/insertOne-dots_and_dollars.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/insertOne-dots_and_dollars.yml rename to specifications/crud/tests/unified/insertOne-dots_and_dollars.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/replaceOne-comment.json b/specifications/crud/tests/unified/replaceOne-comment.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/replaceOne-comment.json rename to specifications/crud/tests/unified/replaceOne-comment.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/replaceOne-comment.yml b/specifications/crud/tests/unified/replaceOne-comment.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/replaceOne-comment.yml rename to specifications/crud/tests/unified/replaceOne-comment.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/replaceOne-dots_and_dollars.json b/specifications/crud/tests/unified/replaceOne-dots_and_dollars.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/replaceOne-dots_and_dollars.json rename to specifications/crud/tests/unified/replaceOne-dots_and_dollars.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/replaceOne-dots_and_dollars.yml b/specifications/crud/tests/unified/replaceOne-dots_and_dollars.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/replaceOne-dots_and_dollars.yml rename to specifications/crud/tests/unified/replaceOne-dots_and_dollars.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/replaceOne-hint.json b/specifications/crud/tests/unified/replaceOne-hint.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/replaceOne-hint.json rename to specifications/crud/tests/unified/replaceOne-hint.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/replaceOne-hint.yml b/specifications/crud/tests/unified/replaceOne-hint.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/replaceOne-hint.yml rename to specifications/crud/tests/unified/replaceOne-hint.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/replaceOne-let.json b/specifications/crud/tests/unified/replaceOne-let.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/replaceOne-let.json rename to specifications/crud/tests/unified/replaceOne-let.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/replaceOne-let.yml b/specifications/crud/tests/unified/replaceOne-let.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/replaceOne-let.yml rename to specifications/crud/tests/unified/replaceOne-let.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/replaceOne-validation.json b/specifications/crud/tests/unified/replaceOne-validation.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/replaceOne-validation.json rename to specifications/crud/tests/unified/replaceOne-validation.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/replaceOne-validation.yml b/specifications/crud/tests/unified/replaceOne-validation.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/replaceOne-validation.yml rename to specifications/crud/tests/unified/replaceOne-validation.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateMany-comment.json b/specifications/crud/tests/unified/updateMany-comment.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateMany-comment.json rename to specifications/crud/tests/unified/updateMany-comment.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateMany-comment.yml b/specifications/crud/tests/unified/updateMany-comment.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateMany-comment.yml rename to specifications/crud/tests/unified/updateMany-comment.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateMany-dots_and_dollars.json b/specifications/crud/tests/unified/updateMany-dots_and_dollars.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateMany-dots_and_dollars.json rename to specifications/crud/tests/unified/updateMany-dots_and_dollars.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateMany-dots_and_dollars.yml b/specifications/crud/tests/unified/updateMany-dots_and_dollars.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateMany-dots_and_dollars.yml rename to specifications/crud/tests/unified/updateMany-dots_and_dollars.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateMany-hint-clientError.json b/specifications/crud/tests/unified/updateMany-hint-clientError.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateMany-hint-clientError.json rename to specifications/crud/tests/unified/updateMany-hint-clientError.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateMany-hint-clientError.yml b/specifications/crud/tests/unified/updateMany-hint-clientError.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateMany-hint-clientError.yml rename to specifications/crud/tests/unified/updateMany-hint-clientError.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateMany-hint-serverError.json b/specifications/crud/tests/unified/updateMany-hint-serverError.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateMany-hint-serverError.json rename to specifications/crud/tests/unified/updateMany-hint-serverError.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateMany-hint-serverError.yml b/specifications/crud/tests/unified/updateMany-hint-serverError.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateMany-hint-serverError.yml rename to specifications/crud/tests/unified/updateMany-hint-serverError.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateMany-hint.json b/specifications/crud/tests/unified/updateMany-hint.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateMany-hint.json rename to specifications/crud/tests/unified/updateMany-hint.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateMany-hint.yml b/specifications/crud/tests/unified/updateMany-hint.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateMany-hint.yml rename to specifications/crud/tests/unified/updateMany-hint.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateMany-let.json b/specifications/crud/tests/unified/updateMany-let.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateMany-let.json rename to specifications/crud/tests/unified/updateMany-let.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateMany-let.yml b/specifications/crud/tests/unified/updateMany-let.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateMany-let.yml rename to specifications/crud/tests/unified/updateMany-let.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateMany-validation.json b/specifications/crud/tests/unified/updateMany-validation.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateMany-validation.json rename to specifications/crud/tests/unified/updateMany-validation.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateMany-validation.yml b/specifications/crud/tests/unified/updateMany-validation.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateMany-validation.yml rename to specifications/crud/tests/unified/updateMany-validation.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateOne-comment.json b/specifications/crud/tests/unified/updateOne-comment.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateOne-comment.json rename to specifications/crud/tests/unified/updateOne-comment.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateOne-comment.yml b/specifications/crud/tests/unified/updateOne-comment.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateOne-comment.yml rename to specifications/crud/tests/unified/updateOne-comment.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateOne-dots_and_dollars.json b/specifications/crud/tests/unified/updateOne-dots_and_dollars.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateOne-dots_and_dollars.json rename to specifications/crud/tests/unified/updateOne-dots_and_dollars.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateOne-dots_and_dollars.yml b/specifications/crud/tests/unified/updateOne-dots_and_dollars.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateOne-dots_and_dollars.yml rename to specifications/crud/tests/unified/updateOne-dots_and_dollars.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateOne-hint-clientError.json b/specifications/crud/tests/unified/updateOne-hint-clientError.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateOne-hint-clientError.json rename to specifications/crud/tests/unified/updateOne-hint-clientError.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateOne-hint-clientError.yml b/specifications/crud/tests/unified/updateOne-hint-clientError.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateOne-hint-clientError.yml rename to specifications/crud/tests/unified/updateOne-hint-clientError.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateOne-hint-serverError.json b/specifications/crud/tests/unified/updateOne-hint-serverError.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateOne-hint-serverError.json rename to specifications/crud/tests/unified/updateOne-hint-serverError.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateOne-hint-serverError.yml b/specifications/crud/tests/unified/updateOne-hint-serverError.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateOne-hint-serverError.yml rename to specifications/crud/tests/unified/updateOne-hint-serverError.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateOne-hint.json b/specifications/crud/tests/unified/updateOne-hint.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateOne-hint.json rename to specifications/crud/tests/unified/updateOne-hint.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateOne-hint.yml b/specifications/crud/tests/unified/updateOne-hint.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateOne-hint.yml rename to specifications/crud/tests/unified/updateOne-hint.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateOne-let.json b/specifications/crud/tests/unified/updateOne-let.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateOne-let.json rename to specifications/crud/tests/unified/updateOne-let.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateOne-let.yml b/specifications/crud/tests/unified/updateOne-let.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateOne-let.yml rename to specifications/crud/tests/unified/updateOne-let.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateOne-validation.json b/specifications/crud/tests/unified/updateOne-validation.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateOne-validation.json rename to specifications/crud/tests/unified/updateOne-validation.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateOne-validation.yml b/specifications/crud/tests/unified/updateOne-validation.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateOne-validation.yml rename to specifications/crud/tests/unified/updateOne-validation.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateWithPipelines.json b/specifications/crud/tests/unified/updateWithPipelines.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateWithPipelines.json rename to specifications/crud/tests/unified/updateWithPipelines.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateWithPipelines.yml b/specifications/crud/tests/unified/updateWithPipelines.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/unified/updateWithPipelines.yml rename to specifications/crud/tests/unified/updateWithPipelines.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/aggregate-collation.json b/specifications/crud/tests/v1/read/aggregate-collation.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/aggregate-collation.json rename to specifications/crud/tests/v1/read/aggregate-collation.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/aggregate-collation.yml b/specifications/crud/tests/v1/read/aggregate-collation.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/aggregate-collation.yml rename to specifications/crud/tests/v1/read/aggregate-collation.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/aggregate-out.json b/specifications/crud/tests/v1/read/aggregate-out.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/aggregate-out.json rename to specifications/crud/tests/v1/read/aggregate-out.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/aggregate-out.yml b/specifications/crud/tests/v1/read/aggregate-out.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/aggregate-out.yml rename to specifications/crud/tests/v1/read/aggregate-out.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/aggregate.json b/specifications/crud/tests/v1/read/aggregate.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/aggregate.json rename to specifications/crud/tests/v1/read/aggregate.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/aggregate.yml b/specifications/crud/tests/v1/read/aggregate.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/aggregate.yml rename to specifications/crud/tests/v1/read/aggregate.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/count-collation.json b/specifications/crud/tests/v1/read/count-collation.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/count-collation.json rename to specifications/crud/tests/v1/read/count-collation.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/count-collation.yml b/specifications/crud/tests/v1/read/count-collation.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/count-collation.yml rename to specifications/crud/tests/v1/read/count-collation.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/count-empty.json b/specifications/crud/tests/v1/read/count-empty.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/count-empty.json rename to specifications/crud/tests/v1/read/count-empty.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/count-empty.yml b/specifications/crud/tests/v1/read/count-empty.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/count-empty.yml rename to specifications/crud/tests/v1/read/count-empty.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/count.json b/specifications/crud/tests/v1/read/count.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/count.json rename to specifications/crud/tests/v1/read/count.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/count.yml b/specifications/crud/tests/v1/read/count.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/count.yml rename to specifications/crud/tests/v1/read/count.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/distinct-collation.json b/specifications/crud/tests/v1/read/distinct-collation.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/distinct-collation.json rename to specifications/crud/tests/v1/read/distinct-collation.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/distinct-collation.yml b/specifications/crud/tests/v1/read/distinct-collation.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/distinct-collation.yml rename to specifications/crud/tests/v1/read/distinct-collation.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/distinct.json b/specifications/crud/tests/v1/read/distinct.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/distinct.json rename to specifications/crud/tests/v1/read/distinct.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/distinct.yml b/specifications/crud/tests/v1/read/distinct.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/distinct.yml rename to specifications/crud/tests/v1/read/distinct.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/find-collation.json b/specifications/crud/tests/v1/read/find-collation.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/find-collation.json rename to specifications/crud/tests/v1/read/find-collation.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/find-collation.yml b/specifications/crud/tests/v1/read/find-collation.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/find-collation.yml rename to specifications/crud/tests/v1/read/find-collation.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/find.json b/specifications/crud/tests/v1/read/find.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/find.json rename to specifications/crud/tests/v1/read/find.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/find.yml b/specifications/crud/tests/v1/read/find.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/read/find.yml rename to specifications/crud/tests/v1/read/find.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/bulkWrite-arrayFilters.json b/specifications/crud/tests/v1/write/bulkWrite-arrayFilters.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/bulkWrite-arrayFilters.json rename to specifications/crud/tests/v1/write/bulkWrite-arrayFilters.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/bulkWrite-arrayFilters.yml b/specifications/crud/tests/v1/write/bulkWrite-arrayFilters.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/bulkWrite-arrayFilters.yml rename to specifications/crud/tests/v1/write/bulkWrite-arrayFilters.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/bulkWrite-collation.json b/specifications/crud/tests/v1/write/bulkWrite-collation.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/bulkWrite-collation.json rename to specifications/crud/tests/v1/write/bulkWrite-collation.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/bulkWrite-collation.yml b/specifications/crud/tests/v1/write/bulkWrite-collation.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/bulkWrite-collation.yml rename to specifications/crud/tests/v1/write/bulkWrite-collation.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/bulkWrite.json b/specifications/crud/tests/v1/write/bulkWrite.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/bulkWrite.json rename to specifications/crud/tests/v1/write/bulkWrite.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/bulkWrite.yml b/specifications/crud/tests/v1/write/bulkWrite.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/bulkWrite.yml rename to specifications/crud/tests/v1/write/bulkWrite.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/deleteMany-collation.json b/specifications/crud/tests/v1/write/deleteMany-collation.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/deleteMany-collation.json rename to specifications/crud/tests/v1/write/deleteMany-collation.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/deleteMany-collation.yml b/specifications/crud/tests/v1/write/deleteMany-collation.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/deleteMany-collation.yml rename to specifications/crud/tests/v1/write/deleteMany-collation.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/deleteMany.json b/specifications/crud/tests/v1/write/deleteMany.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/deleteMany.json rename to specifications/crud/tests/v1/write/deleteMany.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/deleteMany.yml b/specifications/crud/tests/v1/write/deleteMany.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/deleteMany.yml rename to specifications/crud/tests/v1/write/deleteMany.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/deleteOne-collation.json b/specifications/crud/tests/v1/write/deleteOne-collation.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/deleteOne-collation.json rename to specifications/crud/tests/v1/write/deleteOne-collation.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/deleteOne-collation.yml b/specifications/crud/tests/v1/write/deleteOne-collation.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/deleteOne-collation.yml rename to specifications/crud/tests/v1/write/deleteOne-collation.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/deleteOne.json b/specifications/crud/tests/v1/write/deleteOne.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/deleteOne.json rename to specifications/crud/tests/v1/write/deleteOne.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/deleteOne.yml b/specifications/crud/tests/v1/write/deleteOne.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/deleteOne.yml rename to specifications/crud/tests/v1/write/deleteOne.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/findOneAndDelete-collation.json b/specifications/crud/tests/v1/write/findOneAndDelete-collation.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/findOneAndDelete-collation.json rename to specifications/crud/tests/v1/write/findOneAndDelete-collation.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/findOneAndDelete-collation.yml b/specifications/crud/tests/v1/write/findOneAndDelete-collation.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/findOneAndDelete-collation.yml rename to specifications/crud/tests/v1/write/findOneAndDelete-collation.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/findOneAndDelete.json b/specifications/crud/tests/v1/write/findOneAndDelete.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/findOneAndDelete.json rename to specifications/crud/tests/v1/write/findOneAndDelete.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/findOneAndDelete.yml b/specifications/crud/tests/v1/write/findOneAndDelete.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/findOneAndDelete.yml rename to specifications/crud/tests/v1/write/findOneAndDelete.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/findOneAndReplace-collation.json b/specifications/crud/tests/v1/write/findOneAndReplace-collation.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/findOneAndReplace-collation.json rename to specifications/crud/tests/v1/write/findOneAndReplace-collation.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/findOneAndReplace-collation.yml b/specifications/crud/tests/v1/write/findOneAndReplace-collation.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/findOneAndReplace-collation.yml rename to specifications/crud/tests/v1/write/findOneAndReplace-collation.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/findOneAndReplace-upsert.json b/specifications/crud/tests/v1/write/findOneAndReplace-upsert.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/findOneAndReplace-upsert.json rename to specifications/crud/tests/v1/write/findOneAndReplace-upsert.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/findOneAndReplace-upsert.yml b/specifications/crud/tests/v1/write/findOneAndReplace-upsert.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/findOneAndReplace-upsert.yml rename to specifications/crud/tests/v1/write/findOneAndReplace-upsert.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/findOneAndReplace.json b/specifications/crud/tests/v1/write/findOneAndReplace.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/findOneAndReplace.json rename to specifications/crud/tests/v1/write/findOneAndReplace.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/findOneAndReplace.yml b/specifications/crud/tests/v1/write/findOneAndReplace.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/findOneAndReplace.yml rename to specifications/crud/tests/v1/write/findOneAndReplace.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/findOneAndUpdate-arrayFilters.json b/specifications/crud/tests/v1/write/findOneAndUpdate-arrayFilters.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/findOneAndUpdate-arrayFilters.json rename to specifications/crud/tests/v1/write/findOneAndUpdate-arrayFilters.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/findOneAndUpdate-arrayFilters.yml b/specifications/crud/tests/v1/write/findOneAndUpdate-arrayFilters.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/findOneAndUpdate-arrayFilters.yml rename to specifications/crud/tests/v1/write/findOneAndUpdate-arrayFilters.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/findOneAndUpdate-collation.json b/specifications/crud/tests/v1/write/findOneAndUpdate-collation.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/findOneAndUpdate-collation.json rename to specifications/crud/tests/v1/write/findOneAndUpdate-collation.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/findOneAndUpdate-collation.yml b/specifications/crud/tests/v1/write/findOneAndUpdate-collation.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/findOneAndUpdate-collation.yml rename to specifications/crud/tests/v1/write/findOneAndUpdate-collation.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/findOneAndUpdate.json b/specifications/crud/tests/v1/write/findOneAndUpdate.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/findOneAndUpdate.json rename to specifications/crud/tests/v1/write/findOneAndUpdate.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/findOneAndUpdate.yml b/specifications/crud/tests/v1/write/findOneAndUpdate.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/findOneAndUpdate.yml rename to specifications/crud/tests/v1/write/findOneAndUpdate.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/insertMany.json b/specifications/crud/tests/v1/write/insertMany.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/insertMany.json rename to specifications/crud/tests/v1/write/insertMany.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/insertMany.yml b/specifications/crud/tests/v1/write/insertMany.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/insertMany.yml rename to specifications/crud/tests/v1/write/insertMany.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/insertOne.json b/specifications/crud/tests/v1/write/insertOne.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/insertOne.json rename to specifications/crud/tests/v1/write/insertOne.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/insertOne.yml b/specifications/crud/tests/v1/write/insertOne.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/insertOne.yml rename to specifications/crud/tests/v1/write/insertOne.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/replaceOne-collation.json b/specifications/crud/tests/v1/write/replaceOne-collation.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/replaceOne-collation.json rename to specifications/crud/tests/v1/write/replaceOne-collation.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/replaceOne-collation.yml b/specifications/crud/tests/v1/write/replaceOne-collation.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/replaceOne-collation.yml rename to specifications/crud/tests/v1/write/replaceOne-collation.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/replaceOne.json b/specifications/crud/tests/v1/write/replaceOne.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/replaceOne.json rename to specifications/crud/tests/v1/write/replaceOne.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/replaceOne.yml b/specifications/crud/tests/v1/write/replaceOne.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/replaceOne.yml rename to specifications/crud/tests/v1/write/replaceOne.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/updateMany-arrayFilters.json b/specifications/crud/tests/v1/write/updateMany-arrayFilters.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/updateMany-arrayFilters.json rename to specifications/crud/tests/v1/write/updateMany-arrayFilters.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/updateMany-arrayFilters.yml b/specifications/crud/tests/v1/write/updateMany-arrayFilters.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/updateMany-arrayFilters.yml rename to specifications/crud/tests/v1/write/updateMany-arrayFilters.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/updateMany-collation.json b/specifications/crud/tests/v1/write/updateMany-collation.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/updateMany-collation.json rename to specifications/crud/tests/v1/write/updateMany-collation.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/updateMany-collation.yml b/specifications/crud/tests/v1/write/updateMany-collation.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/updateMany-collation.yml rename to specifications/crud/tests/v1/write/updateMany-collation.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/updateMany.json b/specifications/crud/tests/v1/write/updateMany.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/updateMany.json rename to specifications/crud/tests/v1/write/updateMany.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/updateMany.yml b/specifications/crud/tests/v1/write/updateMany.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/updateMany.yml rename to specifications/crud/tests/v1/write/updateMany.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/updateOne-arrayFilters.json b/specifications/crud/tests/v1/write/updateOne-arrayFilters.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/updateOne-arrayFilters.json rename to specifications/crud/tests/v1/write/updateOne-arrayFilters.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/updateOne-arrayFilters.yml b/specifications/crud/tests/v1/write/updateOne-arrayFilters.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/updateOne-arrayFilters.yml rename to specifications/crud/tests/v1/write/updateOne-arrayFilters.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/updateOne-collation.json b/specifications/crud/tests/v1/write/updateOne-collation.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/updateOne-collation.json rename to specifications/crud/tests/v1/write/updateOne-collation.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/updateOne-collation.yml b/specifications/crud/tests/v1/write/updateOne-collation.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/updateOne-collation.yml rename to specifications/crud/tests/v1/write/updateOne-collation.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/updateOne.json b/specifications/crud/tests/v1/write/updateOne.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/updateOne.json rename to specifications/crud/tests/v1/write/updateOne.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/updateOne.yml b/specifications/crud/tests/v1/write/updateOne.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/crud/tests/v1/write/updateOne.yml rename to specifications/crud/tests/v1/write/updateOne.yml diff --git a/tests/MongoDB.Driver.GridFS.Tests/Specifications/gridfs/tests/delete.json b/specifications/gridfs/tests/delete.json similarity index 100% rename from tests/MongoDB.Driver.GridFS.Tests/Specifications/gridfs/tests/delete.json rename to specifications/gridfs/tests/delete.json diff --git a/tests/MongoDB.Driver.GridFS.Tests/Specifications/gridfs/tests/delete.yml b/specifications/gridfs/tests/delete.yml similarity index 100% rename from tests/MongoDB.Driver.GridFS.Tests/Specifications/gridfs/tests/delete.yml rename to specifications/gridfs/tests/delete.yml diff --git a/tests/MongoDB.Driver.GridFS.Tests/Specifications/gridfs/tests/download.json b/specifications/gridfs/tests/download.json similarity index 100% rename from tests/MongoDB.Driver.GridFS.Tests/Specifications/gridfs/tests/download.json rename to specifications/gridfs/tests/download.json diff --git a/tests/MongoDB.Driver.GridFS.Tests/Specifications/gridfs/tests/download.yml b/specifications/gridfs/tests/download.yml similarity index 100% rename from tests/MongoDB.Driver.GridFS.Tests/Specifications/gridfs/tests/download.yml rename to specifications/gridfs/tests/download.yml diff --git a/tests/MongoDB.Driver.GridFS.Tests/Specifications/gridfs/tests/download_by_name.json b/specifications/gridfs/tests/download_by_name.json similarity index 100% rename from tests/MongoDB.Driver.GridFS.Tests/Specifications/gridfs/tests/download_by_name.json rename to specifications/gridfs/tests/download_by_name.json diff --git a/tests/MongoDB.Driver.GridFS.Tests/Specifications/gridfs/tests/download_by_name.yml b/specifications/gridfs/tests/download_by_name.yml similarity index 100% rename from tests/MongoDB.Driver.GridFS.Tests/Specifications/gridfs/tests/download_by_name.yml rename to specifications/gridfs/tests/download_by_name.yml diff --git a/tests/MongoDB.Driver.GridFS.Tests/Specifications/gridfs/tests/upload.json b/specifications/gridfs/tests/upload.json similarity index 100% rename from tests/MongoDB.Driver.GridFS.Tests/Specifications/gridfs/tests/upload.json rename to specifications/gridfs/tests/upload.json diff --git a/tests/MongoDB.Driver.GridFS.Tests/Specifications/gridfs/tests/upload.yml b/specifications/gridfs/tests/upload.yml similarity index 100% rename from tests/MongoDB.Driver.GridFS.Tests/Specifications/gridfs/tests/upload.yml rename to specifications/gridfs/tests/upload.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/README.rst b/specifications/initial-dns-seedlist-discovery/tests/README.rst similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/README.rst rename to specifications/initial-dns-seedlist-discovery/tests/README.rst diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/direct-connection-false.json b/specifications/initial-dns-seedlist-discovery/tests/direct-connection-false.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/direct-connection-false.json rename to specifications/initial-dns-seedlist-discovery/tests/direct-connection-false.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/direct-connection-false.yml b/specifications/initial-dns-seedlist-discovery/tests/direct-connection-false.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/direct-connection-false.yml rename to specifications/initial-dns-seedlist-discovery/tests/direct-connection-false.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/direct-connection-true.json b/specifications/initial-dns-seedlist-discovery/tests/direct-connection-true.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/direct-connection-true.json rename to specifications/initial-dns-seedlist-discovery/tests/direct-connection-true.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/direct-connection-true.yml b/specifications/initial-dns-seedlist-discovery/tests/direct-connection-true.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/direct-connection-true.yml rename to specifications/initial-dns-seedlist-discovery/tests/direct-connection-true.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-directConnection.json b/specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-directConnection.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-directConnection.json rename to specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-directConnection.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-directConnection.yml b/specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-directConnection.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-directConnection.yml rename to specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-directConnection.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-no-results.json b/specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-no-results.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-no-results.json rename to specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-no-results.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-no-results.yml b/specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-no-results.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-no-results.yml rename to specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-no-results.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-replicaSet-errors.json b/specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-replicaSet-errors.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-replicaSet-errors.json rename to specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-replicaSet-errors.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-replicaSet-errors.yml b/specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-replicaSet-errors.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-replicaSet-errors.yml rename to specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-replicaSet-errors.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-true-multiple-hosts.json b/specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-true-multiple-hosts.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-true-multiple-hosts.json rename to specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-true-multiple-hosts.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-true-multiple-hosts.yml b/specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-true-multiple-hosts.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-true-multiple-hosts.yml rename to specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-true-multiple-hosts.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-true-txt.json b/specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-true-txt.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-true-txt.json rename to specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-true-txt.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-true-txt.yml b/specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-true-txt.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-true-txt.yml rename to specifications/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-true-txt.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-conflicts_with_loadBalanced-true-txt.json b/specifications/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-conflicts_with_loadBalanced-true-txt.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-conflicts_with_loadBalanced-true-txt.json rename to specifications/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-conflicts_with_loadBalanced-true-txt.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-conflicts_with_loadBalanced-true-txt.yml b/specifications/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-conflicts_with_loadBalanced-true-txt.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-conflicts_with_loadBalanced-true-txt.yml rename to specifications/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-conflicts_with_loadBalanced-true-txt.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-conflicts_with_loadBalanced-true.json b/specifications/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-conflicts_with_loadBalanced-true.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-conflicts_with_loadBalanced-true.json rename to specifications/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-conflicts_with_loadBalanced-true.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-conflicts_with_loadBalanced-true.yml b/specifications/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-conflicts_with_loadBalanced-true.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-conflicts_with_loadBalanced-true.yml rename to specifications/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-conflicts_with_loadBalanced-true.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-zero-txt.json b/specifications/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-zero-txt.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-zero-txt.json rename to specifications/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-zero-txt.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-zero-txt.yml b/specifications/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-zero-txt.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-zero-txt.yml rename to specifications/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-zero-txt.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-zero.json b/specifications/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-zero.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-zero.json rename to specifications/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-zero.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-zero.yml b/specifications/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-zero.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-zero.yml rename to specifications/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-zero.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/longer-parent-in-return.json b/specifications/initial-dns-seedlist-discovery/tests/longer-parent-in-return.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/longer-parent-in-return.json rename to specifications/initial-dns-seedlist-discovery/tests/longer-parent-in-return.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/longer-parent-in-return.yml b/specifications/initial-dns-seedlist-discovery/tests/longer-parent-in-return.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/longer-parent-in-return.yml rename to specifications/initial-dns-seedlist-discovery/tests/longer-parent-in-return.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/misformatted-option.json b/specifications/initial-dns-seedlist-discovery/tests/misformatted-option.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/misformatted-option.json rename to specifications/initial-dns-seedlist-discovery/tests/misformatted-option.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/misformatted-option.yml b/specifications/initial-dns-seedlist-discovery/tests/misformatted-option.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/misformatted-option.yml rename to specifications/initial-dns-seedlist-discovery/tests/misformatted-option.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/no-results.json b/specifications/initial-dns-seedlist-discovery/tests/no-results.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/no-results.json rename to specifications/initial-dns-seedlist-discovery/tests/no-results.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/no-results.yml b/specifications/initial-dns-seedlist-discovery/tests/no-results.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/no-results.yml rename to specifications/initial-dns-seedlist-discovery/tests/no-results.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/not-enough-parts.json b/specifications/initial-dns-seedlist-discovery/tests/not-enough-parts.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/not-enough-parts.json rename to specifications/initial-dns-seedlist-discovery/tests/not-enough-parts.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/not-enough-parts.yml b/specifications/initial-dns-seedlist-discovery/tests/not-enough-parts.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/not-enough-parts.yml rename to specifications/initial-dns-seedlist-discovery/tests/not-enough-parts.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/one-result-default-port.json b/specifications/initial-dns-seedlist-discovery/tests/one-result-default-port.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/one-result-default-port.json rename to specifications/initial-dns-seedlist-discovery/tests/one-result-default-port.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/one-result-default-port.yml b/specifications/initial-dns-seedlist-discovery/tests/one-result-default-port.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/one-result-default-port.yml rename to specifications/initial-dns-seedlist-discovery/tests/one-result-default-port.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/one-txt-record-multiple-strings.json b/specifications/initial-dns-seedlist-discovery/tests/one-txt-record-multiple-strings.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/one-txt-record-multiple-strings.json rename to specifications/initial-dns-seedlist-discovery/tests/one-txt-record-multiple-strings.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/one-txt-record-multiple-strings.yml b/specifications/initial-dns-seedlist-discovery/tests/one-txt-record-multiple-strings.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/one-txt-record-multiple-strings.yml rename to specifications/initial-dns-seedlist-discovery/tests/one-txt-record-multiple-strings.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/one-txt-record.json b/specifications/initial-dns-seedlist-discovery/tests/one-txt-record.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/one-txt-record.json rename to specifications/initial-dns-seedlist-discovery/tests/one-txt-record.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/one-txt-record.yml b/specifications/initial-dns-seedlist-discovery/tests/one-txt-record.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/one-txt-record.yml rename to specifications/initial-dns-seedlist-discovery/tests/one-txt-record.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch1.json b/specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch1.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch1.json rename to specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch1.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch1.yml b/specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch1.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch1.yml rename to specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch1.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch2.json b/specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch2.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch2.json rename to specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch2.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch2.yml b/specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch2.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch2.yml rename to specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch2.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch3.json b/specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch3.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch3.json rename to specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch3.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch3.yml b/specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch3.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch3.yml rename to specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch3.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch4.json b/specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch4.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch4.json rename to specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch4.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch4.yml b/specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch4.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch4.yml rename to specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch4.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch5.json b/specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch5.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch5.json rename to specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch5.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch5.yml b/specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch5.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch5.yml rename to specifications/initial-dns-seedlist-discovery/tests/parent-part-mismatch5.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/direct-connection-false.json b/specifications/initial-dns-seedlist-discovery/tests/replica-set/direct-connection-false.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/direct-connection-false.json rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/direct-connection-false.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/direct-connection-true.json b/specifications/initial-dns-seedlist-discovery/tests/replica-set/direct-connection-true.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/direct-connection-true.json rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/direct-connection-true.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/loadBalanced-false-txt.json b/specifications/initial-dns-seedlist-discovery/tests/replica-set/loadBalanced-false-txt.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/loadBalanced-false-txt.json rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/loadBalanced-false-txt.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/longer-parent-in-return.json b/specifications/initial-dns-seedlist-discovery/tests/replica-set/longer-parent-in-return.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/longer-parent-in-return.json rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/longer-parent-in-return.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/misformatted-option.json b/specifications/initial-dns-seedlist-discovery/tests/replica-set/misformatted-option.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/misformatted-option.json rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/misformatted-option.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/no-results.json b/specifications/initial-dns-seedlist-discovery/tests/replica-set/no-results.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/no-results.json rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/no-results.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/not-enough-parts.json b/specifications/initial-dns-seedlist-discovery/tests/replica-set/not-enough-parts.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/not-enough-parts.json rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/not-enough-parts.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/one-result-default-port.json b/specifications/initial-dns-seedlist-discovery/tests/replica-set/one-result-default-port.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/one-result-default-port.json rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/one-result-default-port.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/one-txt-record-multiple-strings.json b/specifications/initial-dns-seedlist-discovery/tests/replica-set/one-txt-record-multiple-strings.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/one-txt-record-multiple-strings.json rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/one-txt-record-multiple-strings.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/one-txt-record.json b/specifications/initial-dns-seedlist-discovery/tests/replica-set/one-txt-record.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/one-txt-record.json rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/one-txt-record.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch1.json b/specifications/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch1.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch1.json rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch1.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch2.json b/specifications/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch2.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch2.json rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch2.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch3.json b/specifications/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch3.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch3.json rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch3.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch4.json b/specifications/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch4.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch4.json rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch4.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch5.json b/specifications/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch5.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch5.json rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch5.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/returned-parent-too-short.json b/specifications/initial-dns-seedlist-discovery/tests/replica-set/returned-parent-too-short.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/returned-parent-too-short.json rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/returned-parent-too-short.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/returned-parent-wrong.json b/specifications/initial-dns-seedlist-discovery/tests/replica-set/returned-parent-wrong.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/returned-parent-wrong.json rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/returned-parent-wrong.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-conflicts_with_replicaSet-txt.json b/specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-conflicts_with_replicaSet-txt.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-conflicts_with_replicaSet-txt.json rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-conflicts_with_replicaSet-txt.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-conflicts_with_replicaSet-txt.yml b/specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-conflicts_with_replicaSet-txt.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-conflicts_with_replicaSet-txt.yml rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-conflicts_with_replicaSet-txt.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-conflicts_with_replicaSet.json b/specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-conflicts_with_replicaSet.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-conflicts_with_replicaSet.json rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-conflicts_with_replicaSet.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-conflicts_with_replicaSet.yml b/specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-conflicts_with_replicaSet.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-conflicts_with_replicaSet.yml rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-conflicts_with_replicaSet.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-equal_to_srv_records.json b/specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-equal_to_srv_records.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-equal_to_srv_records.json rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-equal_to_srv_records.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-equal_to_srv_records.yml b/specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-equal_to_srv_records.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-equal_to_srv_records.yml rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-equal_to_srv_records.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-greater_than_srv_records.json b/specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-greater_than_srv_records.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-greater_than_srv_records.json rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-greater_than_srv_records.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-greater_than_srv_records.yml b/specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-greater_than_srv_records.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-greater_than_srv_records.yml rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-greater_than_srv_records.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-invalid_integer.json b/specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-invalid_integer.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-invalid_integer.json rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-invalid_integer.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-invalid_integer.yml b/specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-invalid_integer.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-invalid_integer.yml rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-invalid_integer.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-invalid_type.json b/specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-invalid_type.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-invalid_type.json rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-invalid_type.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-invalid_type.yml b/specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-invalid_type.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-invalid_type.yml rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-invalid_type.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-less_than_srv_records.json b/specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-less_than_srv_records.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-less_than_srv_records.json rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-less_than_srv_records.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-less_than_srv_records.yml b/specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-less_than_srv_records.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-less_than_srv_records.yml rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-less_than_srv_records.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-zero-txt.json b/specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-zero-txt.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-zero-txt.json rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-zero-txt.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-zero-txt.yml b/specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-zero-txt.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-zero-txt.yml rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-zero-txt.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-zero.json b/specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-zero.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-zero.json rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-zero.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-zero.yml b/specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-zero.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-zero.yml rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-zero.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/two-results-default-port.json b/specifications/initial-dns-seedlist-discovery/tests/replica-set/two-results-default-port.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/two-results-default-port.json rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/two-results-default-port.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/two-results-nonstandard-port.json b/specifications/initial-dns-seedlist-discovery/tests/replica-set/two-results-nonstandard-port.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/two-results-nonstandard-port.json rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/two-results-nonstandard-port.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/two-txt-records.json b/specifications/initial-dns-seedlist-discovery/tests/replica-set/two-txt-records.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/two-txt-records.json rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/two-txt-records.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/txt-record-not-allowed-option.json b/specifications/initial-dns-seedlist-discovery/tests/replica-set/txt-record-not-allowed-option.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/txt-record-not-allowed-option.json rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/txt-record-not-allowed-option.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/txt-record-with-overridden-ssl-option.json b/specifications/initial-dns-seedlist-discovery/tests/replica-set/txt-record-with-overridden-ssl-option.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/txt-record-with-overridden-ssl-option.json rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/txt-record-with-overridden-ssl-option.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/txt-record-with-overridden-uri-option.json b/specifications/initial-dns-seedlist-discovery/tests/replica-set/txt-record-with-overridden-uri-option.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/txt-record-with-overridden-uri-option.json rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/txt-record-with-overridden-uri-option.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/txt-record-with-unallowed-option.json b/specifications/initial-dns-seedlist-discovery/tests/replica-set/txt-record-with-unallowed-option.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/txt-record-with-unallowed-option.json rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/txt-record-with-unallowed-option.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/uri-with-port.json b/specifications/initial-dns-seedlist-discovery/tests/replica-set/uri-with-port.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/uri-with-port.json rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/uri-with-port.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/uri-with-two-hosts.json b/specifications/initial-dns-seedlist-discovery/tests/replica-set/uri-with-two-hosts.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/replica-set/uri-with-two-hosts.json rename to specifications/initial-dns-seedlist-discovery/tests/replica-set/uri-with-two-hosts.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/returned-parent-too-short.json b/specifications/initial-dns-seedlist-discovery/tests/returned-parent-too-short.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/returned-parent-too-short.json rename to specifications/initial-dns-seedlist-discovery/tests/returned-parent-too-short.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/returned-parent-too-short.yml b/specifications/initial-dns-seedlist-discovery/tests/returned-parent-too-short.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/returned-parent-too-short.yml rename to specifications/initial-dns-seedlist-discovery/tests/returned-parent-too-short.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/returned-parent-wrong.json b/specifications/initial-dns-seedlist-discovery/tests/returned-parent-wrong.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/returned-parent-wrong.json rename to specifications/initial-dns-seedlist-discovery/tests/returned-parent-wrong.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/returned-parent-wrong.yml b/specifications/initial-dns-seedlist-discovery/tests/returned-parent-wrong.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/returned-parent-wrong.yml rename to specifications/initial-dns-seedlist-discovery/tests/returned-parent-wrong.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-equal_to_srv_records.json b/specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-equal_to_srv_records.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-equal_to_srv_records.json rename to specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-equal_to_srv_records.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-equal_to_srv_records.yml b/specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-equal_to_srv_records.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-equal_to_srv_records.yml rename to specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-equal_to_srv_records.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-greater_than_srv_records.json b/specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-greater_than_srv_records.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-greater_than_srv_records.json rename to specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-greater_than_srv_records.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-greater_than_srv_records.yml b/specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-greater_than_srv_records.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-greater_than_srv_records.yml rename to specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-greater_than_srv_records.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-invalid_integer.json b/specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-invalid_integer.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-invalid_integer.json rename to specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-invalid_integer.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-invalid_integer.yml b/specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-invalid_integer.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-invalid_integer.yml rename to specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-invalid_integer.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-invalid_type.json b/specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-invalid_type.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-invalid_type.json rename to specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-invalid_type.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-invalid_type.yml b/specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-invalid_type.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-invalid_type.yml rename to specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-invalid_type.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-less_than_srv_records.json b/specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-less_than_srv_records.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-less_than_srv_records.json rename to specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-less_than_srv_records.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-less_than_srv_records.yml b/specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-less_than_srv_records.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-less_than_srv_records.yml rename to specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-less_than_srv_records.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-zero.json b/specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-zero.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-zero.json rename to specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-zero.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-zero.yml b/specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-zero.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-zero.yml rename to specifications/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-zero.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/two-results-default-port.json b/specifications/initial-dns-seedlist-discovery/tests/two-results-default-port.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/two-results-default-port.json rename to specifications/initial-dns-seedlist-discovery/tests/two-results-default-port.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/two-results-default-port.yml b/specifications/initial-dns-seedlist-discovery/tests/two-results-default-port.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/two-results-default-port.yml rename to specifications/initial-dns-seedlist-discovery/tests/two-results-default-port.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/two-results-nonstandard-port.json b/specifications/initial-dns-seedlist-discovery/tests/two-results-nonstandard-port.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/two-results-nonstandard-port.json rename to specifications/initial-dns-seedlist-discovery/tests/two-results-nonstandard-port.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/two-results-nonstandard-port.yml b/specifications/initial-dns-seedlist-discovery/tests/two-results-nonstandard-port.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/two-results-nonstandard-port.yml rename to specifications/initial-dns-seedlist-discovery/tests/two-results-nonstandard-port.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/two-txt-records.json b/specifications/initial-dns-seedlist-discovery/tests/two-txt-records.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/two-txt-records.json rename to specifications/initial-dns-seedlist-discovery/tests/two-txt-records.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/two-txt-records.yml b/specifications/initial-dns-seedlist-discovery/tests/two-txt-records.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/two-txt-records.yml rename to specifications/initial-dns-seedlist-discovery/tests/two-txt-records.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/txt-record-not-allowed-option.json b/specifications/initial-dns-seedlist-discovery/tests/txt-record-not-allowed-option.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/txt-record-not-allowed-option.json rename to specifications/initial-dns-seedlist-discovery/tests/txt-record-not-allowed-option.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/txt-record-not-allowed-option.yml b/specifications/initial-dns-seedlist-discovery/tests/txt-record-not-allowed-option.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/txt-record-not-allowed-option.yml rename to specifications/initial-dns-seedlist-discovery/tests/txt-record-not-allowed-option.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/txt-record-with-overridden-ssl-option.json b/specifications/initial-dns-seedlist-discovery/tests/txt-record-with-overridden-ssl-option.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/txt-record-with-overridden-ssl-option.json rename to specifications/initial-dns-seedlist-discovery/tests/txt-record-with-overridden-ssl-option.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/txt-record-with-overridden-ssl-option.yml b/specifications/initial-dns-seedlist-discovery/tests/txt-record-with-overridden-ssl-option.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/txt-record-with-overridden-ssl-option.yml rename to specifications/initial-dns-seedlist-discovery/tests/txt-record-with-overridden-ssl-option.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/txt-record-with-overridden-uri-option.json b/specifications/initial-dns-seedlist-discovery/tests/txt-record-with-overridden-uri-option.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/txt-record-with-overridden-uri-option.json rename to specifications/initial-dns-seedlist-discovery/tests/txt-record-with-overridden-uri-option.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/txt-record-with-overridden-uri-option.yml b/specifications/initial-dns-seedlist-discovery/tests/txt-record-with-overridden-uri-option.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/txt-record-with-overridden-uri-option.yml rename to specifications/initial-dns-seedlist-discovery/tests/txt-record-with-overridden-uri-option.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/txt-record-with-unallowed-option.json b/specifications/initial-dns-seedlist-discovery/tests/txt-record-with-unallowed-option.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/txt-record-with-unallowed-option.json rename to specifications/initial-dns-seedlist-discovery/tests/txt-record-with-unallowed-option.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/txt-record-with-unallowed-option.yml b/specifications/initial-dns-seedlist-discovery/tests/txt-record-with-unallowed-option.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/txt-record-with-unallowed-option.yml rename to specifications/initial-dns-seedlist-discovery/tests/txt-record-with-unallowed-option.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/uri-with-port.json b/specifications/initial-dns-seedlist-discovery/tests/uri-with-port.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/uri-with-port.json rename to specifications/initial-dns-seedlist-discovery/tests/uri-with-port.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/uri-with-port.yml b/specifications/initial-dns-seedlist-discovery/tests/uri-with-port.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/uri-with-port.yml rename to specifications/initial-dns-seedlist-discovery/tests/uri-with-port.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/uri-with-two-hosts.json b/specifications/initial-dns-seedlist-discovery/tests/uri-with-two-hosts.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/uri-with-two-hosts.json rename to specifications/initial-dns-seedlist-discovery/tests/uri-with-two-hosts.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/uri-with-two-hosts.yml b/specifications/initial-dns-seedlist-discovery/tests/uri-with-two-hosts.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/tests/uri-with-two-hosts.yml rename to specifications/initial-dns-seedlist-discovery/tests/uri-with-two-hosts.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/load-balancers/tests/README.rst b/specifications/load-balancers/tests/README.rst similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/load-balancers/tests/README.rst rename to specifications/load-balancers/tests/README.rst diff --git a/tests/MongoDB.Driver.Tests/Specifications/load-balancers/tests/cursors.json b/specifications/load-balancers/tests/cursors.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/load-balancers/tests/cursors.json rename to specifications/load-balancers/tests/cursors.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/load-balancers/tests/cursors.yml b/specifications/load-balancers/tests/cursors.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/load-balancers/tests/cursors.yml rename to specifications/load-balancers/tests/cursors.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/load-balancers/tests/event-monitoring.json b/specifications/load-balancers/tests/event-monitoring.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/load-balancers/tests/event-monitoring.json rename to specifications/load-balancers/tests/event-monitoring.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/load-balancers/tests/event-monitoring.yml b/specifications/load-balancers/tests/event-monitoring.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/load-balancers/tests/event-monitoring.yml rename to specifications/load-balancers/tests/event-monitoring.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/load-balancers/tests/lb-connection-establishment.json b/specifications/load-balancers/tests/lb-connection-establishment.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/load-balancers/tests/lb-connection-establishment.json rename to specifications/load-balancers/tests/lb-connection-establishment.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/load-balancers/tests/lb-connection-establishment.yml b/specifications/load-balancers/tests/lb-connection-establishment.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/load-balancers/tests/lb-connection-establishment.yml rename to specifications/load-balancers/tests/lb-connection-establishment.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/load-balancers/tests/non-lb-connection-establishment.json b/specifications/load-balancers/tests/non-lb-connection-establishment.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/load-balancers/tests/non-lb-connection-establishment.json rename to specifications/load-balancers/tests/non-lb-connection-establishment.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/load-balancers/tests/non-lb-connection-establishment.yml b/specifications/load-balancers/tests/non-lb-connection-establishment.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/load-balancers/tests/non-lb-connection-establishment.yml rename to specifications/load-balancers/tests/non-lb-connection-establishment.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/load-balancers/tests/sdam-error-handling.json b/specifications/load-balancers/tests/sdam-error-handling.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/load-balancers/tests/sdam-error-handling.json rename to specifications/load-balancers/tests/sdam-error-handling.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/load-balancers/tests/sdam-error-handling.yml b/specifications/load-balancers/tests/sdam-error-handling.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/load-balancers/tests/sdam-error-handling.yml rename to specifications/load-balancers/tests/sdam-error-handling.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/load-balancers/tests/server-selection.json b/specifications/load-balancers/tests/server-selection.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/load-balancers/tests/server-selection.json rename to specifications/load-balancers/tests/server-selection.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/load-balancers/tests/server-selection.yml b/specifications/load-balancers/tests/server-selection.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/load-balancers/tests/server-selection.yml rename to specifications/load-balancers/tests/server-selection.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/load-balancers/tests/transactions.json b/specifications/load-balancers/tests/transactions.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/load-balancers/tests/transactions.json rename to specifications/load-balancers/tests/transactions.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/load-balancers/tests/transactions.yml b/specifications/load-balancers/tests/transactions.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/load-balancers/tests/transactions.yml rename to specifications/load-balancers/tests/transactions.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/load-balancers/tests/wait-queue-timeouts.json b/specifications/load-balancers/tests/wait-queue-timeouts.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/load-balancers/tests/wait-queue-timeouts.json rename to specifications/load-balancers/tests/wait-queue-timeouts.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/load-balancers/tests/wait-queue-timeouts.yml b/specifications/load-balancers/tests/wait-queue-timeouts.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/load-balancers/tests/wait-queue-timeouts.yml rename to specifications/load-balancers/tests/wait-queue-timeouts.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/origin.txt b/specifications/max-staleness/origin.txt similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/origin.txt rename to specifications/max-staleness/origin.txt diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/runner.txt b/specifications/max-staleness/runner.txt similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/runner.txt rename to specifications/max-staleness/runner.txt diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/README.rst b/specifications/max-staleness/tests/README.rst similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/README.rst rename to specifications/max-staleness/tests/README.rst diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/DefaultNoMaxStaleness.json b/specifications/max-staleness/tests/ReplicaSetNoPrimary/DefaultNoMaxStaleness.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/DefaultNoMaxStaleness.json rename to specifications/max-staleness/tests/ReplicaSetNoPrimary/DefaultNoMaxStaleness.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/DefaultNoMaxStaleness.yml b/specifications/max-staleness/tests/ReplicaSetNoPrimary/DefaultNoMaxStaleness.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/DefaultNoMaxStaleness.yml rename to specifications/max-staleness/tests/ReplicaSetNoPrimary/DefaultNoMaxStaleness.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/LastUpdateTime.json b/specifications/max-staleness/tests/ReplicaSetNoPrimary/LastUpdateTime.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/LastUpdateTime.json rename to specifications/max-staleness/tests/ReplicaSetNoPrimary/LastUpdateTime.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/LastUpdateTime.yml b/specifications/max-staleness/tests/ReplicaSetNoPrimary/LastUpdateTime.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/LastUpdateTime.yml rename to specifications/max-staleness/tests/ReplicaSetNoPrimary/LastUpdateTime.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/MaxStalenessTooSmall.json b/specifications/max-staleness/tests/ReplicaSetNoPrimary/MaxStalenessTooSmall.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/MaxStalenessTooSmall.json rename to specifications/max-staleness/tests/ReplicaSetNoPrimary/MaxStalenessTooSmall.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/MaxStalenessTooSmall.yml b/specifications/max-staleness/tests/ReplicaSetNoPrimary/MaxStalenessTooSmall.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/MaxStalenessTooSmall.yml rename to specifications/max-staleness/tests/ReplicaSetNoPrimary/MaxStalenessTooSmall.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/Nearest.json b/specifications/max-staleness/tests/ReplicaSetNoPrimary/Nearest.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/Nearest.json rename to specifications/max-staleness/tests/ReplicaSetNoPrimary/Nearest.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/Nearest.yml b/specifications/max-staleness/tests/ReplicaSetNoPrimary/Nearest.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/Nearest.yml rename to specifications/max-staleness/tests/ReplicaSetNoPrimary/Nearest.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/Nearest2.json b/specifications/max-staleness/tests/ReplicaSetNoPrimary/Nearest2.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/Nearest2.json rename to specifications/max-staleness/tests/ReplicaSetNoPrimary/Nearest2.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/Nearest2.yml b/specifications/max-staleness/tests/ReplicaSetNoPrimary/Nearest2.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/Nearest2.yml rename to specifications/max-staleness/tests/ReplicaSetNoPrimary/Nearest2.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/NoKnownServers.json b/specifications/max-staleness/tests/ReplicaSetNoPrimary/NoKnownServers.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/NoKnownServers.json rename to specifications/max-staleness/tests/ReplicaSetNoPrimary/NoKnownServers.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/NoKnownServers.yml b/specifications/max-staleness/tests/ReplicaSetNoPrimary/NoKnownServers.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/NoKnownServers.yml rename to specifications/max-staleness/tests/ReplicaSetNoPrimary/NoKnownServers.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/OneKnownTwoUnavailable.json b/specifications/max-staleness/tests/ReplicaSetNoPrimary/OneKnownTwoUnavailable.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/OneKnownTwoUnavailable.json rename to specifications/max-staleness/tests/ReplicaSetNoPrimary/OneKnownTwoUnavailable.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/OneKnownTwoUnavailable.yml b/specifications/max-staleness/tests/ReplicaSetNoPrimary/OneKnownTwoUnavailable.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/OneKnownTwoUnavailable.yml rename to specifications/max-staleness/tests/ReplicaSetNoPrimary/OneKnownTwoUnavailable.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/PrimaryPreferred.json b/specifications/max-staleness/tests/ReplicaSetNoPrimary/PrimaryPreferred.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/PrimaryPreferred.json rename to specifications/max-staleness/tests/ReplicaSetNoPrimary/PrimaryPreferred.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/PrimaryPreferred.yml b/specifications/max-staleness/tests/ReplicaSetNoPrimary/PrimaryPreferred.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/PrimaryPreferred.yml rename to specifications/max-staleness/tests/ReplicaSetNoPrimary/PrimaryPreferred.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/PrimaryPreferred_tags.json b/specifications/max-staleness/tests/ReplicaSetNoPrimary/PrimaryPreferred_tags.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/PrimaryPreferred_tags.json rename to specifications/max-staleness/tests/ReplicaSetNoPrimary/PrimaryPreferred_tags.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/PrimaryPreferred_tags.yml b/specifications/max-staleness/tests/ReplicaSetNoPrimary/PrimaryPreferred_tags.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/PrimaryPreferred_tags.yml rename to specifications/max-staleness/tests/ReplicaSetNoPrimary/PrimaryPreferred_tags.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/Secondary.json b/specifications/max-staleness/tests/ReplicaSetNoPrimary/Secondary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/Secondary.json rename to specifications/max-staleness/tests/ReplicaSetNoPrimary/Secondary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/Secondary.yml b/specifications/max-staleness/tests/ReplicaSetNoPrimary/Secondary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/Secondary.yml rename to specifications/max-staleness/tests/ReplicaSetNoPrimary/Secondary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/SecondaryPreferred.json b/specifications/max-staleness/tests/ReplicaSetNoPrimary/SecondaryPreferred.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/SecondaryPreferred.json rename to specifications/max-staleness/tests/ReplicaSetNoPrimary/SecondaryPreferred.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/SecondaryPreferred.yml b/specifications/max-staleness/tests/ReplicaSetNoPrimary/SecondaryPreferred.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/SecondaryPreferred.yml rename to specifications/max-staleness/tests/ReplicaSetNoPrimary/SecondaryPreferred.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/SecondaryPreferred_tags.json b/specifications/max-staleness/tests/ReplicaSetNoPrimary/SecondaryPreferred_tags.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/SecondaryPreferred_tags.json rename to specifications/max-staleness/tests/ReplicaSetNoPrimary/SecondaryPreferred_tags.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/SecondaryPreferred_tags.yml b/specifications/max-staleness/tests/ReplicaSetNoPrimary/SecondaryPreferred_tags.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/SecondaryPreferred_tags.yml rename to specifications/max-staleness/tests/ReplicaSetNoPrimary/SecondaryPreferred_tags.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/ZeroMaxStaleness.json b/specifications/max-staleness/tests/ReplicaSetNoPrimary/ZeroMaxStaleness.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/ZeroMaxStaleness.json rename to specifications/max-staleness/tests/ReplicaSetNoPrimary/ZeroMaxStaleness.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/ZeroMaxStaleness.yml b/specifications/max-staleness/tests/ReplicaSetNoPrimary/ZeroMaxStaleness.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetNoPrimary/ZeroMaxStaleness.yml rename to specifications/max-staleness/tests/ReplicaSetNoPrimary/ZeroMaxStaleness.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/DefaultNoMaxStaleness.json b/specifications/max-staleness/tests/ReplicaSetWithPrimary/DefaultNoMaxStaleness.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/DefaultNoMaxStaleness.json rename to specifications/max-staleness/tests/ReplicaSetWithPrimary/DefaultNoMaxStaleness.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/DefaultNoMaxStaleness.yml b/specifications/max-staleness/tests/ReplicaSetWithPrimary/DefaultNoMaxStaleness.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/DefaultNoMaxStaleness.yml rename to specifications/max-staleness/tests/ReplicaSetWithPrimary/DefaultNoMaxStaleness.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/LastUpdateTime.json b/specifications/max-staleness/tests/ReplicaSetWithPrimary/LastUpdateTime.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/LastUpdateTime.json rename to specifications/max-staleness/tests/ReplicaSetWithPrimary/LastUpdateTime.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/LastUpdateTime.yml b/specifications/max-staleness/tests/ReplicaSetWithPrimary/LastUpdateTime.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/LastUpdateTime.yml rename to specifications/max-staleness/tests/ReplicaSetWithPrimary/LastUpdateTime.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/LongHeartbeat.json b/specifications/max-staleness/tests/ReplicaSetWithPrimary/LongHeartbeat.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/LongHeartbeat.json rename to specifications/max-staleness/tests/ReplicaSetWithPrimary/LongHeartbeat.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/LongHeartbeat.yml b/specifications/max-staleness/tests/ReplicaSetWithPrimary/LongHeartbeat.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/LongHeartbeat.yml rename to specifications/max-staleness/tests/ReplicaSetWithPrimary/LongHeartbeat.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/LongHeartbeat2.json b/specifications/max-staleness/tests/ReplicaSetWithPrimary/LongHeartbeat2.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/LongHeartbeat2.json rename to specifications/max-staleness/tests/ReplicaSetWithPrimary/LongHeartbeat2.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/LongHeartbeat2.yml b/specifications/max-staleness/tests/ReplicaSetWithPrimary/LongHeartbeat2.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/LongHeartbeat2.yml rename to specifications/max-staleness/tests/ReplicaSetWithPrimary/LongHeartbeat2.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/MaxStalenessTooSmall.json b/specifications/max-staleness/tests/ReplicaSetWithPrimary/MaxStalenessTooSmall.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/MaxStalenessTooSmall.json rename to specifications/max-staleness/tests/ReplicaSetWithPrimary/MaxStalenessTooSmall.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/MaxStalenessTooSmall.yml b/specifications/max-staleness/tests/ReplicaSetWithPrimary/MaxStalenessTooSmall.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/MaxStalenessTooSmall.yml rename to specifications/max-staleness/tests/ReplicaSetWithPrimary/MaxStalenessTooSmall.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/MaxStalenessWithModePrimary.json b/specifications/max-staleness/tests/ReplicaSetWithPrimary/MaxStalenessWithModePrimary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/MaxStalenessWithModePrimary.json rename to specifications/max-staleness/tests/ReplicaSetWithPrimary/MaxStalenessWithModePrimary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/MaxStalenessWithModePrimary.yml b/specifications/max-staleness/tests/ReplicaSetWithPrimary/MaxStalenessWithModePrimary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/MaxStalenessWithModePrimary.yml rename to specifications/max-staleness/tests/ReplicaSetWithPrimary/MaxStalenessWithModePrimary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/Nearest.json b/specifications/max-staleness/tests/ReplicaSetWithPrimary/Nearest.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/Nearest.json rename to specifications/max-staleness/tests/ReplicaSetWithPrimary/Nearest.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/Nearest.yml b/specifications/max-staleness/tests/ReplicaSetWithPrimary/Nearest.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/Nearest.yml rename to specifications/max-staleness/tests/ReplicaSetWithPrimary/Nearest.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/Nearest2.json b/specifications/max-staleness/tests/ReplicaSetWithPrimary/Nearest2.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/Nearest2.json rename to specifications/max-staleness/tests/ReplicaSetWithPrimary/Nearest2.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/Nearest2.yml b/specifications/max-staleness/tests/ReplicaSetWithPrimary/Nearest2.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/Nearest2.yml rename to specifications/max-staleness/tests/ReplicaSetWithPrimary/Nearest2.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/Nearest_tags.json b/specifications/max-staleness/tests/ReplicaSetWithPrimary/Nearest_tags.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/Nearest_tags.json rename to specifications/max-staleness/tests/ReplicaSetWithPrimary/Nearest_tags.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/Nearest_tags.yml b/specifications/max-staleness/tests/ReplicaSetWithPrimary/Nearest_tags.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/Nearest_tags.yml rename to specifications/max-staleness/tests/ReplicaSetWithPrimary/Nearest_tags.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/PrimaryPreferred.json b/specifications/max-staleness/tests/ReplicaSetWithPrimary/PrimaryPreferred.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/PrimaryPreferred.json rename to specifications/max-staleness/tests/ReplicaSetWithPrimary/PrimaryPreferred.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/PrimaryPreferred.yml b/specifications/max-staleness/tests/ReplicaSetWithPrimary/PrimaryPreferred.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/PrimaryPreferred.yml rename to specifications/max-staleness/tests/ReplicaSetWithPrimary/PrimaryPreferred.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred.json b/specifications/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred.json rename to specifications/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred.yml b/specifications/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred.yml rename to specifications/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred_tags.json b/specifications/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred_tags.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred_tags.json rename to specifications/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred_tags.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred_tags.yml b/specifications/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred_tags.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred_tags.yml rename to specifications/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred_tags.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred_tags2.json b/specifications/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred_tags2.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred_tags2.json rename to specifications/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred_tags2.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred_tags2.yml b/specifications/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred_tags2.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred_tags2.yml rename to specifications/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred_tags2.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/Secondary_tags.json b/specifications/max-staleness/tests/ReplicaSetWithPrimary/Secondary_tags.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/Secondary_tags.json rename to specifications/max-staleness/tests/ReplicaSetWithPrimary/Secondary_tags.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/Secondary_tags.yml b/specifications/max-staleness/tests/ReplicaSetWithPrimary/Secondary_tags.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/Secondary_tags.yml rename to specifications/max-staleness/tests/ReplicaSetWithPrimary/Secondary_tags.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/Secondary_tags2.json b/specifications/max-staleness/tests/ReplicaSetWithPrimary/Secondary_tags2.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/Secondary_tags2.json rename to specifications/max-staleness/tests/ReplicaSetWithPrimary/Secondary_tags2.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/Secondary_tags2.yml b/specifications/max-staleness/tests/ReplicaSetWithPrimary/Secondary_tags2.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/Secondary_tags2.yml rename to specifications/max-staleness/tests/ReplicaSetWithPrimary/Secondary_tags2.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/ZeroMaxStaleness.json b/specifications/max-staleness/tests/ReplicaSetWithPrimary/ZeroMaxStaleness.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/ZeroMaxStaleness.json rename to specifications/max-staleness/tests/ReplicaSetWithPrimary/ZeroMaxStaleness.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/ZeroMaxStaleness.yml b/specifications/max-staleness/tests/ReplicaSetWithPrimary/ZeroMaxStaleness.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/ReplicaSetWithPrimary/ZeroMaxStaleness.yml rename to specifications/max-staleness/tests/ReplicaSetWithPrimary/ZeroMaxStaleness.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/Sharded/SmallMaxStaleness.json b/specifications/max-staleness/tests/Sharded/SmallMaxStaleness.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/Sharded/SmallMaxStaleness.json rename to specifications/max-staleness/tests/Sharded/SmallMaxStaleness.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/Sharded/SmallMaxStaleness.yml b/specifications/max-staleness/tests/Sharded/SmallMaxStaleness.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/Sharded/SmallMaxStaleness.yml rename to specifications/max-staleness/tests/Sharded/SmallMaxStaleness.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/Single/SmallMaxStaleness.json b/specifications/max-staleness/tests/Single/SmallMaxStaleness.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/Single/SmallMaxStaleness.json rename to specifications/max-staleness/tests/Single/SmallMaxStaleness.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/Single/SmallMaxStaleness.yml b/specifications/max-staleness/tests/Single/SmallMaxStaleness.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/Single/SmallMaxStaleness.yml rename to specifications/max-staleness/tests/Single/SmallMaxStaleness.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/Unknown/SmallMaxStaleness.json b/specifications/max-staleness/tests/Unknown/SmallMaxStaleness.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/Unknown/SmallMaxStaleness.json rename to specifications/max-staleness/tests/Unknown/SmallMaxStaleness.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/Unknown/SmallMaxStaleness.yml b/specifications/max-staleness/tests/Unknown/SmallMaxStaleness.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/max-staleness/tests/Unknown/SmallMaxStaleness.yml rename to specifications/max-staleness/tests/Unknown/SmallMaxStaleness.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/read-write-concern/tests/README.rst b/specifications/read-write-concern/tests/README.rst similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/read-write-concern/tests/README.rst rename to specifications/read-write-concern/tests/README.rst diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/read-write-concern/tests/connection-string/read-concern.json b/specifications/read-write-concern/tests/connection-string/read-concern.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/read-write-concern/tests/connection-string/read-concern.json rename to specifications/read-write-concern/tests/connection-string/read-concern.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/read-write-concern/tests/connection-string/read-concern.yml b/specifications/read-write-concern/tests/connection-string/read-concern.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/read-write-concern/tests/connection-string/read-concern.yml rename to specifications/read-write-concern/tests/connection-string/read-concern.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/read-write-concern/tests/connection-string/write-concern.json b/specifications/read-write-concern/tests/connection-string/write-concern.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/read-write-concern/tests/connection-string/write-concern.json rename to specifications/read-write-concern/tests/connection-string/write-concern.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/read-write-concern/tests/connection-string/write-concern.yml b/specifications/read-write-concern/tests/connection-string/write-concern.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/read-write-concern/tests/connection-string/write-concern.yml rename to specifications/read-write-concern/tests/connection-string/write-concern.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/read-write-concern/tests/document/read-concern.json b/specifications/read-write-concern/tests/document/read-concern.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/read-write-concern/tests/document/read-concern.json rename to specifications/read-write-concern/tests/document/read-concern.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/read-write-concern/tests/document/read-concern.yml b/specifications/read-write-concern/tests/document/read-concern.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/read-write-concern/tests/document/read-concern.yml rename to specifications/read-write-concern/tests/document/read-concern.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/read-write-concern/tests/document/write-concern.json b/specifications/read-write-concern/tests/document/write-concern.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/read-write-concern/tests/document/write-concern.json rename to specifications/read-write-concern/tests/document/write-concern.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/read-write-concern/tests/document/write-concern.yml b/specifications/read-write-concern/tests/document/write-concern.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/read-write-concern/tests/document/write-concern.yml rename to specifications/read-write-concern/tests/document/write-concern.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/read-write-concern/tests/operation/default-write-concern-2.6.json b/specifications/read-write-concern/tests/operation/default-write-concern-2.6.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/read-write-concern/tests/operation/default-write-concern-2.6.json rename to specifications/read-write-concern/tests/operation/default-write-concern-2.6.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/read-write-concern/tests/operation/default-write-concern-2.6.yml b/specifications/read-write-concern/tests/operation/default-write-concern-2.6.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/read-write-concern/tests/operation/default-write-concern-2.6.yml rename to specifications/read-write-concern/tests/operation/default-write-concern-2.6.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/read-write-concern/tests/operation/default-write-concern-3.2.json b/specifications/read-write-concern/tests/operation/default-write-concern-3.2.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/read-write-concern/tests/operation/default-write-concern-3.2.json rename to specifications/read-write-concern/tests/operation/default-write-concern-3.2.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/read-write-concern/tests/operation/default-write-concern-3.2.yml b/specifications/read-write-concern/tests/operation/default-write-concern-3.2.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/read-write-concern/tests/operation/default-write-concern-3.2.yml rename to specifications/read-write-concern/tests/operation/default-write-concern-3.2.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/read-write-concern/tests/operation/default-write-concern-3.4.json b/specifications/read-write-concern/tests/operation/default-write-concern-3.4.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/read-write-concern/tests/operation/default-write-concern-3.4.json rename to specifications/read-write-concern/tests/operation/default-write-concern-3.4.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/read-write-concern/tests/operation/default-write-concern-3.4.yml b/specifications/read-write-concern/tests/operation/default-write-concern-3.4.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/read-write-concern/tests/operation/default-write-concern-3.4.yml rename to specifications/read-write-concern/tests/operation/default-write-concern-3.4.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/read-write-concern/tests/operation/default-write-concern-4.2.json b/specifications/read-write-concern/tests/operation/default-write-concern-4.2.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/read-write-concern/tests/operation/default-write-concern-4.2.json rename to specifications/read-write-concern/tests/operation/default-write-concern-4.2.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/read-write-concern/tests/operation/default-write-concern-4.2.yml b/specifications/read-write-concern/tests/operation/default-write-concern-4.2.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/read-write-concern/tests/operation/default-write-concern-4.2.yml rename to specifications/read-write-concern/tests/operation/default-write-concern-4.2.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/README.rst b/specifications/retryable-reads/tests/README.rst similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/README.rst rename to specifications/retryable-reads/tests/README.rst diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/aggregate-merge.json b/specifications/retryable-reads/tests/legacy/aggregate-merge.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/aggregate-merge.json rename to specifications/retryable-reads/tests/legacy/aggregate-merge.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/aggregate-merge.yml b/specifications/retryable-reads/tests/legacy/aggregate-merge.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/aggregate-merge.yml rename to specifications/retryable-reads/tests/legacy/aggregate-merge.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/aggregate-serverErrors.json b/specifications/retryable-reads/tests/legacy/aggregate-serverErrors.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/aggregate-serverErrors.json rename to specifications/retryable-reads/tests/legacy/aggregate-serverErrors.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/aggregate-serverErrors.yml b/specifications/retryable-reads/tests/legacy/aggregate-serverErrors.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/aggregate-serverErrors.yml rename to specifications/retryable-reads/tests/legacy/aggregate-serverErrors.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/aggregate.json b/specifications/retryable-reads/tests/legacy/aggregate.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/aggregate.json rename to specifications/retryable-reads/tests/legacy/aggregate.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/aggregate.yml b/specifications/retryable-reads/tests/legacy/aggregate.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/aggregate.yml rename to specifications/retryable-reads/tests/legacy/aggregate.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/changeStreams-client.watch-serverErrors.json b/specifications/retryable-reads/tests/legacy/changeStreams-client.watch-serverErrors.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/changeStreams-client.watch-serverErrors.json rename to specifications/retryable-reads/tests/legacy/changeStreams-client.watch-serverErrors.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/changeStreams-client.watch-serverErrors.yml b/specifications/retryable-reads/tests/legacy/changeStreams-client.watch-serverErrors.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/changeStreams-client.watch-serverErrors.yml rename to specifications/retryable-reads/tests/legacy/changeStreams-client.watch-serverErrors.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/changeStreams-client.watch.json b/specifications/retryable-reads/tests/legacy/changeStreams-client.watch.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/changeStreams-client.watch.json rename to specifications/retryable-reads/tests/legacy/changeStreams-client.watch.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/changeStreams-client.watch.yml b/specifications/retryable-reads/tests/legacy/changeStreams-client.watch.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/changeStreams-client.watch.yml rename to specifications/retryable-reads/tests/legacy/changeStreams-client.watch.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/changeStreams-db.coll.watch-serverErrors.json b/specifications/retryable-reads/tests/legacy/changeStreams-db.coll.watch-serverErrors.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/changeStreams-db.coll.watch-serverErrors.json rename to specifications/retryable-reads/tests/legacy/changeStreams-db.coll.watch-serverErrors.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/changeStreams-db.coll.watch-serverErrors.yml b/specifications/retryable-reads/tests/legacy/changeStreams-db.coll.watch-serverErrors.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/changeStreams-db.coll.watch-serverErrors.yml rename to specifications/retryable-reads/tests/legacy/changeStreams-db.coll.watch-serverErrors.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/changeStreams-db.coll.watch.json b/specifications/retryable-reads/tests/legacy/changeStreams-db.coll.watch.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/changeStreams-db.coll.watch.json rename to specifications/retryable-reads/tests/legacy/changeStreams-db.coll.watch.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/changeStreams-db.coll.watch.yml b/specifications/retryable-reads/tests/legacy/changeStreams-db.coll.watch.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/changeStreams-db.coll.watch.yml rename to specifications/retryable-reads/tests/legacy/changeStreams-db.coll.watch.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/changeStreams-db.watch-serverErrors.json b/specifications/retryable-reads/tests/legacy/changeStreams-db.watch-serverErrors.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/changeStreams-db.watch-serverErrors.json rename to specifications/retryable-reads/tests/legacy/changeStreams-db.watch-serverErrors.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/changeStreams-db.watch-serverErrors.yml b/specifications/retryable-reads/tests/legacy/changeStreams-db.watch-serverErrors.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/changeStreams-db.watch-serverErrors.yml rename to specifications/retryable-reads/tests/legacy/changeStreams-db.watch-serverErrors.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/changeStreams-db.watch.json b/specifications/retryable-reads/tests/legacy/changeStreams-db.watch.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/changeStreams-db.watch.json rename to specifications/retryable-reads/tests/legacy/changeStreams-db.watch.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/changeStreams-db.watch.yml b/specifications/retryable-reads/tests/legacy/changeStreams-db.watch.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/changeStreams-db.watch.yml rename to specifications/retryable-reads/tests/legacy/changeStreams-db.watch.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/count-serverErrors.json b/specifications/retryable-reads/tests/legacy/count-serverErrors.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/count-serverErrors.json rename to specifications/retryable-reads/tests/legacy/count-serverErrors.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/count-serverErrors.yml b/specifications/retryable-reads/tests/legacy/count-serverErrors.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/count-serverErrors.yml rename to specifications/retryable-reads/tests/legacy/count-serverErrors.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/count.json b/specifications/retryable-reads/tests/legacy/count.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/count.json rename to specifications/retryable-reads/tests/legacy/count.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/count.yml b/specifications/retryable-reads/tests/legacy/count.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/count.yml rename to specifications/retryable-reads/tests/legacy/count.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/countDocuments-serverErrors.json b/specifications/retryable-reads/tests/legacy/countDocuments-serverErrors.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/countDocuments-serverErrors.json rename to specifications/retryable-reads/tests/legacy/countDocuments-serverErrors.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/countDocuments-serverErrors.yml b/specifications/retryable-reads/tests/legacy/countDocuments-serverErrors.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/countDocuments-serverErrors.yml rename to specifications/retryable-reads/tests/legacy/countDocuments-serverErrors.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/countDocuments.json b/specifications/retryable-reads/tests/legacy/countDocuments.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/countDocuments.json rename to specifications/retryable-reads/tests/legacy/countDocuments.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/countDocuments.yml b/specifications/retryable-reads/tests/legacy/countDocuments.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/countDocuments.yml rename to specifications/retryable-reads/tests/legacy/countDocuments.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/distinct-serverErrors.json b/specifications/retryable-reads/tests/legacy/distinct-serverErrors.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/distinct-serverErrors.json rename to specifications/retryable-reads/tests/legacy/distinct-serverErrors.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/distinct-serverErrors.yml b/specifications/retryable-reads/tests/legacy/distinct-serverErrors.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/distinct-serverErrors.yml rename to specifications/retryable-reads/tests/legacy/distinct-serverErrors.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/distinct.json b/specifications/retryable-reads/tests/legacy/distinct.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/distinct.json rename to specifications/retryable-reads/tests/legacy/distinct.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/distinct.yml b/specifications/retryable-reads/tests/legacy/distinct.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/distinct.yml rename to specifications/retryable-reads/tests/legacy/distinct.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/estimatedDocumentCount-serverErrors.json b/specifications/retryable-reads/tests/legacy/estimatedDocumentCount-serverErrors.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/estimatedDocumentCount-serverErrors.json rename to specifications/retryable-reads/tests/legacy/estimatedDocumentCount-serverErrors.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/estimatedDocumentCount-serverErrors.yml b/specifications/retryable-reads/tests/legacy/estimatedDocumentCount-serverErrors.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/estimatedDocumentCount-serverErrors.yml rename to specifications/retryable-reads/tests/legacy/estimatedDocumentCount-serverErrors.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/estimatedDocumentCount.json b/specifications/retryable-reads/tests/legacy/estimatedDocumentCount.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/estimatedDocumentCount.json rename to specifications/retryable-reads/tests/legacy/estimatedDocumentCount.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/estimatedDocumentCount.yml b/specifications/retryable-reads/tests/legacy/estimatedDocumentCount.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/estimatedDocumentCount.yml rename to specifications/retryable-reads/tests/legacy/estimatedDocumentCount.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/find-serverErrors.json b/specifications/retryable-reads/tests/legacy/find-serverErrors.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/find-serverErrors.json rename to specifications/retryable-reads/tests/legacy/find-serverErrors.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/find-serverErrors.yml b/specifications/retryable-reads/tests/legacy/find-serverErrors.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/find-serverErrors.yml rename to specifications/retryable-reads/tests/legacy/find-serverErrors.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/find.json b/specifications/retryable-reads/tests/legacy/find.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/find.json rename to specifications/retryable-reads/tests/legacy/find.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/find.yml b/specifications/retryable-reads/tests/legacy/find.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/find.yml rename to specifications/retryable-reads/tests/legacy/find.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/findOne-serverErrors.json b/specifications/retryable-reads/tests/legacy/findOne-serverErrors.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/findOne-serverErrors.json rename to specifications/retryable-reads/tests/legacy/findOne-serverErrors.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/findOne-serverErrors.yml b/specifications/retryable-reads/tests/legacy/findOne-serverErrors.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/findOne-serverErrors.yml rename to specifications/retryable-reads/tests/legacy/findOne-serverErrors.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/findOne.json b/specifications/retryable-reads/tests/legacy/findOne.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/findOne.json rename to specifications/retryable-reads/tests/legacy/findOne.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/findOne.yml b/specifications/retryable-reads/tests/legacy/findOne.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/findOne.yml rename to specifications/retryable-reads/tests/legacy/findOne.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/gridfs-download-serverErrors.json b/specifications/retryable-reads/tests/legacy/gridfs-download-serverErrors.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/gridfs-download-serverErrors.json rename to specifications/retryable-reads/tests/legacy/gridfs-download-serverErrors.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/gridfs-download-serverErrors.yml b/specifications/retryable-reads/tests/legacy/gridfs-download-serverErrors.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/gridfs-download-serverErrors.yml rename to specifications/retryable-reads/tests/legacy/gridfs-download-serverErrors.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/gridfs-download.json b/specifications/retryable-reads/tests/legacy/gridfs-download.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/gridfs-download.json rename to specifications/retryable-reads/tests/legacy/gridfs-download.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/gridfs-download.yml b/specifications/retryable-reads/tests/legacy/gridfs-download.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/gridfs-download.yml rename to specifications/retryable-reads/tests/legacy/gridfs-download.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/gridfs-downloadByName-serverErrors.json b/specifications/retryable-reads/tests/legacy/gridfs-downloadByName-serverErrors.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/gridfs-downloadByName-serverErrors.json rename to specifications/retryable-reads/tests/legacy/gridfs-downloadByName-serverErrors.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/gridfs-downloadByName-serverErrors.yml b/specifications/retryable-reads/tests/legacy/gridfs-downloadByName-serverErrors.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/gridfs-downloadByName-serverErrors.yml rename to specifications/retryable-reads/tests/legacy/gridfs-downloadByName-serverErrors.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/gridfs-downloadByName.json b/specifications/retryable-reads/tests/legacy/gridfs-downloadByName.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/gridfs-downloadByName.json rename to specifications/retryable-reads/tests/legacy/gridfs-downloadByName.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/gridfs-downloadByName.yml b/specifications/retryable-reads/tests/legacy/gridfs-downloadByName.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/gridfs-downloadByName.yml rename to specifications/retryable-reads/tests/legacy/gridfs-downloadByName.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listCollectionNames-serverErrors.json b/specifications/retryable-reads/tests/legacy/listCollectionNames-serverErrors.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listCollectionNames-serverErrors.json rename to specifications/retryable-reads/tests/legacy/listCollectionNames-serverErrors.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listCollectionNames-serverErrors.yml b/specifications/retryable-reads/tests/legacy/listCollectionNames-serverErrors.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listCollectionNames-serverErrors.yml rename to specifications/retryable-reads/tests/legacy/listCollectionNames-serverErrors.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listCollectionNames.json b/specifications/retryable-reads/tests/legacy/listCollectionNames.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listCollectionNames.json rename to specifications/retryable-reads/tests/legacy/listCollectionNames.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listCollectionNames.yml b/specifications/retryable-reads/tests/legacy/listCollectionNames.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listCollectionNames.yml rename to specifications/retryable-reads/tests/legacy/listCollectionNames.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listCollectionObjects-serverErrors.json b/specifications/retryable-reads/tests/legacy/listCollectionObjects-serverErrors.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listCollectionObjects-serverErrors.json rename to specifications/retryable-reads/tests/legacy/listCollectionObjects-serverErrors.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listCollectionObjects-serverErrors.yml b/specifications/retryable-reads/tests/legacy/listCollectionObjects-serverErrors.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listCollectionObjects-serverErrors.yml rename to specifications/retryable-reads/tests/legacy/listCollectionObjects-serverErrors.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listCollectionObjects.json b/specifications/retryable-reads/tests/legacy/listCollectionObjects.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listCollectionObjects.json rename to specifications/retryable-reads/tests/legacy/listCollectionObjects.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listCollectionObjects.yml b/specifications/retryable-reads/tests/legacy/listCollectionObjects.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listCollectionObjects.yml rename to specifications/retryable-reads/tests/legacy/listCollectionObjects.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listCollections-serverErrors.json b/specifications/retryable-reads/tests/legacy/listCollections-serverErrors.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listCollections-serverErrors.json rename to specifications/retryable-reads/tests/legacy/listCollections-serverErrors.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listCollections-serverErrors.yml b/specifications/retryable-reads/tests/legacy/listCollections-serverErrors.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listCollections-serverErrors.yml rename to specifications/retryable-reads/tests/legacy/listCollections-serverErrors.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listCollections.json b/specifications/retryable-reads/tests/legacy/listCollections.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listCollections.json rename to specifications/retryable-reads/tests/legacy/listCollections.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listCollections.yml b/specifications/retryable-reads/tests/legacy/listCollections.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listCollections.yml rename to specifications/retryable-reads/tests/legacy/listCollections.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listDatabaseNames-serverErrors.json b/specifications/retryable-reads/tests/legacy/listDatabaseNames-serverErrors.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listDatabaseNames-serverErrors.json rename to specifications/retryable-reads/tests/legacy/listDatabaseNames-serverErrors.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listDatabaseNames-serverErrors.yml b/specifications/retryable-reads/tests/legacy/listDatabaseNames-serverErrors.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listDatabaseNames-serverErrors.yml rename to specifications/retryable-reads/tests/legacy/listDatabaseNames-serverErrors.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listDatabaseNames.json b/specifications/retryable-reads/tests/legacy/listDatabaseNames.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listDatabaseNames.json rename to specifications/retryable-reads/tests/legacy/listDatabaseNames.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listDatabaseNames.yml b/specifications/retryable-reads/tests/legacy/listDatabaseNames.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listDatabaseNames.yml rename to specifications/retryable-reads/tests/legacy/listDatabaseNames.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listDatabaseObjects-serverErrors.json b/specifications/retryable-reads/tests/legacy/listDatabaseObjects-serverErrors.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listDatabaseObjects-serverErrors.json rename to specifications/retryable-reads/tests/legacy/listDatabaseObjects-serverErrors.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listDatabaseObjects-serverErrors.yml b/specifications/retryable-reads/tests/legacy/listDatabaseObjects-serverErrors.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listDatabaseObjects-serverErrors.yml rename to specifications/retryable-reads/tests/legacy/listDatabaseObjects-serverErrors.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listDatabaseObjects.json b/specifications/retryable-reads/tests/legacy/listDatabaseObjects.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listDatabaseObjects.json rename to specifications/retryable-reads/tests/legacy/listDatabaseObjects.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listDatabaseObjects.yml b/specifications/retryable-reads/tests/legacy/listDatabaseObjects.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listDatabaseObjects.yml rename to specifications/retryable-reads/tests/legacy/listDatabaseObjects.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listDatabases-serverErrors.json b/specifications/retryable-reads/tests/legacy/listDatabases-serverErrors.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listDatabases-serverErrors.json rename to specifications/retryable-reads/tests/legacy/listDatabases-serverErrors.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listDatabases-serverErrors.yml b/specifications/retryable-reads/tests/legacy/listDatabases-serverErrors.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listDatabases-serverErrors.yml rename to specifications/retryable-reads/tests/legacy/listDatabases-serverErrors.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listDatabases.json b/specifications/retryable-reads/tests/legacy/listDatabases.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listDatabases.json rename to specifications/retryable-reads/tests/legacy/listDatabases.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listDatabases.yml b/specifications/retryable-reads/tests/legacy/listDatabases.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listDatabases.yml rename to specifications/retryable-reads/tests/legacy/listDatabases.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listIndexNames-serverErrors.json b/specifications/retryable-reads/tests/legacy/listIndexNames-serverErrors.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listIndexNames-serverErrors.json rename to specifications/retryable-reads/tests/legacy/listIndexNames-serverErrors.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listIndexNames-serverErrors.yml b/specifications/retryable-reads/tests/legacy/listIndexNames-serverErrors.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listIndexNames-serverErrors.yml rename to specifications/retryable-reads/tests/legacy/listIndexNames-serverErrors.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listIndexNames.json b/specifications/retryable-reads/tests/legacy/listIndexNames.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listIndexNames.json rename to specifications/retryable-reads/tests/legacy/listIndexNames.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listIndexNames.yml b/specifications/retryable-reads/tests/legacy/listIndexNames.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listIndexNames.yml rename to specifications/retryable-reads/tests/legacy/listIndexNames.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listIndexes-serverErrors.json b/specifications/retryable-reads/tests/legacy/listIndexes-serverErrors.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listIndexes-serverErrors.json rename to specifications/retryable-reads/tests/legacy/listIndexes-serverErrors.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listIndexes-serverErrors.yml b/specifications/retryable-reads/tests/legacy/listIndexes-serverErrors.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listIndexes-serverErrors.yml rename to specifications/retryable-reads/tests/legacy/listIndexes-serverErrors.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listIndexes.json b/specifications/retryable-reads/tests/legacy/listIndexes.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listIndexes.json rename to specifications/retryable-reads/tests/legacy/listIndexes.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listIndexes.yml b/specifications/retryable-reads/tests/legacy/listIndexes.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/listIndexes.yml rename to specifications/retryable-reads/tests/legacy/listIndexes.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/mapReduce.json b/specifications/retryable-reads/tests/legacy/mapReduce.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/mapReduce.json rename to specifications/retryable-reads/tests/legacy/mapReduce.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/mapReduce.yml b/specifications/retryable-reads/tests/legacy/mapReduce.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/legacy/mapReduce.yml rename to specifications/retryable-reads/tests/legacy/mapReduce.yml diff --git a/specifications/retryable-reads/tests/unified/handshakeError.json b/specifications/retryable-reads/tests/unified/handshakeError.json new file mode 100644 index 00000000000..2921d8a9540 --- /dev/null +++ b/specifications/retryable-reads/tests/unified/handshakeError.json @@ -0,0 +1,3079 @@ +{ + "description": "retryable reads handshake failures", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.2", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "auth": true + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "connectionCheckOutStartedEvent", + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-handshake-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "client.listDatabases succeeds after retryable handshake network error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "listDatabases", + "object": "client", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "listDatabases" + } + }, + { + "commandSucceededEvent": { + "commandName": "listDatabases" + } + } + ] + } + ] + }, + { + "description": "client.listDatabases succeeds after retryable handshake server error (ShutdownInProgress)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "listDatabases", + "object": "client", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "listDatabases" + } + }, + { + "commandSucceededEvent": { + "commandName": "listDatabases" + } + } + ] + } + ] + }, + { + "description": "client.listDatabaseNames succeeds after retryable handshake network error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "listDatabaseNames", + "object": "client" + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "listDatabases" + } + }, + { + "commandSucceededEvent": { + "commandName": "listDatabases" + } + } + ] + } + ] + }, + { + "description": "client.listDatabaseNames succeeds after retryable handshake server error (ShutdownInProgress)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "listDatabaseNames", + "object": "client" + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "listDatabases" + } + }, + { + "commandSucceededEvent": { + "commandName": "listDatabases" + } + } + ] + } + ] + }, + { + "description": "client.createChangeStream succeeds after retryable handshake network error", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "createChangeStream", + "object": "client", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream" + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandSucceededEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "client.createChangeStream succeeds after retryable handshake server error (ShutdownInProgress)", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "createChangeStream", + "object": "client", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream" + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandSucceededEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "database.aggregate succeeds after retryable handshake network error", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "aggregate", + "object": "database", + "arguments": { + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandSucceededEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "database.aggregate succeeds after retryable handshake server error (ShutdownInProgress)", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "aggregate", + "object": "database", + "arguments": { + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandSucceededEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "database.listCollections succeeds after retryable handshake network error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "listCollections", + "object": "database", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "listCollections" + } + }, + { + "commandSucceededEvent": { + "commandName": "listCollections" + } + } + ] + } + ] + }, + { + "description": "database.listCollections succeeds after retryable handshake server error (ShutdownInProgress)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "listCollections", + "object": "database", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "listCollections" + } + }, + { + "commandSucceededEvent": { + "commandName": "listCollections" + } + } + ] + } + ] + }, + { + "description": "database.listCollectionNames succeeds after retryable handshake network error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "listCollectionNames", + "object": "database", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "listCollections" + } + }, + { + "commandSucceededEvent": { + "commandName": "listCollections" + } + } + ] + } + ] + }, + { + "description": "database.listCollectionNames succeeds after retryable handshake server error (ShutdownInProgress)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "listCollectionNames", + "object": "database", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "listCollections" + } + }, + { + "commandSucceededEvent": { + "commandName": "listCollections" + } + } + ] + } + ] + }, + { + "description": "database.createChangeStream succeeds after retryable handshake network error", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "createChangeStream", + "object": "database", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream" + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandSucceededEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "database.createChangeStream succeeds after retryable handshake server error (ShutdownInProgress)", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "createChangeStream", + "object": "database", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream" + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandSucceededEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "collection.aggregate succeeds after retryable handshake network error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandSucceededEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "collection.aggregate succeeds after retryable handshake server error (ShutdownInProgress)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandSucceededEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "collection.countDocuments succeeds after retryable handshake network error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandSucceededEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "collection.countDocuments succeeds after retryable handshake server error (ShutdownInProgress)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandSucceededEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "collection.estimatedDocumentCount succeeds after retryable handshake network error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "estimatedDocumentCount", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "count" + } + }, + { + "commandSucceededEvent": { + "commandName": "count" + } + } + ] + } + ] + }, + { + "description": "collection.estimatedDocumentCount succeeds after retryable handshake server error (ShutdownInProgress)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "estimatedDocumentCount", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "count" + } + }, + { + "commandSucceededEvent": { + "commandName": "count" + } + } + ] + } + ] + }, + { + "description": "collection.distinct succeeds after retryable handshake network error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "distinct", + "object": "collection", + "arguments": { + "fieldName": "x", + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "distinct" + } + }, + { + "commandSucceededEvent": { + "commandName": "distinct" + } + } + ] + } + ] + }, + { + "description": "collection.distinct succeeds after retryable handshake server error (ShutdownInProgress)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "distinct", + "object": "collection", + "arguments": { + "fieldName": "x", + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "distinct" + } + }, + { + "commandSucceededEvent": { + "commandName": "distinct" + } + } + ] + } + ] + }, + { + "description": "collection.find succeeds after retryable handshake network error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "find" + } + }, + { + "commandSucceededEvent": { + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "collection.find succeeds after retryable handshake server error (ShutdownInProgress)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "find" + } + }, + { + "commandSucceededEvent": { + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "collection.findOne succeeds after retryable handshake network error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "findOne", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "find" + } + }, + { + "commandSucceededEvent": { + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "collection.findOne succeeds after retryable handshake server error (ShutdownInProgress)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "findOne", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "find" + } + }, + { + "commandSucceededEvent": { + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "collection.listIndexes succeeds after retryable handshake network error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "listIndexes", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "listIndexes" + } + }, + { + "commandSucceededEvent": { + "commandName": "listIndexes" + } + } + ] + } + ] + }, + { + "description": "collection.listIndexes succeeds after retryable handshake server error (ShutdownInProgress)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "listIndexes", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "listIndexes" + } + }, + { + "commandSucceededEvent": { + "commandName": "listIndexes" + } + } + ] + } + ] + }, + { + "description": "collection.listIndexNames succeeds after retryable handshake network error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "listIndexNames", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "listIndexes" + } + }, + { + "commandSucceededEvent": { + "commandName": "listIndexes" + } + } + ] + } + ] + }, + { + "description": "collection.listIndexNames succeeds after retryable handshake server error (ShutdownInProgress)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "listIndexNames", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "listIndexes" + } + }, + { + "commandSucceededEvent": { + "commandName": "listIndexes" + } + } + ] + } + ] + }, + { + "description": "collection.createChangeStream succeeds after retryable handshake network error", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "createChangeStream", + "object": "collection", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream" + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandSucceededEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "collection.createChangeStream succeeds after retryable handshake server error (ShutdownInProgress)", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "createChangeStream", + "object": "collection", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream" + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandSucceededEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + } + ] +} diff --git a/specifications/retryable-reads/tests/unified/handshakeError.yml b/specifications/retryable-reads/tests/unified/handshakeError.yml new file mode 100644 index 00000000000..f2b1ec982ce --- /dev/null +++ b/specifications/retryable-reads/tests/unified/handshakeError.yml @@ -0,0 +1,1342 @@ +# Tests in this file are generated from handshakeError.yml.template. + +description: "retryable reads handshake failures" + +# 1.4 is required for "serverless: forbid". +schemaVersion: "1.4" + +runOnRequirements: + - minServerVersion: "4.2" + topologies: [replicaset, sharded, load-balanced] + auth: true + +createEntities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - connectionCheckOutStartedEvent + - commandStartedEvent + - commandSucceededEvent + - commandFailedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName retryable-reads-handshake-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +tests: + # Because setting a failPoint creates a connection in the connection pool, run + # a ping operation that fails immediately after the failPoint operation in + # order to discard the connection before running the actual operation to be + # tested. The saslContinue command is used to avoid SDAM errors. + # + # Description of events: + # - Failpoint operation. + # - Creates a connection in the connection pool that must be closed. + # - Ping operation. + # - Triggers failpoint (first time). + # - Closes the connection made by the fail point operation. + # - Test operation. + # - New connection is created. + # - Triggers failpoint (second time). + # - Tests whether operation successfully retries the handshake and succeeds. + + - description: "client.listDatabases succeeds after retryable handshake network error" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: listDatabases + object: *client + arguments: + filter: {} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: listDatabases + - commandSucceededEvent: + commandName: listDatabases + + - description: "client.listDatabases succeeds after retryable handshake server error (ShutdownInProgress)" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: listDatabases + object: *client + arguments: + filter: {} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: listDatabases + - commandSucceededEvent: + commandName: listDatabases + + - description: "client.listDatabaseNames succeeds after retryable handshake network error" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: listDatabaseNames + object: *client + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: listDatabases + - commandSucceededEvent: + commandName: listDatabases + + - description: "client.listDatabaseNames succeeds after retryable handshake server error (ShutdownInProgress)" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: listDatabaseNames + object: *client + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: listDatabases + - commandSucceededEvent: + commandName: listDatabases + + - description: "client.createChangeStream succeeds after retryable handshake network error" + runOnRequirements: + - serverless: forbid + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: createChangeStream + object: *client + arguments: + pipeline: [] + saveResultAsEntity: changeStream + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: aggregate + - commandSucceededEvent: + commandName: aggregate + + - description: "client.createChangeStream succeeds after retryable handshake server error (ShutdownInProgress)" + runOnRequirements: + - serverless: forbid + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: createChangeStream + object: *client + arguments: + pipeline: [] + saveResultAsEntity: changeStream + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: aggregate + - commandSucceededEvent: + commandName: aggregate + + - description: "database.aggregate succeeds after retryable handshake network error" + runOnRequirements: + - serverless: forbid + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: aggregate + object: *database + arguments: + pipeline: [ { $listLocalSessions: {} }, { $limit: 1 } ] + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: aggregate + - commandSucceededEvent: + commandName: aggregate + + - description: "database.aggregate succeeds after retryable handshake server error (ShutdownInProgress)" + runOnRequirements: + - serverless: forbid + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: aggregate + object: *database + arguments: + pipeline: [ { $listLocalSessions: {} }, { $limit: 1 } ] + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: aggregate + - commandSucceededEvent: + commandName: aggregate + + - description: "database.listCollections succeeds after retryable handshake network error" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: listCollections + object: *database + arguments: + filter: {} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: listCollections + - commandSucceededEvent: + commandName: listCollections + + - description: "database.listCollections succeeds after retryable handshake server error (ShutdownInProgress)" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: listCollections + object: *database + arguments: + filter: {} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: listCollections + - commandSucceededEvent: + commandName: listCollections + + - description: "database.listCollectionNames succeeds after retryable handshake network error" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: listCollectionNames + object: *database + arguments: + filter: {} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: listCollections + - commandSucceededEvent: + commandName: listCollections + + - description: "database.listCollectionNames succeeds after retryable handshake server error (ShutdownInProgress)" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: listCollectionNames + object: *database + arguments: + filter: {} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: listCollections + - commandSucceededEvent: + commandName: listCollections + + - description: "database.createChangeStream succeeds after retryable handshake network error" + runOnRequirements: + - serverless: forbid + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: createChangeStream + object: *database + arguments: + pipeline: [] + saveResultAsEntity: changeStream + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: aggregate + - commandSucceededEvent: + commandName: aggregate + + - description: "database.createChangeStream succeeds after retryable handshake server error (ShutdownInProgress)" + runOnRequirements: + - serverless: forbid + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: createChangeStream + object: *database + arguments: + pipeline: [] + saveResultAsEntity: changeStream + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: aggregate + - commandSucceededEvent: + commandName: aggregate + + - description: "collection.aggregate succeeds after retryable handshake network error" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: aggregate + object: *collection + arguments: + pipeline: [] + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: aggregate + - commandSucceededEvent: + commandName: aggregate + + - description: "collection.aggregate succeeds after retryable handshake server error (ShutdownInProgress)" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: aggregate + object: *collection + arguments: + pipeline: [] + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: aggregate + - commandSucceededEvent: + commandName: aggregate + + - description: "collection.countDocuments succeeds after retryable handshake network error" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: countDocuments + object: *collection + arguments: + filter: {} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: aggregate + - commandSucceededEvent: + commandName: aggregate + + - description: "collection.countDocuments succeeds after retryable handshake server error (ShutdownInProgress)" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: countDocuments + object: *collection + arguments: + filter: {} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: aggregate + - commandSucceededEvent: + commandName: aggregate + + - description: "collection.estimatedDocumentCount succeeds after retryable handshake network error" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: estimatedDocumentCount + object: *collection + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: count + - commandSucceededEvent: + commandName: count + + - description: "collection.estimatedDocumentCount succeeds after retryable handshake server error (ShutdownInProgress)" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: estimatedDocumentCount + object: *collection + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: count + - commandSucceededEvent: + commandName: count + + - description: "collection.distinct succeeds after retryable handshake network error" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: distinct + object: *collection + arguments: + fieldName: x + filter: {} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: distinct + - commandSucceededEvent: + commandName: distinct + + - description: "collection.distinct succeeds after retryable handshake server error (ShutdownInProgress)" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: distinct + object: *collection + arguments: + fieldName: x + filter: {} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: distinct + - commandSucceededEvent: + commandName: distinct + + - description: "collection.find succeeds after retryable handshake network error" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: find + object: *collection + arguments: + filter: {} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: find + - commandSucceededEvent: + commandName: find + + - description: "collection.find succeeds after retryable handshake server error (ShutdownInProgress)" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: find + object: *collection + arguments: + filter: {} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: find + - commandSucceededEvent: + commandName: find + + - description: "collection.findOne succeeds after retryable handshake network error" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: findOne + object: *collection + arguments: + filter: {} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: find + - commandSucceededEvent: + commandName: find + + - description: "collection.findOne succeeds after retryable handshake server error (ShutdownInProgress)" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: findOne + object: *collection + arguments: + filter: {} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: find + - commandSucceededEvent: + commandName: find + + - description: "collection.listIndexes succeeds after retryable handshake network error" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: listIndexes + object: *collection + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: listIndexes + - commandSucceededEvent: + commandName: listIndexes + + - description: "collection.listIndexes succeeds after retryable handshake server error (ShutdownInProgress)" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: listIndexes + object: *collection + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: listIndexes + - commandSucceededEvent: + commandName: listIndexes + + - description: "collection.listIndexNames succeeds after retryable handshake network error" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: listIndexNames + object: *collection + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: listIndexes + - commandSucceededEvent: + commandName: listIndexes + + - description: "collection.listIndexNames succeeds after retryable handshake server error (ShutdownInProgress)" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: listIndexNames + object: *collection + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: listIndexes + - commandSucceededEvent: + commandName: listIndexes + + - description: "collection.createChangeStream succeeds after retryable handshake network error" + runOnRequirements: + - serverless: forbid + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + saveResultAsEntity: changeStream + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: aggregate + - commandSucceededEvent: + commandName: aggregate + + - description: "collection.createChangeStream succeeds after retryable handshake server error (ShutdownInProgress)" + runOnRequirements: + - serverless: forbid + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + saveResultAsEntity: changeStream + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: aggregate + - commandSucceededEvent: + commandName: aggregate diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/README.rst b/specifications/retryable-writes/tests/legacy/README.rst similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/README.rst rename to specifications/retryable-writes/tests/legacy/README.rst diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/bulkWrite-errorLabels.json b/specifications/retryable-writes/tests/legacy/bulkWrite-errorLabels.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/bulkWrite-errorLabels.json rename to specifications/retryable-writes/tests/legacy/bulkWrite-errorLabels.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/bulkWrite-errorLabels.yml b/specifications/retryable-writes/tests/legacy/bulkWrite-errorLabels.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/bulkWrite-errorLabels.yml rename to specifications/retryable-writes/tests/legacy/bulkWrite-errorLabels.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/bulkWrite-serverErrors.json b/specifications/retryable-writes/tests/legacy/bulkWrite-serverErrors.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/bulkWrite-serverErrors.json rename to specifications/retryable-writes/tests/legacy/bulkWrite-serverErrors.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/bulkWrite-serverErrors.yml b/specifications/retryable-writes/tests/legacy/bulkWrite-serverErrors.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/bulkWrite-serverErrors.yml rename to specifications/retryable-writes/tests/legacy/bulkWrite-serverErrors.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/bulkWrite.json b/specifications/retryable-writes/tests/legacy/bulkWrite.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/bulkWrite.json rename to specifications/retryable-writes/tests/legacy/bulkWrite.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/bulkWrite.yml b/specifications/retryable-writes/tests/legacy/bulkWrite.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/bulkWrite.yml rename to specifications/retryable-writes/tests/legacy/bulkWrite.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/deleteMany.json b/specifications/retryable-writes/tests/legacy/deleteMany.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/deleteMany.json rename to specifications/retryable-writes/tests/legacy/deleteMany.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/deleteMany.yml b/specifications/retryable-writes/tests/legacy/deleteMany.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/deleteMany.yml rename to specifications/retryable-writes/tests/legacy/deleteMany.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/deleteOne-errorLabels.json b/specifications/retryable-writes/tests/legacy/deleteOne-errorLabels.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/deleteOne-errorLabels.json rename to specifications/retryable-writes/tests/legacy/deleteOne-errorLabels.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/deleteOne-errorLabels.yml b/specifications/retryable-writes/tests/legacy/deleteOne-errorLabels.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/deleteOne-errorLabels.yml rename to specifications/retryable-writes/tests/legacy/deleteOne-errorLabels.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/deleteOne-serverErrors.json b/specifications/retryable-writes/tests/legacy/deleteOne-serverErrors.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/deleteOne-serverErrors.json rename to specifications/retryable-writes/tests/legacy/deleteOne-serverErrors.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/deleteOne-serverErrors.yml b/specifications/retryable-writes/tests/legacy/deleteOne-serverErrors.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/deleteOne-serverErrors.yml rename to specifications/retryable-writes/tests/legacy/deleteOne-serverErrors.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/deleteOne.json b/specifications/retryable-writes/tests/legacy/deleteOne.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/deleteOne.json rename to specifications/retryable-writes/tests/legacy/deleteOne.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/deleteOne.yml b/specifications/retryable-writes/tests/legacy/deleteOne.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/deleteOne.yml rename to specifications/retryable-writes/tests/legacy/deleteOne.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/findOneAndDelete-errorLabels.json b/specifications/retryable-writes/tests/legacy/findOneAndDelete-errorLabels.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/findOneAndDelete-errorLabels.json rename to specifications/retryable-writes/tests/legacy/findOneAndDelete-errorLabels.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/findOneAndDelete-errorLabels.yml b/specifications/retryable-writes/tests/legacy/findOneAndDelete-errorLabels.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/findOneAndDelete-errorLabels.yml rename to specifications/retryable-writes/tests/legacy/findOneAndDelete-errorLabels.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/findOneAndDelete-serverErrors.json b/specifications/retryable-writes/tests/legacy/findOneAndDelete-serverErrors.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/findOneAndDelete-serverErrors.json rename to specifications/retryable-writes/tests/legacy/findOneAndDelete-serverErrors.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/findOneAndDelete-serverErrors.yml b/specifications/retryable-writes/tests/legacy/findOneAndDelete-serverErrors.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/findOneAndDelete-serverErrors.yml rename to specifications/retryable-writes/tests/legacy/findOneAndDelete-serverErrors.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/findOneAndDelete.json b/specifications/retryable-writes/tests/legacy/findOneAndDelete.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/findOneAndDelete.json rename to specifications/retryable-writes/tests/legacy/findOneAndDelete.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/findOneAndDelete.yml b/specifications/retryable-writes/tests/legacy/findOneAndDelete.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/findOneAndDelete.yml rename to specifications/retryable-writes/tests/legacy/findOneAndDelete.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/findOneAndReplace-errorLabels.json b/specifications/retryable-writes/tests/legacy/findOneAndReplace-errorLabels.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/findOneAndReplace-errorLabels.json rename to specifications/retryable-writes/tests/legacy/findOneAndReplace-errorLabels.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/findOneAndReplace-errorLabels.yml b/specifications/retryable-writes/tests/legacy/findOneAndReplace-errorLabels.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/findOneAndReplace-errorLabels.yml rename to specifications/retryable-writes/tests/legacy/findOneAndReplace-errorLabels.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/findOneAndReplace-serverErrors.json b/specifications/retryable-writes/tests/legacy/findOneAndReplace-serverErrors.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/findOneAndReplace-serverErrors.json rename to specifications/retryable-writes/tests/legacy/findOneAndReplace-serverErrors.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/findOneAndReplace-serverErrors.yml b/specifications/retryable-writes/tests/legacy/findOneAndReplace-serverErrors.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/findOneAndReplace-serverErrors.yml rename to specifications/retryable-writes/tests/legacy/findOneAndReplace-serverErrors.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/findOneAndReplace.json b/specifications/retryable-writes/tests/legacy/findOneAndReplace.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/findOneAndReplace.json rename to specifications/retryable-writes/tests/legacy/findOneAndReplace.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/findOneAndReplace.yml b/specifications/retryable-writes/tests/legacy/findOneAndReplace.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/findOneAndReplace.yml rename to specifications/retryable-writes/tests/legacy/findOneAndReplace.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/findOneAndUpdate-errorLabels.json b/specifications/retryable-writes/tests/legacy/findOneAndUpdate-errorLabels.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/findOneAndUpdate-errorLabels.json rename to specifications/retryable-writes/tests/legacy/findOneAndUpdate-errorLabels.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/findOneAndUpdate-errorLabels.yml b/specifications/retryable-writes/tests/legacy/findOneAndUpdate-errorLabels.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/findOneAndUpdate-errorLabels.yml rename to specifications/retryable-writes/tests/legacy/findOneAndUpdate-errorLabels.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/findOneAndUpdate-serverErrors.json b/specifications/retryable-writes/tests/legacy/findOneAndUpdate-serverErrors.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/findOneAndUpdate-serverErrors.json rename to specifications/retryable-writes/tests/legacy/findOneAndUpdate-serverErrors.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/findOneAndUpdate-serverErrors.yml b/specifications/retryable-writes/tests/legacy/findOneAndUpdate-serverErrors.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/findOneAndUpdate-serverErrors.yml rename to specifications/retryable-writes/tests/legacy/findOneAndUpdate-serverErrors.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/findOneAndUpdate.json b/specifications/retryable-writes/tests/legacy/findOneAndUpdate.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/findOneAndUpdate.json rename to specifications/retryable-writes/tests/legacy/findOneAndUpdate.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/findOneAndUpdate.yml b/specifications/retryable-writes/tests/legacy/findOneAndUpdate.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/findOneAndUpdate.yml rename to specifications/retryable-writes/tests/legacy/findOneAndUpdate.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/insertMany-errorLabels.json b/specifications/retryable-writes/tests/legacy/insertMany-errorLabels.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/insertMany-errorLabels.json rename to specifications/retryable-writes/tests/legacy/insertMany-errorLabels.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/insertMany-errorLabels.yml b/specifications/retryable-writes/tests/legacy/insertMany-errorLabels.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/insertMany-errorLabels.yml rename to specifications/retryable-writes/tests/legacy/insertMany-errorLabels.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/insertMany-serverErrors.json b/specifications/retryable-writes/tests/legacy/insertMany-serverErrors.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/insertMany-serverErrors.json rename to specifications/retryable-writes/tests/legacy/insertMany-serverErrors.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/insertMany-serverErrors.yml b/specifications/retryable-writes/tests/legacy/insertMany-serverErrors.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/insertMany-serverErrors.yml rename to specifications/retryable-writes/tests/legacy/insertMany-serverErrors.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/insertMany.json b/specifications/retryable-writes/tests/legacy/insertMany.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/insertMany.json rename to specifications/retryable-writes/tests/legacy/insertMany.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/insertMany.yml b/specifications/retryable-writes/tests/legacy/insertMany.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/insertMany.yml rename to specifications/retryable-writes/tests/legacy/insertMany.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/insertOne-errorLabels.json b/specifications/retryable-writes/tests/legacy/insertOne-errorLabels.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/insertOne-errorLabels.json rename to specifications/retryable-writes/tests/legacy/insertOne-errorLabels.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/insertOne-errorLabels.yml b/specifications/retryable-writes/tests/legacy/insertOne-errorLabels.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/insertOne-errorLabels.yml rename to specifications/retryable-writes/tests/legacy/insertOne-errorLabels.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/insertOne-serverErrors.json b/specifications/retryable-writes/tests/legacy/insertOne-serverErrors.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/insertOne-serverErrors.json rename to specifications/retryable-writes/tests/legacy/insertOne-serverErrors.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/insertOne-serverErrors.yml b/specifications/retryable-writes/tests/legacy/insertOne-serverErrors.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/insertOne-serverErrors.yml rename to specifications/retryable-writes/tests/legacy/insertOne-serverErrors.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/insertOne.json b/specifications/retryable-writes/tests/legacy/insertOne.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/insertOne.json rename to specifications/retryable-writes/tests/legacy/insertOne.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/insertOne.yml b/specifications/retryable-writes/tests/legacy/insertOne.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/insertOne.yml rename to specifications/retryable-writes/tests/legacy/insertOne.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/replaceOne-errorLabels.json b/specifications/retryable-writes/tests/legacy/replaceOne-errorLabels.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/replaceOne-errorLabels.json rename to specifications/retryable-writes/tests/legacy/replaceOne-errorLabels.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/replaceOne-errorLabels.yml b/specifications/retryable-writes/tests/legacy/replaceOne-errorLabels.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/replaceOne-errorLabels.yml rename to specifications/retryable-writes/tests/legacy/replaceOne-errorLabels.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/replaceOne-serverErrors.json b/specifications/retryable-writes/tests/legacy/replaceOne-serverErrors.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/replaceOne-serverErrors.json rename to specifications/retryable-writes/tests/legacy/replaceOne-serverErrors.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/replaceOne-serverErrors.yml b/specifications/retryable-writes/tests/legacy/replaceOne-serverErrors.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/replaceOne-serverErrors.yml rename to specifications/retryable-writes/tests/legacy/replaceOne-serverErrors.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/replaceOne.json b/specifications/retryable-writes/tests/legacy/replaceOne.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/replaceOne.json rename to specifications/retryable-writes/tests/legacy/replaceOne.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/replaceOne.yml b/specifications/retryable-writes/tests/legacy/replaceOne.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/replaceOne.yml rename to specifications/retryable-writes/tests/legacy/replaceOne.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/updateMany.json b/specifications/retryable-writes/tests/legacy/updateMany.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/updateMany.json rename to specifications/retryable-writes/tests/legacy/updateMany.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/updateMany.yml b/specifications/retryable-writes/tests/legacy/updateMany.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/updateMany.yml rename to specifications/retryable-writes/tests/legacy/updateMany.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/updateOne-errorLabels.json b/specifications/retryable-writes/tests/legacy/updateOne-errorLabels.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/updateOne-errorLabels.json rename to specifications/retryable-writes/tests/legacy/updateOne-errorLabels.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/updateOne-errorLabels.yml b/specifications/retryable-writes/tests/legacy/updateOne-errorLabels.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/updateOne-errorLabels.yml rename to specifications/retryable-writes/tests/legacy/updateOne-errorLabels.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/updateOne-serverErrors.json b/specifications/retryable-writes/tests/legacy/updateOne-serverErrors.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/updateOne-serverErrors.json rename to specifications/retryable-writes/tests/legacy/updateOne-serverErrors.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/updateOne-serverErrors.yml b/specifications/retryable-writes/tests/legacy/updateOne-serverErrors.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/updateOne-serverErrors.yml rename to specifications/retryable-writes/tests/legacy/updateOne-serverErrors.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/updateOne.json b/specifications/retryable-writes/tests/legacy/updateOne.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/updateOne.json rename to specifications/retryable-writes/tests/legacy/updateOne.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/updateOne.yml b/specifications/retryable-writes/tests/legacy/updateOne.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/legacy/updateOne.yml rename to specifications/retryable-writes/tests/legacy/updateOne.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/unified/bulkWrite-serverErrors.json b/specifications/retryable-writes/tests/unified/bulkWrite-serverErrors.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/unified/bulkWrite-serverErrors.json rename to specifications/retryable-writes/tests/unified/bulkWrite-serverErrors.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/unified/bulkWrite-serverErrors.yml b/specifications/retryable-writes/tests/unified/bulkWrite-serverErrors.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/unified/bulkWrite-serverErrors.yml rename to specifications/retryable-writes/tests/unified/bulkWrite-serverErrors.yml diff --git a/specifications/retryable-writes/tests/unified/handshakeError.json b/specifications/retryable-writes/tests/unified/handshakeError.json new file mode 100644 index 00000000000..df37bd72322 --- /dev/null +++ b/specifications/retryable-writes/tests/unified/handshakeError.json @@ -0,0 +1,1797 @@ +{ + "description": "retryable writes handshake failures", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.2", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "auth": true + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "connectionCheckOutStartedEvent", + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-handshake-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "collection.insertOne succeeds after retryable handshake network error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 2, + "x": 22 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandSucceededEvent": { + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "collection.insertOne succeeds after retryable handshake server error (ShutdownInProgress)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 2, + "x": 22 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandSucceededEvent": { + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "collection.insertMany succeeds after retryable handshake network error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 2, + "x": 22 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandSucceededEvent": { + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "collection.insertMany succeeds after retryable handshake server error (ShutdownInProgress)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 2, + "x": 22 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandSucceededEvent": { + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "collection.deleteOne succeeds after retryable handshake network error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "deleteOne", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "delete" + } + }, + { + "commandSucceededEvent": { + "commandName": "delete" + } + } + ] + } + ] + }, + { + "description": "collection.deleteOne succeeds after retryable handshake server error (ShutdownInProgress)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "deleteOne", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "delete" + } + }, + { + "commandSucceededEvent": { + "commandName": "delete" + } + } + ] + } + ] + }, + { + "description": "collection.replaceOne succeeds after retryable handshake network error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "replaceOne", + "object": "collection", + "arguments": { + "filter": {}, + "replacement": { + "x": 22 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "update" + } + }, + { + "commandSucceededEvent": { + "commandName": "update" + } + } + ] + } + ] + }, + { + "description": "collection.replaceOne succeeds after retryable handshake server error (ShutdownInProgress)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "replaceOne", + "object": "collection", + "arguments": { + "filter": {}, + "replacement": { + "x": 22 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "update" + } + }, + { + "commandSucceededEvent": { + "commandName": "update" + } + } + ] + } + ] + }, + { + "description": "collection.updateOne succeeds after retryable handshake network error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "updateOne", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 22 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "update" + } + }, + { + "commandSucceededEvent": { + "commandName": "update" + } + } + ] + } + ] + }, + { + "description": "collection.updateOne succeeds after retryable handshake server error (ShutdownInProgress)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "updateOne", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 22 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "update" + } + }, + { + "commandSucceededEvent": { + "commandName": "update" + } + } + ] + } + ] + }, + { + "description": "collection.findOneAndDelete succeeds after retryable handshake network error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "findOneAndDelete", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandSucceededEvent": { + "commandName": "findAndModify" + } + } + ] + } + ] + }, + { + "description": "collection.findOneAndDelete succeeds after retryable handshake server error (ShutdownInProgress)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "findOneAndDelete", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandSucceededEvent": { + "commandName": "findAndModify" + } + } + ] + } + ] + }, + { + "description": "collection.findOneAndReplace succeeds after retryable handshake network error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "findOneAndReplace", + "object": "collection", + "arguments": { + "filter": {}, + "replacement": { + "x": 22 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandSucceededEvent": { + "commandName": "findAndModify" + } + } + ] + } + ] + }, + { + "description": "collection.findOneAndReplace succeeds after retryable handshake server error (ShutdownInProgress)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "findOneAndReplace", + "object": "collection", + "arguments": { + "filter": {}, + "replacement": { + "x": 22 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandSucceededEvent": { + "commandName": "findAndModify" + } + } + ] + } + ] + }, + { + "description": "collection.findOneAndUpdate succeeds after retryable handshake network error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "findOneAndUpdate", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 22 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandSucceededEvent": { + "commandName": "findAndModify" + } + } + ] + } + ] + }, + { + "description": "collection.findOneAndUpdate succeeds after retryable handshake server error (ShutdownInProgress)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "findOneAndUpdate", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 22 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandSucceededEvent": { + "commandName": "findAndModify" + } + } + ] + } + ] + }, + { + "description": "collection.bulkWrite succeeds after retryable handshake network error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "bulkWrite", + "object": "collection", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 2, + "x": 22 + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandSucceededEvent": { + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "collection.bulkWrite succeeds after retryable handshake server error (ShutdownInProgress)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "bulkWrite", + "object": "collection", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 2, + "x": 22 + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandSucceededEvent": { + "commandName": "insert" + } + } + ] + } + ] + } + ] +} diff --git a/specifications/retryable-writes/tests/unified/handshakeError.yml b/specifications/retryable-writes/tests/unified/handshakeError.yml new file mode 100644 index 00000000000..9b2774bc77e --- /dev/null +++ b/specifications/retryable-writes/tests/unified/handshakeError.yml @@ -0,0 +1,785 @@ +# Tests in this file are generated from handshakeError.yml.template. + +description: "retryable writes handshake failures" + +schemaVersion: "1.3" + +runOnRequirements: + - minServerVersion: "4.2" + topologies: [replicaset, sharded, load-balanced] + auth: true + +createEntities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - connectionCheckOutStartedEvent + - commandStartedEvent + - commandSucceededEvent + - commandFailedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName retryable-writes-handshake-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + +tests: + # Because setting a failPoint creates a connection in the connection pool, run + # a ping operation that fails immediately after the failPoint operation in + # order to discard the connection before running the actual operation to be + # tested. The saslContinue command is used to avoid SDAM errors. + # + # Description of events: + # - Failpoint operation. + # - Creates a connection in the connection pool that must be closed. + # - Ping operation. + # - Triggers failpoint (first time). + # - Closes the connection made by the fail point operation. + # - Test operation. + # - New connection is created. + # - Triggers failpoint (second time). + # - Tests whether operation successfully retries the handshake and succeeds. + + - description: "collection.insertOne succeeds after retryable handshake network error" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: insertOne + object: *collection + arguments: + document: { _id: 2, x: 22 } + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: insert + - commandSucceededEvent: + commandName: insert + + - description: "collection.insertOne succeeds after retryable handshake server error (ShutdownInProgress)" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: insertOne + object: *collection + arguments: + document: { _id: 2, x: 22 } + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: insert + - commandSucceededEvent: + commandName: insert + + - description: "collection.insertMany succeeds after retryable handshake network error" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: insertMany + object: *collection + arguments: + documents: + - { _id: 2, x: 22 } + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: insert + - commandSucceededEvent: + commandName: insert + + - description: "collection.insertMany succeeds after retryable handshake server error (ShutdownInProgress)" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: insertMany + object: *collection + arguments: + documents: + - { _id: 2, x: 22 } + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: insert + - commandSucceededEvent: + commandName: insert + + - description: "collection.deleteOne succeeds after retryable handshake network error" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: deleteOne + object: *collection + arguments: + filter: {} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: delete + - commandSucceededEvent: + commandName: delete + + - description: "collection.deleteOne succeeds after retryable handshake server error (ShutdownInProgress)" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: deleteOne + object: *collection + arguments: + filter: {} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: delete + - commandSucceededEvent: + commandName: delete + + - description: "collection.replaceOne succeeds after retryable handshake network error" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: replaceOne + object: *collection + arguments: + filter: {} + replacement: { x: 22 } + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: update + - commandSucceededEvent: + commandName: update + + - description: "collection.replaceOne succeeds after retryable handshake server error (ShutdownInProgress)" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: replaceOne + object: *collection + arguments: + filter: {} + replacement: { x: 22 } + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: update + - commandSucceededEvent: + commandName: update + + - description: "collection.updateOne succeeds after retryable handshake network error" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: updateOne + object: *collection + arguments: + filter: {} + update: { $set: { x: 22 } } + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: update + - commandSucceededEvent: + commandName: update + + - description: "collection.updateOne succeeds after retryable handshake server error (ShutdownInProgress)" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: updateOne + object: *collection + arguments: + filter: {} + update: { $set: { x: 22 } } + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: update + - commandSucceededEvent: + commandName: update + + - description: "collection.findOneAndDelete succeeds after retryable handshake network error" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: findOneAndDelete + object: *collection + arguments: + filter: {} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: findAndModify + - commandSucceededEvent: + commandName: findAndModify + + - description: "collection.findOneAndDelete succeeds after retryable handshake server error (ShutdownInProgress)" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: findOneAndDelete + object: *collection + arguments: + filter: {} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: findAndModify + - commandSucceededEvent: + commandName: findAndModify + + - description: "collection.findOneAndReplace succeeds after retryable handshake network error" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: findOneAndReplace + object: *collection + arguments: + filter: {} + replacement: { x: 22 } + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: findAndModify + - commandSucceededEvent: + commandName: findAndModify + + - description: "collection.findOneAndReplace succeeds after retryable handshake server error (ShutdownInProgress)" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: findOneAndReplace + object: *collection + arguments: + filter: {} + replacement: { x: 22 } + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: findAndModify + - commandSucceededEvent: + commandName: findAndModify + + - description: "collection.findOneAndUpdate succeeds after retryable handshake network error" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: findOneAndUpdate + object: *collection + arguments: + filter: {} + update: { $set: { x: 22 } } + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: findAndModify + - commandSucceededEvent: + commandName: findAndModify + + - description: "collection.findOneAndUpdate succeeds after retryable handshake server error (ShutdownInProgress)" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: findOneAndUpdate + object: *collection + arguments: + filter: {} + update: { $set: { x: 22 } } + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: findAndModify + - commandSucceededEvent: + commandName: findAndModify + + - description: "collection.bulkWrite succeeds after retryable handshake network error" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: bulkWrite + object: *collection + arguments: + requests: + - insertOne: + document: { _id: 2, x: 22 } + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: insert + - commandSucceededEvent: + commandName: insert + + - description: "collection.bulkWrite succeeds after retryable handshake server error (ShutdownInProgress)" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping, saslContinue] + closeConnection: true + - name: runCommand + object: *database + arguments: { commandName: ping, command: { ping: 1 } } + expectError: { isError: true } + - name: bulkWrite + object: *collection + arguments: + requests: + - insertOne: + document: { _id: 2, x: 22 } + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: insert + - commandSucceededEvent: + commandName: insert diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/unified/insertOne-serverErrors.json b/specifications/retryable-writes/tests/unified/insertOne-serverErrors.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/unified/insertOne-serverErrors.json rename to specifications/retryable-writes/tests/unified/insertOne-serverErrors.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/unified/insertOne-serverErrors.yml b/specifications/retryable-writes/tests/unified/insertOne-serverErrors.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/unified/insertOne-serverErrors.yml rename to specifications/retryable-writes/tests/unified/insertOne-serverErrors.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/server-discovery-and-monitoring-tests.rst b/specifications/server-discovery-and-monitoring/server-discovery-and-monitoring-tests.rst similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/server-discovery-and-monitoring-tests.rst rename to specifications/server-discovery-and-monitoring/server-discovery-and-monitoring-tests.rst diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/README.rst b/specifications/server-discovery-and-monitoring/tests/README.rst similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/README.rst rename to specifications/server-discovery-and-monitoring/tests/README.rst diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/error_handling_handshake.json b/specifications/server-discovery-and-monitoring/tests/errors/error_handling_handshake.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/error_handling_handshake.json rename to specifications/server-discovery-and-monitoring/tests/errors/error_handling_handshake.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/error_handling_handshake.yml b/specifications/server-discovery-and-monitoring/tests/errors/error_handling_handshake.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/error_handling_handshake.yml rename to specifications/server-discovery-and-monitoring/tests/errors/error_handling_handshake.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-network-error.json b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-network-error.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-network-error.json rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-network-error.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-network-error.yml b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-network-error.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-network-error.yml rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-network-error.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-network-timeout-error.json b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-network-timeout-error.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-network-timeout-error.json rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-network-timeout-error.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-network-timeout-error.yml b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-network-timeout-error.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-network-timeout-error.yml rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-network-timeout-error.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-InterruptedAtShutdown.json b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-InterruptedAtShutdown.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-InterruptedAtShutdown.json rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-InterruptedAtShutdown.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-InterruptedAtShutdown.yml b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-InterruptedAtShutdown.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-InterruptedAtShutdown.yml rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-InterruptedAtShutdown.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-InterruptedDueToReplStateChange.json b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-InterruptedDueToReplStateChange.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-InterruptedDueToReplStateChange.json rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-InterruptedDueToReplStateChange.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-InterruptedDueToReplStateChange.yml b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-InterruptedDueToReplStateChange.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-InterruptedDueToReplStateChange.yml rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-InterruptedDueToReplStateChange.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-LegacyNotPrimary.json b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-LegacyNotPrimary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-LegacyNotPrimary.json rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-LegacyNotPrimary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-LegacyNotPrimary.yml b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-LegacyNotPrimary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-LegacyNotPrimary.yml rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-LegacyNotPrimary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotPrimaryNoSecondaryOk.json b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotPrimaryNoSecondaryOk.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotPrimaryNoSecondaryOk.json rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotPrimaryNoSecondaryOk.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotPrimaryNoSecondaryOk.yml b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotPrimaryNoSecondaryOk.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotPrimaryNoSecondaryOk.yml rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotPrimaryNoSecondaryOk.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotPrimaryOrSecondary.json b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotPrimaryOrSecondary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotPrimaryOrSecondary.json rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotPrimaryOrSecondary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotPrimaryOrSecondary.yml b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotPrimaryOrSecondary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotPrimaryOrSecondary.yml rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotPrimaryOrSecondary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotWritablePrimary.json b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotWritablePrimary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotWritablePrimary.json rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotWritablePrimary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotWritablePrimary.yml b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotWritablePrimary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotWritablePrimary.yml rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotWritablePrimary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-PrimarySteppedDown.json b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-PrimarySteppedDown.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-PrimarySteppedDown.json rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-PrimarySteppedDown.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-PrimarySteppedDown.yml b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-PrimarySteppedDown.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-PrimarySteppedDown.yml rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-PrimarySteppedDown.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-ShutdownInProgress.json b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-ShutdownInProgress.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-ShutdownInProgress.json rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-ShutdownInProgress.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-ShutdownInProgress.yml b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-ShutdownInProgress.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-ShutdownInProgress.yml rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-ShutdownInProgress.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-InterruptedAtShutdown.json b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-InterruptedAtShutdown.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-InterruptedAtShutdown.json rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-InterruptedAtShutdown.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-InterruptedAtShutdown.yml b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-InterruptedAtShutdown.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-InterruptedAtShutdown.yml rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-InterruptedAtShutdown.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-InterruptedDueToReplStateChange.json b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-InterruptedDueToReplStateChange.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-InterruptedDueToReplStateChange.json rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-InterruptedDueToReplStateChange.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-InterruptedDueToReplStateChange.yml b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-InterruptedDueToReplStateChange.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-InterruptedDueToReplStateChange.yml rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-InterruptedDueToReplStateChange.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-LegacyNotPrimary.json b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-LegacyNotPrimary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-LegacyNotPrimary.json rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-LegacyNotPrimary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-LegacyNotPrimary.yml b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-LegacyNotPrimary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-LegacyNotPrimary.yml rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-LegacyNotPrimary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotPrimaryNoSecondaryOk.json b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotPrimaryNoSecondaryOk.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotPrimaryNoSecondaryOk.json rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotPrimaryNoSecondaryOk.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotPrimaryNoSecondaryOk.yml b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotPrimaryNoSecondaryOk.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotPrimaryNoSecondaryOk.yml rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotPrimaryNoSecondaryOk.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotPrimaryOrSecondary.json b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotPrimaryOrSecondary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotPrimaryOrSecondary.json rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotPrimaryOrSecondary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotPrimaryOrSecondary.yml b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotPrimaryOrSecondary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotPrimaryOrSecondary.yml rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotPrimaryOrSecondary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotWritablePrimary.json b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotWritablePrimary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotWritablePrimary.json rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotWritablePrimary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotWritablePrimary.yml b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotWritablePrimary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotWritablePrimary.yml rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotWritablePrimary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-PrimarySteppedDown.json b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-PrimarySteppedDown.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-PrimarySteppedDown.json rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-PrimarySteppedDown.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-PrimarySteppedDown.yml b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-PrimarySteppedDown.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-PrimarySteppedDown.yml rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-PrimarySteppedDown.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-ShutdownInProgress.json b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-ShutdownInProgress.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-ShutdownInProgress.json rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-ShutdownInProgress.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-ShutdownInProgress.yml b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-ShutdownInProgress.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-ShutdownInProgress.yml rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-ShutdownInProgress.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-InterruptedAtShutdown.json b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-InterruptedAtShutdown.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-InterruptedAtShutdown.json rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-InterruptedAtShutdown.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-InterruptedAtShutdown.yml b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-InterruptedAtShutdown.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-InterruptedAtShutdown.yml rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-InterruptedAtShutdown.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-InterruptedDueToReplStateChange.json b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-InterruptedDueToReplStateChange.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-InterruptedDueToReplStateChange.json rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-InterruptedDueToReplStateChange.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-InterruptedDueToReplStateChange.yml b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-InterruptedDueToReplStateChange.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-InterruptedDueToReplStateChange.yml rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-InterruptedDueToReplStateChange.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-LegacyNotPrimary.json b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-LegacyNotPrimary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-LegacyNotPrimary.json rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-LegacyNotPrimary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-LegacyNotPrimary.yml b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-LegacyNotPrimary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-LegacyNotPrimary.yml rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-LegacyNotPrimary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotPrimaryNoSecondaryOk.json b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotPrimaryNoSecondaryOk.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotPrimaryNoSecondaryOk.json rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotPrimaryNoSecondaryOk.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotPrimaryNoSecondaryOk.yml b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotPrimaryNoSecondaryOk.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotPrimaryNoSecondaryOk.yml rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotPrimaryNoSecondaryOk.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotPrimaryOrSecondary.json b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotPrimaryOrSecondary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotPrimaryOrSecondary.json rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotPrimaryOrSecondary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotPrimaryOrSecondary.yml b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotPrimaryOrSecondary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotPrimaryOrSecondary.yml rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotPrimaryOrSecondary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotWritablePrimary.json b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotWritablePrimary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotWritablePrimary.json rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotWritablePrimary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotWritablePrimary.yml b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotWritablePrimary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotWritablePrimary.yml rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotWritablePrimary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-PrimarySteppedDown.json b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-PrimarySteppedDown.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-PrimarySteppedDown.json rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-PrimarySteppedDown.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-PrimarySteppedDown.yml b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-PrimarySteppedDown.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-PrimarySteppedDown.yml rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-PrimarySteppedDown.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-ShutdownInProgress.json b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-ShutdownInProgress.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-ShutdownInProgress.json rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-ShutdownInProgress.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-ShutdownInProgress.yml b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-ShutdownInProgress.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-ShutdownInProgress.yml rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-ShutdownInProgress.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion.yml.template b/specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion.yml.template similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion.yml.template rename to specifications/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion.yml.template diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/post-42-InterruptedAtShutdown.json b/specifications/server-discovery-and-monitoring/tests/errors/post-42-InterruptedAtShutdown.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/post-42-InterruptedAtShutdown.json rename to specifications/server-discovery-and-monitoring/tests/errors/post-42-InterruptedAtShutdown.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/post-42-InterruptedAtShutdown.yml b/specifications/server-discovery-and-monitoring/tests/errors/post-42-InterruptedAtShutdown.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/post-42-InterruptedAtShutdown.yml rename to specifications/server-discovery-and-monitoring/tests/errors/post-42-InterruptedAtShutdown.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/post-42-InterruptedDueToReplStateChange.json b/specifications/server-discovery-and-monitoring/tests/errors/post-42-InterruptedDueToReplStateChange.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/post-42-InterruptedDueToReplStateChange.json rename to specifications/server-discovery-and-monitoring/tests/errors/post-42-InterruptedDueToReplStateChange.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/post-42-InterruptedDueToReplStateChange.yml b/specifications/server-discovery-and-monitoring/tests/errors/post-42-InterruptedDueToReplStateChange.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/post-42-InterruptedDueToReplStateChange.yml rename to specifications/server-discovery-and-monitoring/tests/errors/post-42-InterruptedDueToReplStateChange.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/post-42-LegacyNotPrimary.json b/specifications/server-discovery-and-monitoring/tests/errors/post-42-LegacyNotPrimary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/post-42-LegacyNotPrimary.json rename to specifications/server-discovery-and-monitoring/tests/errors/post-42-LegacyNotPrimary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/post-42-LegacyNotPrimary.yml b/specifications/server-discovery-and-monitoring/tests/errors/post-42-LegacyNotPrimary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/post-42-LegacyNotPrimary.yml rename to specifications/server-discovery-and-monitoring/tests/errors/post-42-LegacyNotPrimary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/post-42-NotPrimaryNoSecondaryOk.json b/specifications/server-discovery-and-monitoring/tests/errors/post-42-NotPrimaryNoSecondaryOk.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/post-42-NotPrimaryNoSecondaryOk.json rename to specifications/server-discovery-and-monitoring/tests/errors/post-42-NotPrimaryNoSecondaryOk.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/post-42-NotPrimaryNoSecondaryOk.yml b/specifications/server-discovery-and-monitoring/tests/errors/post-42-NotPrimaryNoSecondaryOk.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/post-42-NotPrimaryNoSecondaryOk.yml rename to specifications/server-discovery-and-monitoring/tests/errors/post-42-NotPrimaryNoSecondaryOk.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/post-42-NotPrimaryOrSecondary.json b/specifications/server-discovery-and-monitoring/tests/errors/post-42-NotPrimaryOrSecondary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/post-42-NotPrimaryOrSecondary.json rename to specifications/server-discovery-and-monitoring/tests/errors/post-42-NotPrimaryOrSecondary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/post-42-NotPrimaryOrSecondary.yml b/specifications/server-discovery-and-monitoring/tests/errors/post-42-NotPrimaryOrSecondary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/post-42-NotPrimaryOrSecondary.yml rename to specifications/server-discovery-and-monitoring/tests/errors/post-42-NotPrimaryOrSecondary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/post-42-NotWritablePrimary.json b/specifications/server-discovery-and-monitoring/tests/errors/post-42-NotWritablePrimary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/post-42-NotWritablePrimary.json rename to specifications/server-discovery-and-monitoring/tests/errors/post-42-NotWritablePrimary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/post-42-NotWritablePrimary.yml b/specifications/server-discovery-and-monitoring/tests/errors/post-42-NotWritablePrimary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/post-42-NotWritablePrimary.yml rename to specifications/server-discovery-and-monitoring/tests/errors/post-42-NotWritablePrimary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/post-42-PrimarySteppedDown.json b/specifications/server-discovery-and-monitoring/tests/errors/post-42-PrimarySteppedDown.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/post-42-PrimarySteppedDown.json rename to specifications/server-discovery-and-monitoring/tests/errors/post-42-PrimarySteppedDown.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/post-42-PrimarySteppedDown.yml b/specifications/server-discovery-and-monitoring/tests/errors/post-42-PrimarySteppedDown.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/post-42-PrimarySteppedDown.yml rename to specifications/server-discovery-and-monitoring/tests/errors/post-42-PrimarySteppedDown.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/post-42-ShutdownInProgress.json b/specifications/server-discovery-and-monitoring/tests/errors/post-42-ShutdownInProgress.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/post-42-ShutdownInProgress.json rename to specifications/server-discovery-and-monitoring/tests/errors/post-42-ShutdownInProgress.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/post-42-ShutdownInProgress.yml b/specifications/server-discovery-and-monitoring/tests/errors/post-42-ShutdownInProgress.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/post-42-ShutdownInProgress.yml rename to specifications/server-discovery-and-monitoring/tests/errors/post-42-ShutdownInProgress.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/post-42.yml.template b/specifications/server-discovery-and-monitoring/tests/errors/post-42.yml.template similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/post-42.yml.template rename to specifications/server-discovery-and-monitoring/tests/errors/post-42.yml.template diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/pre-42-InterruptedAtShutdown.json b/specifications/server-discovery-and-monitoring/tests/errors/pre-42-InterruptedAtShutdown.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/pre-42-InterruptedAtShutdown.json rename to specifications/server-discovery-and-monitoring/tests/errors/pre-42-InterruptedAtShutdown.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/pre-42-InterruptedAtShutdown.yml b/specifications/server-discovery-and-monitoring/tests/errors/pre-42-InterruptedAtShutdown.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/pre-42-InterruptedAtShutdown.yml rename to specifications/server-discovery-and-monitoring/tests/errors/pre-42-InterruptedAtShutdown.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/pre-42-InterruptedDueToReplStateChange.json b/specifications/server-discovery-and-monitoring/tests/errors/pre-42-InterruptedDueToReplStateChange.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/pre-42-InterruptedDueToReplStateChange.json rename to specifications/server-discovery-and-monitoring/tests/errors/pre-42-InterruptedDueToReplStateChange.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/pre-42-InterruptedDueToReplStateChange.yml b/specifications/server-discovery-and-monitoring/tests/errors/pre-42-InterruptedDueToReplStateChange.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/pre-42-InterruptedDueToReplStateChange.yml rename to specifications/server-discovery-and-monitoring/tests/errors/pre-42-InterruptedDueToReplStateChange.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/pre-42-LegacyNotPrimary.json b/specifications/server-discovery-and-monitoring/tests/errors/pre-42-LegacyNotPrimary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/pre-42-LegacyNotPrimary.json rename to specifications/server-discovery-and-monitoring/tests/errors/pre-42-LegacyNotPrimary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/pre-42-LegacyNotPrimary.yml b/specifications/server-discovery-and-monitoring/tests/errors/pre-42-LegacyNotPrimary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/pre-42-LegacyNotPrimary.yml rename to specifications/server-discovery-and-monitoring/tests/errors/pre-42-LegacyNotPrimary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/pre-42-NotPrimaryNoSecondaryOk.json b/specifications/server-discovery-and-monitoring/tests/errors/pre-42-NotPrimaryNoSecondaryOk.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/pre-42-NotPrimaryNoSecondaryOk.json rename to specifications/server-discovery-and-monitoring/tests/errors/pre-42-NotPrimaryNoSecondaryOk.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/pre-42-NotPrimaryNoSecondaryOk.yml b/specifications/server-discovery-and-monitoring/tests/errors/pre-42-NotPrimaryNoSecondaryOk.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/pre-42-NotPrimaryNoSecondaryOk.yml rename to specifications/server-discovery-and-monitoring/tests/errors/pre-42-NotPrimaryNoSecondaryOk.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/pre-42-NotPrimaryOrSecondary.json b/specifications/server-discovery-and-monitoring/tests/errors/pre-42-NotPrimaryOrSecondary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/pre-42-NotPrimaryOrSecondary.json rename to specifications/server-discovery-and-monitoring/tests/errors/pre-42-NotPrimaryOrSecondary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/pre-42-NotPrimaryOrSecondary.yml b/specifications/server-discovery-and-monitoring/tests/errors/pre-42-NotPrimaryOrSecondary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/pre-42-NotPrimaryOrSecondary.yml rename to specifications/server-discovery-and-monitoring/tests/errors/pre-42-NotPrimaryOrSecondary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/pre-42-NotWritablePrimary.json b/specifications/server-discovery-and-monitoring/tests/errors/pre-42-NotWritablePrimary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/pre-42-NotWritablePrimary.json rename to specifications/server-discovery-and-monitoring/tests/errors/pre-42-NotWritablePrimary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/pre-42-NotWritablePrimary.yml b/specifications/server-discovery-and-monitoring/tests/errors/pre-42-NotWritablePrimary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/pre-42-NotWritablePrimary.yml rename to specifications/server-discovery-and-monitoring/tests/errors/pre-42-NotWritablePrimary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/pre-42-PrimarySteppedDown.json b/specifications/server-discovery-and-monitoring/tests/errors/pre-42-PrimarySteppedDown.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/pre-42-PrimarySteppedDown.json rename to specifications/server-discovery-and-monitoring/tests/errors/pre-42-PrimarySteppedDown.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/pre-42-PrimarySteppedDown.yml b/specifications/server-discovery-and-monitoring/tests/errors/pre-42-PrimarySteppedDown.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/pre-42-PrimarySteppedDown.yml rename to specifications/server-discovery-and-monitoring/tests/errors/pre-42-PrimarySteppedDown.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/pre-42-ShutdownInProgress.json b/specifications/server-discovery-and-monitoring/tests/errors/pre-42-ShutdownInProgress.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/pre-42-ShutdownInProgress.json rename to specifications/server-discovery-and-monitoring/tests/errors/pre-42-ShutdownInProgress.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/pre-42-ShutdownInProgress.yml b/specifications/server-discovery-and-monitoring/tests/errors/pre-42-ShutdownInProgress.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/pre-42-ShutdownInProgress.yml rename to specifications/server-discovery-and-monitoring/tests/errors/pre-42-ShutdownInProgress.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/pre-42.yml.template b/specifications/server-discovery-and-monitoring/tests/errors/pre-42.yml.template similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/pre-42.yml.template rename to specifications/server-discovery-and-monitoring/tests/errors/pre-42.yml.template diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/prefer-error-code.json b/specifications/server-discovery-and-monitoring/tests/errors/prefer-error-code.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/prefer-error-code.json rename to specifications/server-discovery-and-monitoring/tests/errors/prefer-error-code.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/prefer-error-code.yml b/specifications/server-discovery-and-monitoring/tests/errors/prefer-error-code.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/prefer-error-code.yml rename to specifications/server-discovery-and-monitoring/tests/errors/prefer-error-code.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-InterruptedAtShutdown.json b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-InterruptedAtShutdown.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-InterruptedAtShutdown.json rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-InterruptedAtShutdown.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-InterruptedAtShutdown.yml b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-InterruptedAtShutdown.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-InterruptedAtShutdown.yml rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-InterruptedAtShutdown.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-InterruptedDueToReplStateChange.json b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-InterruptedDueToReplStateChange.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-InterruptedDueToReplStateChange.json rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-InterruptedDueToReplStateChange.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-InterruptedDueToReplStateChange.yml b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-InterruptedDueToReplStateChange.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-InterruptedDueToReplStateChange.yml rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-InterruptedDueToReplStateChange.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-NotPrimaryNoSecondaryOk.json b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-NotPrimaryNoSecondaryOk.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-NotPrimaryNoSecondaryOk.json rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-NotPrimaryNoSecondaryOk.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-NotPrimaryNoSecondaryOk.yml b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-NotPrimaryNoSecondaryOk.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-NotPrimaryNoSecondaryOk.yml rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-NotPrimaryNoSecondaryOk.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-NotPrimaryOrSecondary.json b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-NotPrimaryOrSecondary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-NotPrimaryOrSecondary.json rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-NotPrimaryOrSecondary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-NotPrimaryOrSecondary.yml b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-NotPrimaryOrSecondary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-NotPrimaryOrSecondary.yml rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-NotPrimaryOrSecondary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-NotWritablePrimary.json b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-NotWritablePrimary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-NotWritablePrimary.json rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-NotWritablePrimary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-NotWritablePrimary.yml b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-NotWritablePrimary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-NotWritablePrimary.yml rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-NotWritablePrimary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-PrimarySteppedDown.json b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-PrimarySteppedDown.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-PrimarySteppedDown.json rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-PrimarySteppedDown.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-PrimarySteppedDown.yml b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-PrimarySteppedDown.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-PrimarySteppedDown.yml rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-PrimarySteppedDown.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-ShutdownInProgress.json b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-ShutdownInProgress.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-ShutdownInProgress.json rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-ShutdownInProgress.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-ShutdownInProgress.yml b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-ShutdownInProgress.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-ShutdownInProgress.yml rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-ShutdownInProgress.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-InterruptedAtShutdown.json b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-InterruptedAtShutdown.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-InterruptedAtShutdown.json rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-InterruptedAtShutdown.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-InterruptedAtShutdown.yml b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-InterruptedAtShutdown.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-InterruptedAtShutdown.yml rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-InterruptedAtShutdown.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-InterruptedDueToReplStateChange.json b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-InterruptedDueToReplStateChange.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-InterruptedDueToReplStateChange.json rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-InterruptedDueToReplStateChange.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-InterruptedDueToReplStateChange.yml b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-InterruptedDueToReplStateChange.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-InterruptedDueToReplStateChange.yml rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-InterruptedDueToReplStateChange.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-LegacyNotPrimary.json b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-LegacyNotPrimary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-LegacyNotPrimary.json rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-LegacyNotPrimary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-LegacyNotPrimary.yml b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-LegacyNotPrimary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-LegacyNotPrimary.yml rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-LegacyNotPrimary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotPrimaryNoSecondaryOk.json b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotPrimaryNoSecondaryOk.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotPrimaryNoSecondaryOk.json rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotPrimaryNoSecondaryOk.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotPrimaryNoSecondaryOk.yml b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotPrimaryNoSecondaryOk.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotPrimaryNoSecondaryOk.yml rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotPrimaryNoSecondaryOk.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotPrimaryOrSecondary.json b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotPrimaryOrSecondary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotPrimaryOrSecondary.json rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotPrimaryOrSecondary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotPrimaryOrSecondary.yml b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotPrimaryOrSecondary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotPrimaryOrSecondary.yml rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotPrimaryOrSecondary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotWritablePrimary.json b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotWritablePrimary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotWritablePrimary.json rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotWritablePrimary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotWritablePrimary.yml b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotWritablePrimary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotWritablePrimary.yml rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotWritablePrimary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-PrimarySteppedDown.json b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-PrimarySteppedDown.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-PrimarySteppedDown.json rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-PrimarySteppedDown.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-PrimarySteppedDown.yml b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-PrimarySteppedDown.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-PrimarySteppedDown.yml rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-PrimarySteppedDown.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-ShutdownInProgress.json b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-ShutdownInProgress.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-ShutdownInProgress.json rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-ShutdownInProgress.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-ShutdownInProgress.yml b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-ShutdownInProgress.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-ShutdownInProgress.yml rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-ShutdownInProgress.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-network.json b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-network.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-network.json rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-network.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-network.yml b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-network.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-network.yml rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-network.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-timeout.json b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-timeout.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-timeout.json rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-timeout.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-timeout.yml b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-timeout.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-timeout.yml rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-timeout.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-InterruptedAtShutdown.json b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-InterruptedAtShutdown.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-InterruptedAtShutdown.json rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-InterruptedAtShutdown.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-InterruptedAtShutdown.yml b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-InterruptedAtShutdown.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-InterruptedAtShutdown.yml rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-InterruptedAtShutdown.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-InterruptedDueToReplStateChange.json b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-InterruptedDueToReplStateChange.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-InterruptedDueToReplStateChange.json rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-InterruptedDueToReplStateChange.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-InterruptedDueToReplStateChange.yml b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-InterruptedDueToReplStateChange.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-InterruptedDueToReplStateChange.yml rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-InterruptedDueToReplStateChange.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-LegacyNotPrimary.json b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-LegacyNotPrimary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-LegacyNotPrimary.json rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-LegacyNotPrimary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-LegacyNotPrimary.yml b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-LegacyNotPrimary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-LegacyNotPrimary.yml rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-LegacyNotPrimary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotPrimaryNoSecondaryOk.json b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotPrimaryNoSecondaryOk.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotPrimaryNoSecondaryOk.json rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotPrimaryNoSecondaryOk.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotPrimaryNoSecondaryOk.yml b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotPrimaryNoSecondaryOk.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotPrimaryNoSecondaryOk.yml rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotPrimaryNoSecondaryOk.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotPrimaryOrSecondary.json b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotPrimaryOrSecondary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotPrimaryOrSecondary.json rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotPrimaryOrSecondary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotPrimaryOrSecondary.yml b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotPrimaryOrSecondary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotPrimaryOrSecondary.yml rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotPrimaryOrSecondary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotWritablePrimary.json b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotWritablePrimary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotWritablePrimary.json rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotWritablePrimary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotWritablePrimary.yml b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotWritablePrimary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotWritablePrimary.yml rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotWritablePrimary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-PrimarySteppedDown.json b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-PrimarySteppedDown.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-PrimarySteppedDown.json rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-PrimarySteppedDown.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-PrimarySteppedDown.yml b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-PrimarySteppedDown.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-PrimarySteppedDown.yml rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-PrimarySteppedDown.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-ShutdownInProgress.json b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-ShutdownInProgress.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-ShutdownInProgress.json rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-ShutdownInProgress.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-ShutdownInProgress.yml b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-ShutdownInProgress.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-ShutdownInProgress.yml rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-ShutdownInProgress.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-network.json b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-network.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-network.json rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-network.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-network.yml b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-network.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-network.yml rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-network.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-timeout.json b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-timeout.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-timeout.json rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-timeout.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-timeout.yml b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-timeout.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-timeout.yml rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-timeout.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation.yml.template b/specifications/server-discovery-and-monitoring/tests/errors/stale-generation.yml.template similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-generation.yml.template rename to specifications/server-discovery-and-monitoring/tests/errors/stale-generation.yml.template diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-InterruptedAtShutdown.json b/specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-InterruptedAtShutdown.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-InterruptedAtShutdown.json rename to specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-InterruptedAtShutdown.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-InterruptedAtShutdown.yml b/specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-InterruptedAtShutdown.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-InterruptedAtShutdown.yml rename to specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-InterruptedAtShutdown.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-InterruptedDueToReplStateChange.json b/specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-InterruptedDueToReplStateChange.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-InterruptedDueToReplStateChange.json rename to specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-InterruptedDueToReplStateChange.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-InterruptedDueToReplStateChange.yml b/specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-InterruptedDueToReplStateChange.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-InterruptedDueToReplStateChange.yml rename to specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-InterruptedDueToReplStateChange.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-LegacyNotPrimary.json b/specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-LegacyNotPrimary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-LegacyNotPrimary.json rename to specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-LegacyNotPrimary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-LegacyNotPrimary.yml b/specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-LegacyNotPrimary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-LegacyNotPrimary.yml rename to specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-LegacyNotPrimary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotPrimaryNoSecondaryOk.json b/specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotPrimaryNoSecondaryOk.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotPrimaryNoSecondaryOk.json rename to specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotPrimaryNoSecondaryOk.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotPrimaryNoSecondaryOk.yml b/specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotPrimaryNoSecondaryOk.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotPrimaryNoSecondaryOk.yml rename to specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotPrimaryNoSecondaryOk.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotPrimaryOrSecondary.json b/specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotPrimaryOrSecondary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotPrimaryOrSecondary.json rename to specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotPrimaryOrSecondary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotPrimaryOrSecondary.yml b/specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotPrimaryOrSecondary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotPrimaryOrSecondary.yml rename to specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotPrimaryOrSecondary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotWritablePrimary.json b/specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotWritablePrimary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotWritablePrimary.json rename to specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotWritablePrimary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotWritablePrimary.yml b/specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotWritablePrimary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotWritablePrimary.yml rename to specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotWritablePrimary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-PrimarySteppedDown.json b/specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-PrimarySteppedDown.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-PrimarySteppedDown.json rename to specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-PrimarySteppedDown.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-PrimarySteppedDown.yml b/specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-PrimarySteppedDown.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-PrimarySteppedDown.yml rename to specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-PrimarySteppedDown.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-ShutdownInProgress.json b/specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-ShutdownInProgress.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-ShutdownInProgress.json rename to specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-ShutdownInProgress.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-ShutdownInProgress.yml b/specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-ShutdownInProgress.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-ShutdownInProgress.yml rename to specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-ShutdownInProgress.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion.yml.template b/specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion.yml.template similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion.yml.template rename to specifications/server-discovery-and-monitoring/tests/errors/stale-topologyVersion.yml.template diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/write_errors_ignored.json b/specifications/server-discovery-and-monitoring/tests/errors/write_errors_ignored.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/write_errors_ignored.json rename to specifications/server-discovery-and-monitoring/tests/errors/write_errors_ignored.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/write_errors_ignored.yml b/specifications/server-discovery-and-monitoring/tests/errors/write_errors_ignored.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/errors/write_errors_ignored.yml rename to specifications/server-discovery-and-monitoring/tests/errors/write_errors_ignored.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/auth-error.json b/specifications/server-discovery-and-monitoring/tests/integration/auth-error.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/auth-error.json rename to specifications/server-discovery-and-monitoring/tests/integration/auth-error.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/auth-error.yml b/specifications/server-discovery-and-monitoring/tests/integration/auth-error.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/auth-error.yml rename to specifications/server-discovery-and-monitoring/tests/integration/auth-error.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/auth-misc-command-error.json b/specifications/server-discovery-and-monitoring/tests/integration/auth-misc-command-error.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/auth-misc-command-error.json rename to specifications/server-discovery-and-monitoring/tests/integration/auth-misc-command-error.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/auth-misc-command-error.yml b/specifications/server-discovery-and-monitoring/tests/integration/auth-misc-command-error.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/auth-misc-command-error.yml rename to specifications/server-discovery-and-monitoring/tests/integration/auth-misc-command-error.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/auth-network-error.json b/specifications/server-discovery-and-monitoring/tests/integration/auth-network-error.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/auth-network-error.json rename to specifications/server-discovery-and-monitoring/tests/integration/auth-network-error.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/auth-network-error.yml b/specifications/server-discovery-and-monitoring/tests/integration/auth-network-error.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/auth-network-error.yml rename to specifications/server-discovery-and-monitoring/tests/integration/auth-network-error.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/auth-network-timeout-error.json b/specifications/server-discovery-and-monitoring/tests/integration/auth-network-timeout-error.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/auth-network-timeout-error.json rename to specifications/server-discovery-and-monitoring/tests/integration/auth-network-timeout-error.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/auth-network-timeout-error.yml b/specifications/server-discovery-and-monitoring/tests/integration/auth-network-timeout-error.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/auth-network-timeout-error.yml rename to specifications/server-discovery-and-monitoring/tests/integration/auth-network-timeout-error.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/auth-shutdown-error.json b/specifications/server-discovery-and-monitoring/tests/integration/auth-shutdown-error.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/auth-shutdown-error.json rename to specifications/server-discovery-and-monitoring/tests/integration/auth-shutdown-error.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/auth-shutdown-error.yml b/specifications/server-discovery-and-monitoring/tests/integration/auth-shutdown-error.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/auth-shutdown-error.yml rename to specifications/server-discovery-and-monitoring/tests/integration/auth-shutdown-error.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/cancel-server-check.json b/specifications/server-discovery-and-monitoring/tests/integration/cancel-server-check.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/cancel-server-check.json rename to specifications/server-discovery-and-monitoring/tests/integration/cancel-server-check.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/cancel-server-check.yml b/specifications/server-discovery-and-monitoring/tests/integration/cancel-server-check.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/cancel-server-check.yml rename to specifications/server-discovery-and-monitoring/tests/integration/cancel-server-check.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/connectTimeoutMS.json b/specifications/server-discovery-and-monitoring/tests/integration/connectTimeoutMS.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/connectTimeoutMS.json rename to specifications/server-discovery-and-monitoring/tests/integration/connectTimeoutMS.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/connectTimeoutMS.yml b/specifications/server-discovery-and-monitoring/tests/integration/connectTimeoutMS.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/connectTimeoutMS.yml rename to specifications/server-discovery-and-monitoring/tests/integration/connectTimeoutMS.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/find-network-error.json b/specifications/server-discovery-and-monitoring/tests/integration/find-network-error.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/find-network-error.json rename to specifications/server-discovery-and-monitoring/tests/integration/find-network-error.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/find-network-error.yml b/specifications/server-discovery-and-monitoring/tests/integration/find-network-error.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/find-network-error.yml rename to specifications/server-discovery-and-monitoring/tests/integration/find-network-error.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/find-network-timeout-error.json b/specifications/server-discovery-and-monitoring/tests/integration/find-network-timeout-error.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/find-network-timeout-error.json rename to specifications/server-discovery-and-monitoring/tests/integration/find-network-timeout-error.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/find-network-timeout-error.yml b/specifications/server-discovery-and-monitoring/tests/integration/find-network-timeout-error.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/find-network-timeout-error.yml rename to specifications/server-discovery-and-monitoring/tests/integration/find-network-timeout-error.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/find-shutdown-error.json b/specifications/server-discovery-and-monitoring/tests/integration/find-shutdown-error.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/find-shutdown-error.json rename to specifications/server-discovery-and-monitoring/tests/integration/find-shutdown-error.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/find-shutdown-error.yml b/specifications/server-discovery-and-monitoring/tests/integration/find-shutdown-error.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/find-shutdown-error.yml rename to specifications/server-discovery-and-monitoring/tests/integration/find-shutdown-error.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/hello-command-error.json b/specifications/server-discovery-and-monitoring/tests/integration/hello-command-error.json similarity index 92% rename from tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/hello-command-error.json rename to specifications/server-discovery-and-monitoring/tests/integration/hello-command-error.json index 05a93e751c5..d3bccd39008 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/hello-command-error.json +++ b/specifications/server-discovery-and-monitoring/tests/integration/hello-command-error.json @@ -117,7 +117,7 @@ "failPoint": { "configureFailPoint": "failCommand", "mode": { - "times": 2 + "times": 4 }, "data": { "failCommands": [ @@ -162,22 +162,6 @@ } ] } - }, - { - "name": "assertEventCount", - "object": "testRunner", - "arguments": { - "event": "ServerMarkedUnknownEvent", - "count": 1 - } - }, - { - "name": "assertEventCount", - "object": "testRunner", - "arguments": { - "event": "PoolClearedEvent", - "count": 1 - } } ], "expectations": [ diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/hello-command-error.yml b/specifications/server-discovery-and-monitoring/tests/integration/hello-command-error.yml similarity index 89% rename from tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/hello-command-error.yml rename to specifications/server-discovery-and-monitoring/tests/integration/hello-command-error.yml index ae25e4946c5..b6d8f2f80da 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/hello-command-error.yml +++ b/specifications/server-discovery-and-monitoring/tests/integration/hello-command-error.yml @@ -84,15 +84,16 @@ tests: documents: - _id: 1 - _id: 2 - # Configure the next streaming hello check to fail with a command - # error. - # Use times: 2 so that the RTT hello is blocked as well. + # Configure the next streaming hello check to fail with a command error. + # Use "times: 4" to increase the probability that the Monitor check fails + # since the RTT hello may trigger this failpoint one or many times as + # well. - name: configureFailPoint object: testRunner arguments: failPoint: configureFailPoint: failCommand - mode: { times: 2 } + mode: { times: 4 } data: failCommands: ["hello", "isMaster"] appName: commandErrorCheckTest @@ -119,17 +120,9 @@ tests: documents: - _id: 3 - _id: 4 - # Assert the server was marked Unknown and pool was cleared exactly once. - - name: assertEventCount - object: testRunner - arguments: - event: ServerMarkedUnknownEvent - count: 1 - - name: assertEventCount - object: testRunner - arguments: - event: PoolClearedEvent - count: 1 + # We cannot assert the server was marked Unknown and pool was cleared an + # exact number of times because the RTT hello may have triggered this + # failpoint one or many times as well. expectations: - command_started_event: diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/hello-network-error.json b/specifications/server-discovery-and-monitoring/tests/integration/hello-network-error.json similarity index 99% rename from tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/hello-network-error.json rename to specifications/server-discovery-and-monitoring/tests/integration/hello-network-error.json index b699363923b..f9761d75563 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/hello-network-error.json +++ b/specifications/server-discovery-and-monitoring/tests/integration/hello-network-error.json @@ -116,7 +116,7 @@ "failPoint": { "configureFailPoint": "failCommand", "mode": { - "times": 2 + "times": 4 }, "data": { "failCommands": [ diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/hello-network-error.yml b/specifications/server-discovery-and-monitoring/tests/integration/hello-network-error.yml similarity index 87% rename from tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/hello-network-error.yml rename to specifications/server-discovery-and-monitoring/tests/integration/hello-network-error.yml index 54c6a3bac3c..10d9ac6d90f 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/hello-network-error.yml +++ b/specifications/server-discovery-and-monitoring/tests/integration/hello-network-error.yml @@ -42,16 +42,6 @@ tests: # We cannot assert the server was marked Unknown and pool was cleared an # exact number of times because the RTT hello may or may not have # triggered this failpoint as well. - # - name: assertEventCount - # object: testRunner - # arguments: - # event: ServerMarkedUnknownEvent - # count: 1 - # - name: assertEventCount - # object: testRunner - # arguments: - # event: PoolClearedEvent - # count: 1 expectations: - command_started_event: @@ -84,14 +74,15 @@ tests: - _id: 1 - _id: 2 # Configure the next streaming hello check to fail with a non-timeout - # network error. Use times: 2 to ensure that the the Monitor check fails - # since the RTT hello may trigger this failpoint as well. + # network error. Use "times: 4" to increase the probability that the + # Monitor check fails since the RTT hello may trigger this failpoint one + # or many times as well. - name: configureFailPoint object: testRunner arguments: failPoint: configureFailPoint: failCommand - mode: { times: 2 } + mode: { times: 4 } data: failCommands: ["hello", "isMaster"] appName: networkErrorCheckTest @@ -116,8 +107,8 @@ tests: - _id: 3 - _id: 4 # We cannot assert the server was marked Unknown and pool was cleared an - # exact number of times because the RTT hello may or may not have - # triggered this failpoint as well. + # exact number of times because the RTT hello may have triggered this + # failpoint one or many times as well. # - name: assertEventCount # object: testRunner # arguments: diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/hello-timeout.json b/specifications/server-discovery-and-monitoring/tests/integration/hello-timeout.json similarity index 94% rename from tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/hello-timeout.json rename to specifications/server-discovery-and-monitoring/tests/integration/hello-timeout.json index 0d3c6fb0156..2d3d0ed718c 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/hello-timeout.json +++ b/specifications/server-discovery-and-monitoring/tests/integration/hello-timeout.json @@ -117,7 +117,7 @@ "failPoint": { "configureFailPoint": "failCommand", "mode": { - "times": 2 + "times": 4 }, "data": { "failCommands": [ @@ -160,22 +160,6 @@ } ] } - }, - { - "name": "assertEventCount", - "object": "testRunner", - "arguments": { - "event": "ServerMarkedUnknownEvent", - "count": 1 - } - }, - { - "name": "assertEventCount", - "object": "testRunner", - "arguments": { - "event": "PoolClearedEvent", - "count": 1 - } } ], "expectations": [ diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/hello-timeout.yml b/specifications/server-discovery-and-monitoring/tests/integration/hello-timeout.yml similarity index 93% rename from tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/hello-timeout.yml rename to specifications/server-discovery-and-monitoring/tests/integration/hello-timeout.yml index 69c9990f5cb..f7d40d88ef3 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/hello-timeout.yml +++ b/specifications/server-discovery-and-monitoring/tests/integration/hello-timeout.yml @@ -86,14 +86,16 @@ tests: documents: - _id: 1 - _id: 2 - # Configure the next streaming hello check to fail with a timeout - # Use times: 2 so that the RTT hello is blocked as well. + # Configure the next streaming hello check to fail with a timeout. + # Use "times: 4" to increase the probability that the Monitor check times + # out since the RTT hello may trigger this failpoint one or many times as + # well. - name: configureFailPoint object: testRunner arguments: failPoint: configureFailPoint: failCommand - mode: { times: 2 } + mode: { times: 4 } data: failCommands: ["hello", "isMaster"] appName: timeoutMonitorCheckTest @@ -121,17 +123,9 @@ tests: documents: - _id: 3 - _id: 4 - # Assert the server was marked Unknown and pool was cleared exactly once. - - name: assertEventCount - object: testRunner - arguments: - event: ServerMarkedUnknownEvent - count: 1 - - name: assertEventCount - object: testRunner - arguments: - event: PoolClearedEvent - count: 1 + # We cannot assert the server was marked Unknown and pool was cleared an + # exact number of times because the RTT hello may have triggered this + # failpoint one or many times as well. expectations: - command_started_event: diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/insert-network-error.json b/specifications/server-discovery-and-monitoring/tests/integration/insert-network-error.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/insert-network-error.json rename to specifications/server-discovery-and-monitoring/tests/integration/insert-network-error.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/insert-network-error.yml b/specifications/server-discovery-and-monitoring/tests/integration/insert-network-error.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/insert-network-error.yml rename to specifications/server-discovery-and-monitoring/tests/integration/insert-network-error.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/insert-shutdown-error.json b/specifications/server-discovery-and-monitoring/tests/integration/insert-shutdown-error.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/insert-shutdown-error.json rename to specifications/server-discovery-and-monitoring/tests/integration/insert-shutdown-error.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/insert-shutdown-error.yml b/specifications/server-discovery-and-monitoring/tests/integration/insert-shutdown-error.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/insert-shutdown-error.yml rename to specifications/server-discovery-and-monitoring/tests/integration/insert-shutdown-error.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/minPoolSize-error.json b/specifications/server-discovery-and-monitoring/tests/integration/minPoolSize-error.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/minPoolSize-error.json rename to specifications/server-discovery-and-monitoring/tests/integration/minPoolSize-error.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/minPoolSize-error.yml b/specifications/server-discovery-and-monitoring/tests/integration/minPoolSize-error.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/minPoolSize-error.yml rename to specifications/server-discovery-and-monitoring/tests/integration/minPoolSize-error.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/pool-cleared-error.json b/specifications/server-discovery-and-monitoring/tests/integration/pool-cleared-error.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/pool-cleared-error.json rename to specifications/server-discovery-and-monitoring/tests/integration/pool-cleared-error.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/pool-cleared-error.yml b/specifications/server-discovery-and-monitoring/tests/integration/pool-cleared-error.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/pool-cleared-error.yml rename to specifications/server-discovery-and-monitoring/tests/integration/pool-cleared-error.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/rediscover-quickly-after-step-down.json b/specifications/server-discovery-and-monitoring/tests/integration/rediscover-quickly-after-step-down.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/rediscover-quickly-after-step-down.json rename to specifications/server-discovery-and-monitoring/tests/integration/rediscover-quickly-after-step-down.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/rediscover-quickly-after-step-down.yml b/specifications/server-discovery-and-monitoring/tests/integration/rediscover-quickly-after-step-down.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/tests/integration/rediscover-quickly-after-step-down.yml rename to specifications/server-discovery-and-monitoring/tests/integration/rediscover-quickly-after-step-down.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/load-balanced/discover_load_balancer.json b/specifications/server-discovery-and-monitoring/tests/load-balanced/discover_load_balancer.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/load-balanced/discover_load_balancer.json rename to specifications/server-discovery-and-monitoring/tests/load-balanced/discover_load_balancer.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/load-balanced/discover_load_balancer.yml b/specifications/server-discovery-and-monitoring/tests/load-balanced/discover_load_balancer.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/load-balanced/discover_load_balancer.yml rename to specifications/server-discovery-and-monitoring/tests/load-balanced/discover_load_balancer.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/monitoring/README.rst b/specifications/server-discovery-and-monitoring/tests/monitoring/README.rst similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/monitoring/README.rst rename to specifications/server-discovery-and-monitoring/tests/monitoring/README.rst diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/monitoring/discovered_standalone.json b/specifications/server-discovery-and-monitoring/tests/monitoring/discovered_standalone.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/monitoring/discovered_standalone.json rename to specifications/server-discovery-and-monitoring/tests/monitoring/discovered_standalone.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/monitoring/discovered_standalone.yml b/specifications/server-discovery-and-monitoring/tests/monitoring/discovered_standalone.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/monitoring/discovered_standalone.yml rename to specifications/server-discovery-and-monitoring/tests/monitoring/discovered_standalone.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/monitoring/load_balancer.json b/specifications/server-discovery-and-monitoring/tests/monitoring/load_balancer.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/monitoring/load_balancer.json rename to specifications/server-discovery-and-monitoring/tests/monitoring/load_balancer.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/monitoring/load_balancer.yml b/specifications/server-discovery-and-monitoring/tests/monitoring/load_balancer.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/monitoring/load_balancer.yml rename to specifications/server-discovery-and-monitoring/tests/monitoring/load_balancer.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/monitoring/replica_set_with_no_primary.json b/specifications/server-discovery-and-monitoring/tests/monitoring/replica_set_with_no_primary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/monitoring/replica_set_with_no_primary.json rename to specifications/server-discovery-and-monitoring/tests/monitoring/replica_set_with_no_primary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/monitoring/replica_set_with_no_primary.yml b/specifications/server-discovery-and-monitoring/tests/monitoring/replica_set_with_no_primary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/monitoring/replica_set_with_no_primary.yml rename to specifications/server-discovery-and-monitoring/tests/monitoring/replica_set_with_no_primary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/monitoring/replica_set_with_primary.json b/specifications/server-discovery-and-monitoring/tests/monitoring/replica_set_with_primary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/monitoring/replica_set_with_primary.json rename to specifications/server-discovery-and-monitoring/tests/monitoring/replica_set_with_primary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/monitoring/replica_set_with_primary.yml b/specifications/server-discovery-and-monitoring/tests/monitoring/replica_set_with_primary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/monitoring/replica_set_with_primary.yml rename to specifications/server-discovery-and-monitoring/tests/monitoring/replica_set_with_primary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/monitoring/replica_set_with_removal.json b/specifications/server-discovery-and-monitoring/tests/monitoring/replica_set_with_removal.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/monitoring/replica_set_with_removal.json rename to specifications/server-discovery-and-monitoring/tests/monitoring/replica_set_with_removal.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/monitoring/replica_set_with_removal.yml b/specifications/server-discovery-and-monitoring/tests/monitoring/replica_set_with_removal.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/monitoring/replica_set_with_removal.yml rename to specifications/server-discovery-and-monitoring/tests/monitoring/replica_set_with_removal.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/monitoring/required_replica_set.json b/specifications/server-discovery-and-monitoring/tests/monitoring/required_replica_set.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/monitoring/required_replica_set.json rename to specifications/server-discovery-and-monitoring/tests/monitoring/required_replica_set.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/monitoring/required_replica_set.yml b/specifications/server-discovery-and-monitoring/tests/monitoring/required_replica_set.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/monitoring/required_replica_set.yml rename to specifications/server-discovery-and-monitoring/tests/monitoring/required_replica_set.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/monitoring/standalone.json b/specifications/server-discovery-and-monitoring/tests/monitoring/standalone.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/monitoring/standalone.json rename to specifications/server-discovery-and-monitoring/tests/monitoring/standalone.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/monitoring/standalone.yml b/specifications/server-discovery-and-monitoring/tests/monitoring/standalone.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/monitoring/standalone.yml rename to specifications/server-discovery-and-monitoring/tests/monitoring/standalone.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/monitoring/standalone_suppress_equal_description_changes.json b/specifications/server-discovery-and-monitoring/tests/monitoring/standalone_suppress_equal_description_changes.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/monitoring/standalone_suppress_equal_description_changes.json rename to specifications/server-discovery-and-monitoring/tests/monitoring/standalone_suppress_equal_description_changes.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/monitoring/standalone_suppress_equal_description_changes.yml b/specifications/server-discovery-and-monitoring/tests/monitoring/standalone_suppress_equal_description_changes.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/monitoring/standalone_suppress_equal_description_changes.yml rename to specifications/server-discovery-and-monitoring/tests/monitoring/standalone_suppress_equal_description_changes.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/compatible.json b/specifications/server-discovery-and-monitoring/tests/rs/compatible.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/compatible.json rename to specifications/server-discovery-and-monitoring/tests/rs/compatible.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/compatible.yml b/specifications/server-discovery-and-monitoring/tests/rs/compatible.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/compatible.yml rename to specifications/server-discovery-and-monitoring/tests/rs/compatible.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/compatible_unknown.json b/specifications/server-discovery-and-monitoring/tests/rs/compatible_unknown.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/compatible_unknown.json rename to specifications/server-discovery-and-monitoring/tests/rs/compatible_unknown.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/compatible_unknown.yml b/specifications/server-discovery-and-monitoring/tests/rs/compatible_unknown.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/compatible_unknown.yml rename to specifications/server-discovery-and-monitoring/tests/rs/compatible_unknown.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_arbiters.json b/specifications/server-discovery-and-monitoring/tests/rs/discover_arbiters.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_arbiters.json rename to specifications/server-discovery-and-monitoring/tests/rs/discover_arbiters.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_arbiters.yml b/specifications/server-discovery-and-monitoring/tests/rs/discover_arbiters.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_arbiters.yml rename to specifications/server-discovery-and-monitoring/tests/rs/discover_arbiters.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_arbiters_replicaset.json b/specifications/server-discovery-and-monitoring/tests/rs/discover_arbiters_replicaset.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_arbiters_replicaset.json rename to specifications/server-discovery-and-monitoring/tests/rs/discover_arbiters_replicaset.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_arbiters_replicaset.yml b/specifications/server-discovery-and-monitoring/tests/rs/discover_arbiters_replicaset.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_arbiters_replicaset.yml rename to specifications/server-discovery-and-monitoring/tests/rs/discover_arbiters_replicaset.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_ghost.json b/specifications/server-discovery-and-monitoring/tests/rs/discover_ghost.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_ghost.json rename to specifications/server-discovery-and-monitoring/tests/rs/discover_ghost.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_ghost.yml b/specifications/server-discovery-and-monitoring/tests/rs/discover_ghost.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_ghost.yml rename to specifications/server-discovery-and-monitoring/tests/rs/discover_ghost.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_ghost_replicaset.json b/specifications/server-discovery-and-monitoring/tests/rs/discover_ghost_replicaset.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_ghost_replicaset.json rename to specifications/server-discovery-and-monitoring/tests/rs/discover_ghost_replicaset.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_ghost_replicaset.yml b/specifications/server-discovery-and-monitoring/tests/rs/discover_ghost_replicaset.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_ghost_replicaset.yml rename to specifications/server-discovery-and-monitoring/tests/rs/discover_ghost_replicaset.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_hidden.json b/specifications/server-discovery-and-monitoring/tests/rs/discover_hidden.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_hidden.json rename to specifications/server-discovery-and-monitoring/tests/rs/discover_hidden.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_hidden.yml b/specifications/server-discovery-and-monitoring/tests/rs/discover_hidden.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_hidden.yml rename to specifications/server-discovery-and-monitoring/tests/rs/discover_hidden.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_hidden_replicaset.json b/specifications/server-discovery-and-monitoring/tests/rs/discover_hidden_replicaset.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_hidden_replicaset.json rename to specifications/server-discovery-and-monitoring/tests/rs/discover_hidden_replicaset.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_hidden_replicaset.yml b/specifications/server-discovery-and-monitoring/tests/rs/discover_hidden_replicaset.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_hidden_replicaset.yml rename to specifications/server-discovery-and-monitoring/tests/rs/discover_hidden_replicaset.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_passives.json b/specifications/server-discovery-and-monitoring/tests/rs/discover_passives.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_passives.json rename to specifications/server-discovery-and-monitoring/tests/rs/discover_passives.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_passives.yml b/specifications/server-discovery-and-monitoring/tests/rs/discover_passives.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_passives.yml rename to specifications/server-discovery-and-monitoring/tests/rs/discover_passives.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_passives_replicaset.json b/specifications/server-discovery-and-monitoring/tests/rs/discover_passives_replicaset.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_passives_replicaset.json rename to specifications/server-discovery-and-monitoring/tests/rs/discover_passives_replicaset.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_passives_replicaset.yml b/specifications/server-discovery-and-monitoring/tests/rs/discover_passives_replicaset.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_passives_replicaset.yml rename to specifications/server-discovery-and-monitoring/tests/rs/discover_passives_replicaset.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_primary.json b/specifications/server-discovery-and-monitoring/tests/rs/discover_primary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_primary.json rename to specifications/server-discovery-and-monitoring/tests/rs/discover_primary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_primary.yml b/specifications/server-discovery-and-monitoring/tests/rs/discover_primary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_primary.yml rename to specifications/server-discovery-and-monitoring/tests/rs/discover_primary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_primary_replicaset.json b/specifications/server-discovery-and-monitoring/tests/rs/discover_primary_replicaset.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_primary_replicaset.json rename to specifications/server-discovery-and-monitoring/tests/rs/discover_primary_replicaset.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_primary_replicaset.yml b/specifications/server-discovery-and-monitoring/tests/rs/discover_primary_replicaset.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_primary_replicaset.yml rename to specifications/server-discovery-and-monitoring/tests/rs/discover_primary_replicaset.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_rsother.json b/specifications/server-discovery-and-monitoring/tests/rs/discover_rsother.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_rsother.json rename to specifications/server-discovery-and-monitoring/tests/rs/discover_rsother.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_rsother.yml b/specifications/server-discovery-and-monitoring/tests/rs/discover_rsother.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_rsother.yml rename to specifications/server-discovery-and-monitoring/tests/rs/discover_rsother.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_rsother_replicaset.json b/specifications/server-discovery-and-monitoring/tests/rs/discover_rsother_replicaset.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_rsother_replicaset.json rename to specifications/server-discovery-and-monitoring/tests/rs/discover_rsother_replicaset.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_rsother_replicaset.yml b/specifications/server-discovery-and-monitoring/tests/rs/discover_rsother_replicaset.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_rsother_replicaset.yml rename to specifications/server-discovery-and-monitoring/tests/rs/discover_rsother_replicaset.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_secondary.json b/specifications/server-discovery-and-monitoring/tests/rs/discover_secondary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_secondary.json rename to specifications/server-discovery-and-monitoring/tests/rs/discover_secondary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_secondary.yml b/specifications/server-discovery-and-monitoring/tests/rs/discover_secondary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_secondary.yml rename to specifications/server-discovery-and-monitoring/tests/rs/discover_secondary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_secondary_replicaset.json b/specifications/server-discovery-and-monitoring/tests/rs/discover_secondary_replicaset.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_secondary_replicaset.json rename to specifications/server-discovery-and-monitoring/tests/rs/discover_secondary_replicaset.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_secondary_replicaset.yml b/specifications/server-discovery-and-monitoring/tests/rs/discover_secondary_replicaset.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discover_secondary_replicaset.yml rename to specifications/server-discovery-and-monitoring/tests/rs/discover_secondary_replicaset.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discovery.json b/specifications/server-discovery-and-monitoring/tests/rs/discovery.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discovery.json rename to specifications/server-discovery-and-monitoring/tests/rs/discovery.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discovery.yml b/specifications/server-discovery-and-monitoring/tests/rs/discovery.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/discovery.yml rename to specifications/server-discovery-and-monitoring/tests/rs/discovery.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/equal_electionids.json b/specifications/server-discovery-and-monitoring/tests/rs/equal_electionids.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/equal_electionids.json rename to specifications/server-discovery-and-monitoring/tests/rs/equal_electionids.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/equal_electionids.yml b/specifications/server-discovery-and-monitoring/tests/rs/equal_electionids.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/equal_electionids.yml rename to specifications/server-discovery-and-monitoring/tests/rs/equal_electionids.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/hosts_differ_from_seeds.json b/specifications/server-discovery-and-monitoring/tests/rs/hosts_differ_from_seeds.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/hosts_differ_from_seeds.json rename to specifications/server-discovery-and-monitoring/tests/rs/hosts_differ_from_seeds.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/hosts_differ_from_seeds.yml b/specifications/server-discovery-and-monitoring/tests/rs/hosts_differ_from_seeds.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/hosts_differ_from_seeds.yml rename to specifications/server-discovery-and-monitoring/tests/rs/hosts_differ_from_seeds.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/incompatible_arbiter.json b/specifications/server-discovery-and-monitoring/tests/rs/incompatible_arbiter.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/incompatible_arbiter.json rename to specifications/server-discovery-and-monitoring/tests/rs/incompatible_arbiter.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/incompatible_arbiter.yml b/specifications/server-discovery-and-monitoring/tests/rs/incompatible_arbiter.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/incompatible_arbiter.yml rename to specifications/server-discovery-and-monitoring/tests/rs/incompatible_arbiter.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/incompatible_ghost.json b/specifications/server-discovery-and-monitoring/tests/rs/incompatible_ghost.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/incompatible_ghost.json rename to specifications/server-discovery-and-monitoring/tests/rs/incompatible_ghost.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/incompatible_ghost.yml b/specifications/server-discovery-and-monitoring/tests/rs/incompatible_ghost.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/incompatible_ghost.yml rename to specifications/server-discovery-and-monitoring/tests/rs/incompatible_ghost.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/incompatible_other.json b/specifications/server-discovery-and-monitoring/tests/rs/incompatible_other.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/incompatible_other.json rename to specifications/server-discovery-and-monitoring/tests/rs/incompatible_other.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/incompatible_other.yml b/specifications/server-discovery-and-monitoring/tests/rs/incompatible_other.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/incompatible_other.yml rename to specifications/server-discovery-and-monitoring/tests/rs/incompatible_other.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/ls_timeout.json b/specifications/server-discovery-and-monitoring/tests/rs/ls_timeout.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/ls_timeout.json rename to specifications/server-discovery-and-monitoring/tests/rs/ls_timeout.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/ls_timeout.yml b/specifications/server-discovery-and-monitoring/tests/rs/ls_timeout.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/ls_timeout.yml rename to specifications/server-discovery-and-monitoring/tests/rs/ls_timeout.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/member_reconfig.json b/specifications/server-discovery-and-monitoring/tests/rs/member_reconfig.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/member_reconfig.json rename to specifications/server-discovery-and-monitoring/tests/rs/member_reconfig.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/member_reconfig.yml b/specifications/server-discovery-and-monitoring/tests/rs/member_reconfig.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/member_reconfig.yml rename to specifications/server-discovery-and-monitoring/tests/rs/member_reconfig.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/member_standalone.json b/specifications/server-discovery-and-monitoring/tests/rs/member_standalone.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/member_standalone.json rename to specifications/server-discovery-and-monitoring/tests/rs/member_standalone.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/member_standalone.yml b/specifications/server-discovery-and-monitoring/tests/rs/member_standalone.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/member_standalone.yml rename to specifications/server-discovery-and-monitoring/tests/rs/member_standalone.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/new_primary.json b/specifications/server-discovery-and-monitoring/tests/rs/new_primary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/new_primary.json rename to specifications/server-discovery-and-monitoring/tests/rs/new_primary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/new_primary.yml b/specifications/server-discovery-and-monitoring/tests/rs/new_primary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/new_primary.yml rename to specifications/server-discovery-and-monitoring/tests/rs/new_primary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/new_primary_new_electionid.json b/specifications/server-discovery-and-monitoring/tests/rs/new_primary_new_electionid.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/new_primary_new_electionid.json rename to specifications/server-discovery-and-monitoring/tests/rs/new_primary_new_electionid.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/new_primary_new_electionid.yml b/specifications/server-discovery-and-monitoring/tests/rs/new_primary_new_electionid.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/new_primary_new_electionid.yml rename to specifications/server-discovery-and-monitoring/tests/rs/new_primary_new_electionid.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/new_primary_new_setversion.json b/specifications/server-discovery-and-monitoring/tests/rs/new_primary_new_setversion.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/new_primary_new_setversion.json rename to specifications/server-discovery-and-monitoring/tests/rs/new_primary_new_setversion.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/new_primary_new_setversion.yml b/specifications/server-discovery-and-monitoring/tests/rs/new_primary_new_setversion.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/new_primary_new_setversion.yml rename to specifications/server-discovery-and-monitoring/tests/rs/new_primary_new_setversion.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/new_primary_wrong_set_name.json b/specifications/server-discovery-and-monitoring/tests/rs/new_primary_wrong_set_name.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/new_primary_wrong_set_name.json rename to specifications/server-discovery-and-monitoring/tests/rs/new_primary_wrong_set_name.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/new_primary_wrong_set_name.yml b/specifications/server-discovery-and-monitoring/tests/rs/new_primary_wrong_set_name.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/new_primary_wrong_set_name.yml rename to specifications/server-discovery-and-monitoring/tests/rs/new_primary_wrong_set_name.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/non_rs_member.json b/specifications/server-discovery-and-monitoring/tests/rs/non_rs_member.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/non_rs_member.json rename to specifications/server-discovery-and-monitoring/tests/rs/non_rs_member.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/non_rs_member.yml b/specifications/server-discovery-and-monitoring/tests/rs/non_rs_member.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/non_rs_member.yml rename to specifications/server-discovery-and-monitoring/tests/rs/non_rs_member.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/normalize_case.json b/specifications/server-discovery-and-monitoring/tests/rs/normalize_case.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/normalize_case.json rename to specifications/server-discovery-and-monitoring/tests/rs/normalize_case.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/normalize_case.yml b/specifications/server-discovery-and-monitoring/tests/rs/normalize_case.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/normalize_case.yml rename to specifications/server-discovery-and-monitoring/tests/rs/normalize_case.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/normalize_case_me.json b/specifications/server-discovery-and-monitoring/tests/rs/normalize_case_me.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/normalize_case_me.json rename to specifications/server-discovery-and-monitoring/tests/rs/normalize_case_me.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/normalize_case_me.yml b/specifications/server-discovery-and-monitoring/tests/rs/normalize_case_me.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/normalize_case_me.yml rename to specifications/server-discovery-and-monitoring/tests/rs/normalize_case_me.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/null_election_id.json b/specifications/server-discovery-and-monitoring/tests/rs/null_election_id.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/null_election_id.json rename to specifications/server-discovery-and-monitoring/tests/rs/null_election_id.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/null_election_id.yml b/specifications/server-discovery-and-monitoring/tests/rs/null_election_id.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/null_election_id.yml rename to specifications/server-discovery-and-monitoring/tests/rs/null_election_id.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_becomes_ghost.json b/specifications/server-discovery-and-monitoring/tests/rs/primary_becomes_ghost.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_becomes_ghost.json rename to specifications/server-discovery-and-monitoring/tests/rs/primary_becomes_ghost.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_becomes_ghost.yml b/specifications/server-discovery-and-monitoring/tests/rs/primary_becomes_ghost.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_becomes_ghost.yml rename to specifications/server-discovery-and-monitoring/tests/rs/primary_becomes_ghost.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_becomes_mongos.json b/specifications/server-discovery-and-monitoring/tests/rs/primary_becomes_mongos.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_becomes_mongos.json rename to specifications/server-discovery-and-monitoring/tests/rs/primary_becomes_mongos.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_becomes_mongos.yml b/specifications/server-discovery-and-monitoring/tests/rs/primary_becomes_mongos.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_becomes_mongos.yml rename to specifications/server-discovery-and-monitoring/tests/rs/primary_becomes_mongos.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_becomes_standalone.json b/specifications/server-discovery-and-monitoring/tests/rs/primary_becomes_standalone.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_becomes_standalone.json rename to specifications/server-discovery-and-monitoring/tests/rs/primary_becomes_standalone.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_becomes_standalone.yml b/specifications/server-discovery-and-monitoring/tests/rs/primary_becomes_standalone.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_becomes_standalone.yml rename to specifications/server-discovery-and-monitoring/tests/rs/primary_becomes_standalone.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_changes_set_name.json b/specifications/server-discovery-and-monitoring/tests/rs/primary_changes_set_name.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_changes_set_name.json rename to specifications/server-discovery-and-monitoring/tests/rs/primary_changes_set_name.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_changes_set_name.yml b/specifications/server-discovery-and-monitoring/tests/rs/primary_changes_set_name.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_changes_set_name.yml rename to specifications/server-discovery-and-monitoring/tests/rs/primary_changes_set_name.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_disconnect.json b/specifications/server-discovery-and-monitoring/tests/rs/primary_disconnect.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_disconnect.json rename to specifications/server-discovery-and-monitoring/tests/rs/primary_disconnect.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_disconnect.yml b/specifications/server-discovery-and-monitoring/tests/rs/primary_disconnect.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_disconnect.yml rename to specifications/server-discovery-and-monitoring/tests/rs/primary_disconnect.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_disconnect_electionid.json b/specifications/server-discovery-and-monitoring/tests/rs/primary_disconnect_electionid.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_disconnect_electionid.json rename to specifications/server-discovery-and-monitoring/tests/rs/primary_disconnect_electionid.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_disconnect_electionid.yml b/specifications/server-discovery-and-monitoring/tests/rs/primary_disconnect_electionid.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_disconnect_electionid.yml rename to specifications/server-discovery-and-monitoring/tests/rs/primary_disconnect_electionid.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_disconnect_setversion.json b/specifications/server-discovery-and-monitoring/tests/rs/primary_disconnect_setversion.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_disconnect_setversion.json rename to specifications/server-discovery-and-monitoring/tests/rs/primary_disconnect_setversion.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_disconnect_setversion.yml b/specifications/server-discovery-and-monitoring/tests/rs/primary_disconnect_setversion.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_disconnect_setversion.yml rename to specifications/server-discovery-and-monitoring/tests/rs/primary_disconnect_setversion.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_hint_from_secondary_with_mismatched_me.json b/specifications/server-discovery-and-monitoring/tests/rs/primary_hint_from_secondary_with_mismatched_me.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_hint_from_secondary_with_mismatched_me.json rename to specifications/server-discovery-and-monitoring/tests/rs/primary_hint_from_secondary_with_mismatched_me.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_hint_from_secondary_with_mismatched_me.yml b/specifications/server-discovery-and-monitoring/tests/rs/primary_hint_from_secondary_with_mismatched_me.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_hint_from_secondary_with_mismatched_me.yml rename to specifications/server-discovery-and-monitoring/tests/rs/primary_hint_from_secondary_with_mismatched_me.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_mismatched_me.json b/specifications/server-discovery-and-monitoring/tests/rs/primary_mismatched_me.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_mismatched_me.json rename to specifications/server-discovery-and-monitoring/tests/rs/primary_mismatched_me.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_mismatched_me.yml b/specifications/server-discovery-and-monitoring/tests/rs/primary_mismatched_me.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_mismatched_me.yml rename to specifications/server-discovery-and-monitoring/tests/rs/primary_mismatched_me.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_mismatched_me_not_removed.json b/specifications/server-discovery-and-monitoring/tests/rs/primary_mismatched_me_not_removed.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_mismatched_me_not_removed.json rename to specifications/server-discovery-and-monitoring/tests/rs/primary_mismatched_me_not_removed.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_mismatched_me_not_removed.yml b/specifications/server-discovery-and-monitoring/tests/rs/primary_mismatched_me_not_removed.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_mismatched_me_not_removed.yml rename to specifications/server-discovery-and-monitoring/tests/rs/primary_mismatched_me_not_removed.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_reports_new_member.json b/specifications/server-discovery-and-monitoring/tests/rs/primary_reports_new_member.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_reports_new_member.json rename to specifications/server-discovery-and-monitoring/tests/rs/primary_reports_new_member.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_reports_new_member.yml b/specifications/server-discovery-and-monitoring/tests/rs/primary_reports_new_member.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_reports_new_member.yml rename to specifications/server-discovery-and-monitoring/tests/rs/primary_reports_new_member.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_to_no_primary_mismatched_me.json b/specifications/server-discovery-and-monitoring/tests/rs/primary_to_no_primary_mismatched_me.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_to_no_primary_mismatched_me.json rename to specifications/server-discovery-and-monitoring/tests/rs/primary_to_no_primary_mismatched_me.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_to_no_primary_mismatched_me.yml b/specifications/server-discovery-and-monitoring/tests/rs/primary_to_no_primary_mismatched_me.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_to_no_primary_mismatched_me.yml rename to specifications/server-discovery-and-monitoring/tests/rs/primary_to_no_primary_mismatched_me.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_wrong_set_name.json b/specifications/server-discovery-and-monitoring/tests/rs/primary_wrong_set_name.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_wrong_set_name.json rename to specifications/server-discovery-and-monitoring/tests/rs/primary_wrong_set_name.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_wrong_set_name.yml b/specifications/server-discovery-and-monitoring/tests/rs/primary_wrong_set_name.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/primary_wrong_set_name.yml rename to specifications/server-discovery-and-monitoring/tests/rs/primary_wrong_set_name.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/repeated.json b/specifications/server-discovery-and-monitoring/tests/rs/repeated.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/repeated.json rename to specifications/server-discovery-and-monitoring/tests/rs/repeated.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/repeated.yml b/specifications/server-discovery-and-monitoring/tests/rs/repeated.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/repeated.yml rename to specifications/server-discovery-and-monitoring/tests/rs/repeated.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/replicaset_rsnp.json b/specifications/server-discovery-and-monitoring/tests/rs/replicaset_rsnp.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/replicaset_rsnp.json rename to specifications/server-discovery-and-monitoring/tests/rs/replicaset_rsnp.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/replicaset_rsnp.yml b/specifications/server-discovery-and-monitoring/tests/rs/replicaset_rsnp.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/replicaset_rsnp.yml rename to specifications/server-discovery-and-monitoring/tests/rs/replicaset_rsnp.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/response_from_removed.json b/specifications/server-discovery-and-monitoring/tests/rs/response_from_removed.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/response_from_removed.json rename to specifications/server-discovery-and-monitoring/tests/rs/response_from_removed.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/response_from_removed.yml b/specifications/server-discovery-and-monitoring/tests/rs/response_from_removed.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/response_from_removed.yml rename to specifications/server-discovery-and-monitoring/tests/rs/response_from_removed.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/sec_not_auth.json b/specifications/server-discovery-and-monitoring/tests/rs/sec_not_auth.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/sec_not_auth.json rename to specifications/server-discovery-and-monitoring/tests/rs/sec_not_auth.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/sec_not_auth.yml b/specifications/server-discovery-and-monitoring/tests/rs/sec_not_auth.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/sec_not_auth.yml rename to specifications/server-discovery-and-monitoring/tests/rs/sec_not_auth.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/secondary_ignore_ok_0.json b/specifications/server-discovery-and-monitoring/tests/rs/secondary_ignore_ok_0.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/secondary_ignore_ok_0.json rename to specifications/server-discovery-and-monitoring/tests/rs/secondary_ignore_ok_0.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/secondary_ignore_ok_0.yml b/specifications/server-discovery-and-monitoring/tests/rs/secondary_ignore_ok_0.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/secondary_ignore_ok_0.yml rename to specifications/server-discovery-and-monitoring/tests/rs/secondary_ignore_ok_0.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/secondary_mismatched_me.json b/specifications/server-discovery-and-monitoring/tests/rs/secondary_mismatched_me.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/secondary_mismatched_me.json rename to specifications/server-discovery-and-monitoring/tests/rs/secondary_mismatched_me.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/secondary_mismatched_me.yml b/specifications/server-discovery-and-monitoring/tests/rs/secondary_mismatched_me.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/secondary_mismatched_me.yml rename to specifications/server-discovery-and-monitoring/tests/rs/secondary_mismatched_me.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/secondary_wrong_set_name.json b/specifications/server-discovery-and-monitoring/tests/rs/secondary_wrong_set_name.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/secondary_wrong_set_name.json rename to specifications/server-discovery-and-monitoring/tests/rs/secondary_wrong_set_name.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/secondary_wrong_set_name.yml b/specifications/server-discovery-and-monitoring/tests/rs/secondary_wrong_set_name.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/secondary_wrong_set_name.yml rename to specifications/server-discovery-and-monitoring/tests/rs/secondary_wrong_set_name.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/secondary_wrong_set_name_with_primary.json b/specifications/server-discovery-and-monitoring/tests/rs/secondary_wrong_set_name_with_primary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/secondary_wrong_set_name_with_primary.json rename to specifications/server-discovery-and-monitoring/tests/rs/secondary_wrong_set_name_with_primary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/secondary_wrong_set_name_with_primary.yml b/specifications/server-discovery-and-monitoring/tests/rs/secondary_wrong_set_name_with_primary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/secondary_wrong_set_name_with_primary.yml rename to specifications/server-discovery-and-monitoring/tests/rs/secondary_wrong_set_name_with_primary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/setversion_without_electionid.json b/specifications/server-discovery-and-monitoring/tests/rs/setversion_without_electionid.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/setversion_without_electionid.json rename to specifications/server-discovery-and-monitoring/tests/rs/setversion_without_electionid.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/setversion_without_electionid.yml b/specifications/server-discovery-and-monitoring/tests/rs/setversion_without_electionid.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/setversion_without_electionid.yml rename to specifications/server-discovery-and-monitoring/tests/rs/setversion_without_electionid.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/stepdown_change_set_name.json b/specifications/server-discovery-and-monitoring/tests/rs/stepdown_change_set_name.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/stepdown_change_set_name.json rename to specifications/server-discovery-and-monitoring/tests/rs/stepdown_change_set_name.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/stepdown_change_set_name.yml b/specifications/server-discovery-and-monitoring/tests/rs/stepdown_change_set_name.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/stepdown_change_set_name.yml rename to specifications/server-discovery-and-monitoring/tests/rs/stepdown_change_set_name.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/too_new.json b/specifications/server-discovery-and-monitoring/tests/rs/too_new.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/too_new.json rename to specifications/server-discovery-and-monitoring/tests/rs/too_new.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/too_new.yml b/specifications/server-discovery-and-monitoring/tests/rs/too_new.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/too_new.yml rename to specifications/server-discovery-and-monitoring/tests/rs/too_new.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/too_old.json b/specifications/server-discovery-and-monitoring/tests/rs/too_old.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/too_old.json rename to specifications/server-discovery-and-monitoring/tests/rs/too_old.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/too_old.yml b/specifications/server-discovery-and-monitoring/tests/rs/too_old.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/too_old.yml rename to specifications/server-discovery-and-monitoring/tests/rs/too_old.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/topology_version_equal.json b/specifications/server-discovery-and-monitoring/tests/rs/topology_version_equal.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/topology_version_equal.json rename to specifications/server-discovery-and-monitoring/tests/rs/topology_version_equal.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/topology_version_equal.yml b/specifications/server-discovery-and-monitoring/tests/rs/topology_version_equal.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/topology_version_equal.yml rename to specifications/server-discovery-and-monitoring/tests/rs/topology_version_equal.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/topology_version_greater.json b/specifications/server-discovery-and-monitoring/tests/rs/topology_version_greater.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/topology_version_greater.json rename to specifications/server-discovery-and-monitoring/tests/rs/topology_version_greater.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/topology_version_greater.yml b/specifications/server-discovery-and-monitoring/tests/rs/topology_version_greater.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/topology_version_greater.yml rename to specifications/server-discovery-and-monitoring/tests/rs/topology_version_greater.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/topology_version_less.json b/specifications/server-discovery-and-monitoring/tests/rs/topology_version_less.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/topology_version_less.json rename to specifications/server-discovery-and-monitoring/tests/rs/topology_version_less.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/topology_version_less.yml b/specifications/server-discovery-and-monitoring/tests/rs/topology_version_less.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/topology_version_less.yml rename to specifications/server-discovery-and-monitoring/tests/rs/topology_version_less.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/unexpected_mongos.json b/specifications/server-discovery-and-monitoring/tests/rs/unexpected_mongos.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/unexpected_mongos.json rename to specifications/server-discovery-and-monitoring/tests/rs/unexpected_mongos.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/unexpected_mongos.yml b/specifications/server-discovery-and-monitoring/tests/rs/unexpected_mongos.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/unexpected_mongos.yml rename to specifications/server-discovery-and-monitoring/tests/rs/unexpected_mongos.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/use_setversion_without_electionid.json b/specifications/server-discovery-and-monitoring/tests/rs/use_setversion_without_electionid.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/use_setversion_without_electionid.json rename to specifications/server-discovery-and-monitoring/tests/rs/use_setversion_without_electionid.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/use_setversion_without_electionid.yml b/specifications/server-discovery-and-monitoring/tests/rs/use_setversion_without_electionid.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/use_setversion_without_electionid.yml rename to specifications/server-discovery-and-monitoring/tests/rs/use_setversion_without_electionid.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/wrong_set_name.json b/specifications/server-discovery-and-monitoring/tests/rs/wrong_set_name.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/wrong_set_name.json rename to specifications/server-discovery-and-monitoring/tests/rs/wrong_set_name.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/wrong_set_name.yml b/specifications/server-discovery-and-monitoring/tests/rs/wrong_set_name.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/rs/wrong_set_name.yml rename to specifications/server-discovery-and-monitoring/tests/rs/wrong_set_name.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/sharded/compatible.json b/specifications/server-discovery-and-monitoring/tests/sharded/compatible.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/sharded/compatible.json rename to specifications/server-discovery-and-monitoring/tests/sharded/compatible.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/sharded/compatible.yml b/specifications/server-discovery-and-monitoring/tests/sharded/compatible.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/sharded/compatible.yml rename to specifications/server-discovery-and-monitoring/tests/sharded/compatible.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/sharded/discover_single_mongos.json b/specifications/server-discovery-and-monitoring/tests/sharded/discover_single_mongos.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/sharded/discover_single_mongos.json rename to specifications/server-discovery-and-monitoring/tests/sharded/discover_single_mongos.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/sharded/discover_single_mongos.yml b/specifications/server-discovery-and-monitoring/tests/sharded/discover_single_mongos.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/sharded/discover_single_mongos.yml rename to specifications/server-discovery-and-monitoring/tests/sharded/discover_single_mongos.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/sharded/ls_timeout_mongos.json b/specifications/server-discovery-and-monitoring/tests/sharded/ls_timeout_mongos.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/sharded/ls_timeout_mongos.json rename to specifications/server-discovery-and-monitoring/tests/sharded/ls_timeout_mongos.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/sharded/ls_timeout_mongos.yml b/specifications/server-discovery-and-monitoring/tests/sharded/ls_timeout_mongos.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/sharded/ls_timeout_mongos.yml rename to specifications/server-discovery-and-monitoring/tests/sharded/ls_timeout_mongos.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/sharded/mongos_disconnect.json b/specifications/server-discovery-and-monitoring/tests/sharded/mongos_disconnect.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/sharded/mongos_disconnect.json rename to specifications/server-discovery-and-monitoring/tests/sharded/mongos_disconnect.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/sharded/mongos_disconnect.yml b/specifications/server-discovery-and-monitoring/tests/sharded/mongos_disconnect.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/sharded/mongos_disconnect.yml rename to specifications/server-discovery-and-monitoring/tests/sharded/mongos_disconnect.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/sharded/multiple_mongoses.json b/specifications/server-discovery-and-monitoring/tests/sharded/multiple_mongoses.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/sharded/multiple_mongoses.json rename to specifications/server-discovery-and-monitoring/tests/sharded/multiple_mongoses.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/sharded/multiple_mongoses.yml b/specifications/server-discovery-and-monitoring/tests/sharded/multiple_mongoses.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/sharded/multiple_mongoses.yml rename to specifications/server-discovery-and-monitoring/tests/sharded/multiple_mongoses.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/sharded/non_mongos_removed.json b/specifications/server-discovery-and-monitoring/tests/sharded/non_mongos_removed.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/sharded/non_mongos_removed.json rename to specifications/server-discovery-and-monitoring/tests/sharded/non_mongos_removed.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/sharded/non_mongos_removed.yml b/specifications/server-discovery-and-monitoring/tests/sharded/non_mongos_removed.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/sharded/non_mongos_removed.yml rename to specifications/server-discovery-and-monitoring/tests/sharded/non_mongos_removed.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/sharded/normalize_uri_case.json b/specifications/server-discovery-and-monitoring/tests/sharded/normalize_uri_case.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/sharded/normalize_uri_case.json rename to specifications/server-discovery-and-monitoring/tests/sharded/normalize_uri_case.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/sharded/normalize_uri_case.yml b/specifications/server-discovery-and-monitoring/tests/sharded/normalize_uri_case.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/sharded/normalize_uri_case.yml rename to specifications/server-discovery-and-monitoring/tests/sharded/normalize_uri_case.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/sharded/too_new.json b/specifications/server-discovery-and-monitoring/tests/sharded/too_new.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/sharded/too_new.json rename to specifications/server-discovery-and-monitoring/tests/sharded/too_new.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/sharded/too_new.yml b/specifications/server-discovery-and-monitoring/tests/sharded/too_new.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/sharded/too_new.yml rename to specifications/server-discovery-and-monitoring/tests/sharded/too_new.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/sharded/too_old.json b/specifications/server-discovery-and-monitoring/tests/sharded/too_old.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/sharded/too_old.json rename to specifications/server-discovery-and-monitoring/tests/sharded/too_old.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/sharded/too_old.yml b/specifications/server-discovery-and-monitoring/tests/sharded/too_old.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/sharded/too_old.yml rename to specifications/server-discovery-and-monitoring/tests/sharded/too_old.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/compatible.json b/specifications/server-discovery-and-monitoring/tests/single/compatible.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/compatible.json rename to specifications/server-discovery-and-monitoring/tests/single/compatible.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/compatible.yml b/specifications/server-discovery-and-monitoring/tests/single/compatible.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/compatible.yml rename to specifications/server-discovery-and-monitoring/tests/single/compatible.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/direct_connection_external_ip.json b/specifications/server-discovery-and-monitoring/tests/single/direct_connection_external_ip.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/direct_connection_external_ip.json rename to specifications/server-discovery-and-monitoring/tests/single/direct_connection_external_ip.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/direct_connection_external_ip.yml b/specifications/server-discovery-and-monitoring/tests/single/direct_connection_external_ip.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/direct_connection_external_ip.yml rename to specifications/server-discovery-and-monitoring/tests/single/direct_connection_external_ip.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/direct_connection_mongos.json b/specifications/server-discovery-and-monitoring/tests/single/direct_connection_mongos.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/direct_connection_mongos.json rename to specifications/server-discovery-and-monitoring/tests/single/direct_connection_mongos.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/direct_connection_mongos.yml b/specifications/server-discovery-and-monitoring/tests/single/direct_connection_mongos.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/direct_connection_mongos.yml rename to specifications/server-discovery-and-monitoring/tests/single/direct_connection_mongos.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/direct_connection_replicaset.json b/specifications/server-discovery-and-monitoring/tests/single/direct_connection_replicaset.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/direct_connection_replicaset.json rename to specifications/server-discovery-and-monitoring/tests/single/direct_connection_replicaset.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/direct_connection_replicaset.yml b/specifications/server-discovery-and-monitoring/tests/single/direct_connection_replicaset.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/direct_connection_replicaset.yml rename to specifications/server-discovery-and-monitoring/tests/single/direct_connection_replicaset.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/direct_connection_rsarbiter.json b/specifications/server-discovery-and-monitoring/tests/single/direct_connection_rsarbiter.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/direct_connection_rsarbiter.json rename to specifications/server-discovery-and-monitoring/tests/single/direct_connection_rsarbiter.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/direct_connection_rsarbiter.yml b/specifications/server-discovery-and-monitoring/tests/single/direct_connection_rsarbiter.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/direct_connection_rsarbiter.yml rename to specifications/server-discovery-and-monitoring/tests/single/direct_connection_rsarbiter.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/direct_connection_rsprimary.json b/specifications/server-discovery-and-monitoring/tests/single/direct_connection_rsprimary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/direct_connection_rsprimary.json rename to specifications/server-discovery-and-monitoring/tests/single/direct_connection_rsprimary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/direct_connection_rsprimary.yml b/specifications/server-discovery-and-monitoring/tests/single/direct_connection_rsprimary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/direct_connection_rsprimary.yml rename to specifications/server-discovery-and-monitoring/tests/single/direct_connection_rsprimary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/direct_connection_rssecondary.json b/specifications/server-discovery-and-monitoring/tests/single/direct_connection_rssecondary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/direct_connection_rssecondary.json rename to specifications/server-discovery-and-monitoring/tests/single/direct_connection_rssecondary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/direct_connection_rssecondary.yml b/specifications/server-discovery-and-monitoring/tests/single/direct_connection_rssecondary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/direct_connection_rssecondary.yml rename to specifications/server-discovery-and-monitoring/tests/single/direct_connection_rssecondary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/direct_connection_standalone.json b/specifications/server-discovery-and-monitoring/tests/single/direct_connection_standalone.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/direct_connection_standalone.json rename to specifications/server-discovery-and-monitoring/tests/single/direct_connection_standalone.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/direct_connection_standalone.yml b/specifications/server-discovery-and-monitoring/tests/single/direct_connection_standalone.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/direct_connection_standalone.yml rename to specifications/server-discovery-and-monitoring/tests/single/direct_connection_standalone.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/direct_connection_unavailable_seed.json b/specifications/server-discovery-and-monitoring/tests/single/direct_connection_unavailable_seed.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/direct_connection_unavailable_seed.json rename to specifications/server-discovery-and-monitoring/tests/single/direct_connection_unavailable_seed.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/direct_connection_unavailable_seed.yml b/specifications/server-discovery-and-monitoring/tests/single/direct_connection_unavailable_seed.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/direct_connection_unavailable_seed.yml rename to specifications/server-discovery-and-monitoring/tests/single/direct_connection_unavailable_seed.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/direct_connection_wrong_set_name.json b/specifications/server-discovery-and-monitoring/tests/single/direct_connection_wrong_set_name.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/direct_connection_wrong_set_name.json rename to specifications/server-discovery-and-monitoring/tests/single/direct_connection_wrong_set_name.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/direct_connection_wrong_set_name.yml b/specifications/server-discovery-and-monitoring/tests/single/direct_connection_wrong_set_name.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/direct_connection_wrong_set_name.yml rename to specifications/server-discovery-and-monitoring/tests/single/direct_connection_wrong_set_name.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/discover_standalone.json b/specifications/server-discovery-and-monitoring/tests/single/discover_standalone.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/discover_standalone.json rename to specifications/server-discovery-and-monitoring/tests/single/discover_standalone.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/discover_standalone.yml b/specifications/server-discovery-and-monitoring/tests/single/discover_standalone.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/discover_standalone.yml rename to specifications/server-discovery-and-monitoring/tests/single/discover_standalone.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/discover_unavailable_seed.json b/specifications/server-discovery-and-monitoring/tests/single/discover_unavailable_seed.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/discover_unavailable_seed.json rename to specifications/server-discovery-and-monitoring/tests/single/discover_unavailable_seed.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/discover_unavailable_seed.yml b/specifications/server-discovery-and-monitoring/tests/single/discover_unavailable_seed.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/discover_unavailable_seed.yml rename to specifications/server-discovery-and-monitoring/tests/single/discover_unavailable_seed.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/ls_timeout_standalone.json b/specifications/server-discovery-and-monitoring/tests/single/ls_timeout_standalone.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/ls_timeout_standalone.json rename to specifications/server-discovery-and-monitoring/tests/single/ls_timeout_standalone.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/ls_timeout_standalone.yml b/specifications/server-discovery-and-monitoring/tests/single/ls_timeout_standalone.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/ls_timeout_standalone.yml rename to specifications/server-discovery-and-monitoring/tests/single/ls_timeout_standalone.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/not_ok_response.json b/specifications/server-discovery-and-monitoring/tests/single/not_ok_response.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/not_ok_response.json rename to specifications/server-discovery-and-monitoring/tests/single/not_ok_response.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/not_ok_response.yml b/specifications/server-discovery-and-monitoring/tests/single/not_ok_response.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/not_ok_response.yml rename to specifications/server-discovery-and-monitoring/tests/single/not_ok_response.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/standalone_removed.json b/specifications/server-discovery-and-monitoring/tests/single/standalone_removed.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/standalone_removed.json rename to specifications/server-discovery-and-monitoring/tests/single/standalone_removed.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/standalone_removed.yml b/specifications/server-discovery-and-monitoring/tests/single/standalone_removed.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/standalone_removed.yml rename to specifications/server-discovery-and-monitoring/tests/single/standalone_removed.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/standalone_using_legacy_hello.json b/specifications/server-discovery-and-monitoring/tests/single/standalone_using_legacy_hello.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/standalone_using_legacy_hello.json rename to specifications/server-discovery-and-monitoring/tests/single/standalone_using_legacy_hello.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/standalone_using_legacy_hello.yml b/specifications/server-discovery-and-monitoring/tests/single/standalone_using_legacy_hello.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/standalone_using_legacy_hello.yml rename to specifications/server-discovery-and-monitoring/tests/single/standalone_using_legacy_hello.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/too_new.json b/specifications/server-discovery-and-monitoring/tests/single/too_new.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/too_new.json rename to specifications/server-discovery-and-monitoring/tests/single/too_new.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/too_new.yml b/specifications/server-discovery-and-monitoring/tests/single/too_new.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/too_new.yml rename to specifications/server-discovery-and-monitoring/tests/single/too_new.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/too_old.json b/specifications/server-discovery-and-monitoring/tests/single/too_old.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/too_old.json rename to specifications/server-discovery-and-monitoring/tests/single/too_old.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/too_old.yml b/specifications/server-discovery-and-monitoring/tests/single/too_old.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/too_old.yml rename to specifications/server-discovery-and-monitoring/tests/single/too_old.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/too_old_then_upgraded.json b/specifications/server-discovery-and-monitoring/tests/single/too_old_then_upgraded.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/too_old_then_upgraded.json rename to specifications/server-discovery-and-monitoring/tests/single/too_old_then_upgraded.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/too_old_then_upgraded.yml b/specifications/server-discovery-and-monitoring/tests/single/too_old_then_upgraded.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/tests/single/too_old_then_upgraded.yml rename to specifications/server-discovery-and-monitoring/tests/single/too_old_then_upgraded.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/README.rst b/specifications/server-selection/tests/README.rst similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/README.rst rename to specifications/server-selection/tests/README.rst diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/in_window/equilibrium.json b/specifications/server-selection/tests/in_window/equilibrium.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/in_window/equilibrium.json rename to specifications/server-selection/tests/in_window/equilibrium.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/in_window/equilibrium.yml b/specifications/server-selection/tests/in_window/equilibrium.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/in_window/equilibrium.yml rename to specifications/server-selection/tests/in_window/equilibrium.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/in_window/many-choices.json b/specifications/server-selection/tests/in_window/many-choices.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/in_window/many-choices.json rename to specifications/server-selection/tests/in_window/many-choices.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/in_window/many-choices.yml b/specifications/server-selection/tests/in_window/many-choices.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/in_window/many-choices.yml rename to specifications/server-selection/tests/in_window/many-choices.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/in_window/one-least-two-tied.json b/specifications/server-selection/tests/in_window/one-least-two-tied.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/in_window/one-least-two-tied.json rename to specifications/server-selection/tests/in_window/one-least-two-tied.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/in_window/one-least-two-tied.yml b/specifications/server-selection/tests/in_window/one-least-two-tied.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/in_window/one-least-two-tied.yml rename to specifications/server-selection/tests/in_window/one-least-two-tied.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/in_window/rs-equilibrium.json b/specifications/server-selection/tests/in_window/rs-equilibrium.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/in_window/rs-equilibrium.json rename to specifications/server-selection/tests/in_window/rs-equilibrium.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/in_window/rs-equilibrium.yml b/specifications/server-selection/tests/in_window/rs-equilibrium.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/in_window/rs-equilibrium.yml rename to specifications/server-selection/tests/in_window/rs-equilibrium.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/in_window/rs-three-choices.json b/specifications/server-selection/tests/in_window/rs-three-choices.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/in_window/rs-three-choices.json rename to specifications/server-selection/tests/in_window/rs-three-choices.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/in_window/rs-three-choices.yml b/specifications/server-selection/tests/in_window/rs-three-choices.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/in_window/rs-three-choices.yml rename to specifications/server-selection/tests/in_window/rs-three-choices.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/in_window/three-choices.json b/specifications/server-selection/tests/in_window/three-choices.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/in_window/three-choices.json rename to specifications/server-selection/tests/in_window/three-choices.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/in_window/three-choices.yml b/specifications/server-selection/tests/in_window/three-choices.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/in_window/three-choices.yml rename to specifications/server-selection/tests/in_window/three-choices.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/in_window/two-choices.json b/specifications/server-selection/tests/in_window/two-choices.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/in_window/two-choices.json rename to specifications/server-selection/tests/in_window/two-choices.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/in_window/two-choices.yml b/specifications/server-selection/tests/in_window/two-choices.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/in_window/two-choices.yml rename to specifications/server-selection/tests/in_window/two-choices.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/in_window/two-least.json b/specifications/server-selection/tests/in_window/two-least.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/in_window/two-least.json rename to specifications/server-selection/tests/in_window/two-least.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/in_window/two-least.yml b/specifications/server-selection/tests/in_window/two-least.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/in_window/two-least.yml rename to specifications/server-selection/tests/in_window/two-least.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/rtt/first_value.json b/specifications/server-selection/tests/rtt/first_value.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/rtt/first_value.json rename to specifications/server-selection/tests/rtt/first_value.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/rtt/first_value.yml b/specifications/server-selection/tests/rtt/first_value.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/rtt/first_value.yml rename to specifications/server-selection/tests/rtt/first_value.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/rtt/first_value_zero.json b/specifications/server-selection/tests/rtt/first_value_zero.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/rtt/first_value_zero.json rename to specifications/server-selection/tests/rtt/first_value_zero.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/rtt/first_value_zero.yml b/specifications/server-selection/tests/rtt/first_value_zero.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/rtt/first_value_zero.yml rename to specifications/server-selection/tests/rtt/first_value_zero.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/rtt/value_test_1.json b/specifications/server-selection/tests/rtt/value_test_1.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/rtt/value_test_1.json rename to specifications/server-selection/tests/rtt/value_test_1.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/rtt/value_test_1.yml b/specifications/server-selection/tests/rtt/value_test_1.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/rtt/value_test_1.yml rename to specifications/server-selection/tests/rtt/value_test_1.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/rtt/value_test_2.json b/specifications/server-selection/tests/rtt/value_test_2.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/rtt/value_test_2.json rename to specifications/server-selection/tests/rtt/value_test_2.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/rtt/value_test_2.yml b/specifications/server-selection/tests/rtt/value_test_2.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/rtt/value_test_2.yml rename to specifications/server-selection/tests/rtt/value_test_2.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/rtt/value_test_3.json b/specifications/server-selection/tests/rtt/value_test_3.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/rtt/value_test_3.json rename to specifications/server-selection/tests/rtt/value_test_3.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/rtt/value_test_3.yml b/specifications/server-selection/tests/rtt/value_test_3.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/rtt/value_test_3.yml rename to specifications/server-selection/tests/rtt/value_test_3.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/rtt/value_test_4.json b/specifications/server-selection/tests/rtt/value_test_4.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/rtt/value_test_4.json rename to specifications/server-selection/tests/rtt/value_test_4.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/rtt/value_test_4.yml b/specifications/server-selection/tests/rtt/value_test_4.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/rtt/value_test_4.yml rename to specifications/server-selection/tests/rtt/value_test_4.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/rtt/value_test_5.json b/specifications/server-selection/tests/rtt/value_test_5.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/rtt/value_test_5.json rename to specifications/server-selection/tests/rtt/value_test_5.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/rtt/value_test_5.yml b/specifications/server-selection/tests/rtt/value_test_5.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/rtt/value_test_5.yml rename to specifications/server-selection/tests/rtt/value_test_5.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/LoadBalanced/read/Nearest.json b/specifications/server-selection/tests/server_selection/LoadBalanced/read/Nearest.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/LoadBalanced/read/Nearest.json rename to specifications/server-selection/tests/server_selection/LoadBalanced/read/Nearest.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/LoadBalanced/read/Primary.json b/specifications/server-selection/tests/server_selection/LoadBalanced/read/Primary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/LoadBalanced/read/Primary.json rename to specifications/server-selection/tests/server_selection/LoadBalanced/read/Primary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/LoadBalanced/read/PrimaryPreferred.json b/specifications/server-selection/tests/server_selection/LoadBalanced/read/PrimaryPreferred.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/LoadBalanced/read/PrimaryPreferred.json rename to specifications/server-selection/tests/server_selection/LoadBalanced/read/PrimaryPreferred.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/LoadBalanced/read/Secondary.json b/specifications/server-selection/tests/server_selection/LoadBalanced/read/Secondary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/LoadBalanced/read/Secondary.json rename to specifications/server-selection/tests/server_selection/LoadBalanced/read/Secondary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/LoadBalanced/read/SecondaryPreferred.json b/specifications/server-selection/tests/server_selection/LoadBalanced/read/SecondaryPreferred.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/LoadBalanced/read/SecondaryPreferred.json rename to specifications/server-selection/tests/server_selection/LoadBalanced/read/SecondaryPreferred.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/LoadBalanced/write/Nearest.json b/specifications/server-selection/tests/server_selection/LoadBalanced/write/Nearest.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/LoadBalanced/write/Nearest.json rename to specifications/server-selection/tests/server_selection/LoadBalanced/write/Nearest.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/LoadBalanced/write/Primary.json b/specifications/server-selection/tests/server_selection/LoadBalanced/write/Primary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/LoadBalanced/write/Primary.json rename to specifications/server-selection/tests/server_selection/LoadBalanced/write/Primary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/LoadBalanced/write/PrimaryPreferred.json b/specifications/server-selection/tests/server_selection/LoadBalanced/write/PrimaryPreferred.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/LoadBalanced/write/PrimaryPreferred.json rename to specifications/server-selection/tests/server_selection/LoadBalanced/write/PrimaryPreferred.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/LoadBalanced/write/Secondary.json b/specifications/server-selection/tests/server_selection/LoadBalanced/write/Secondary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/LoadBalanced/write/Secondary.json rename to specifications/server-selection/tests/server_selection/LoadBalanced/write/Secondary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/LoadBalanced/write/SecondaryPreferred.json b/specifications/server-selection/tests/server_selection/LoadBalanced/write/SecondaryPreferred.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/LoadBalanced/write/SecondaryPreferred.json rename to specifications/server-selection/tests/server_selection/LoadBalanced/write/SecondaryPreferred.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Nearest.json b/specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Nearest.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Nearest.json rename to specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Nearest.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Nearest.yml b/specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Nearest.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Nearest.yml rename to specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Nearest.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Nearest_multiple.json b/specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Nearest_multiple.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Nearest_multiple.json rename to specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Nearest_multiple.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Nearest_multiple.yml b/specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Nearest_multiple.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Nearest_multiple.yml rename to specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Nearest_multiple.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Nearest_non_matching.json b/specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Nearest_non_matching.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Nearest_non_matching.json rename to specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Nearest_non_matching.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Nearest_non_matching.yml b/specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Nearest_non_matching.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Nearest_non_matching.yml rename to specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Nearest_non_matching.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Primary.json b/specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Primary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Primary.json rename to specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Primary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Primary.yml b/specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Primary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Primary.yml rename to specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Primary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/PrimaryPreferred.json b/specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/PrimaryPreferred.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/PrimaryPreferred.json rename to specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/PrimaryPreferred.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/PrimaryPreferred.yml b/specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/PrimaryPreferred.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/PrimaryPreferred.yml rename to specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/PrimaryPreferred.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/PrimaryPreferred_non_matching.json b/specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/PrimaryPreferred_non_matching.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/PrimaryPreferred_non_matching.json rename to specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/PrimaryPreferred_non_matching.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/PrimaryPreferred_non_matching.yml b/specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/PrimaryPreferred_non_matching.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/PrimaryPreferred_non_matching.yml rename to specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/PrimaryPreferred_non_matching.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Secondary.json b/specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Secondary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Secondary.json rename to specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Secondary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Secondary.yml b/specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Secondary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Secondary.yml rename to specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Secondary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/SecondaryPreferred.json b/specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/SecondaryPreferred.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/SecondaryPreferred.json rename to specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/SecondaryPreferred.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/SecondaryPreferred.yml b/specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/SecondaryPreferred.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/SecondaryPreferred.yml rename to specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/SecondaryPreferred.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/SecondaryPreferred_non_matching.json b/specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/SecondaryPreferred_non_matching.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/SecondaryPreferred_non_matching.json rename to specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/SecondaryPreferred_non_matching.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/SecondaryPreferred_non_matching.yml b/specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/SecondaryPreferred_non_matching.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/SecondaryPreferred_non_matching.yml rename to specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/SecondaryPreferred_non_matching.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Secondary_multi_tags.json b/specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Secondary_multi_tags.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Secondary_multi_tags.json rename to specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Secondary_multi_tags.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Secondary_multi_tags.yml b/specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Secondary_multi_tags.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Secondary_multi_tags.yml rename to specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Secondary_multi_tags.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Secondary_multi_tags2.json b/specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Secondary_multi_tags2.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Secondary_multi_tags2.json rename to specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Secondary_multi_tags2.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Secondary_multi_tags2.yml b/specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Secondary_multi_tags2.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Secondary_multi_tags2.yml rename to specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Secondary_multi_tags2.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Secondary_non_matching.json b/specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Secondary_non_matching.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Secondary_non_matching.json rename to specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Secondary_non_matching.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Secondary_non_matching.yml b/specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Secondary_non_matching.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Secondary_non_matching.yml rename to specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/Secondary_non_matching.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/write/SecondaryPreferred.json b/specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/write/SecondaryPreferred.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/write/SecondaryPreferred.json rename to specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/write/SecondaryPreferred.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/write/SecondaryPreferred.yml b/specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/write/SecondaryPreferred.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/write/SecondaryPreferred.yml rename to specifications/server-selection/tests/server_selection/ReplicaSetNoPrimary/write/SecondaryPreferred.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Nearest.json b/specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Nearest.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Nearest.json rename to specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Nearest.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Nearest.yml b/specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Nearest.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Nearest.yml rename to specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Nearest.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Nearest_multiple.json b/specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Nearest_multiple.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Nearest_multiple.json rename to specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Nearest_multiple.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Nearest_multiple.yml b/specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Nearest_multiple.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Nearest_multiple.yml rename to specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Nearest_multiple.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Nearest_non_matching.json b/specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Nearest_non_matching.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Nearest_non_matching.json rename to specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Nearest_non_matching.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Nearest_non_matching.yml b/specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Nearest_non_matching.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Nearest_non_matching.yml rename to specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Nearest_non_matching.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Primary.json b/specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Primary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Primary.json rename to specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Primary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Primary.yml b/specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Primary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Primary.yml rename to specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Primary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/PrimaryPreferred.json b/specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/PrimaryPreferred.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/PrimaryPreferred.json rename to specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/PrimaryPreferred.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/PrimaryPreferred.yml b/specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/PrimaryPreferred.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/PrimaryPreferred.yml rename to specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/PrimaryPreferred.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/PrimaryPreferred_non_matching.json b/specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/PrimaryPreferred_non_matching.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/PrimaryPreferred_non_matching.json rename to specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/PrimaryPreferred_non_matching.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/PrimaryPreferred_non_matching.yml b/specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/PrimaryPreferred_non_matching.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/PrimaryPreferred_non_matching.yml rename to specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/PrimaryPreferred_non_matching.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Secondary.json b/specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Secondary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Secondary.json rename to specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Secondary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Secondary.yml b/specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Secondary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Secondary.yml rename to specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Secondary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred.json b/specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred.json rename to specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred.yml b/specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred.yml rename to specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred_non_matching.json b/specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred_non_matching.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred_non_matching.json rename to specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred_non_matching.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred_non_matching.yml b/specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred_non_matching.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred_non_matching.yml rename to specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred_non_matching.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred_tags.json b/specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred_tags.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred_tags.json rename to specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred_tags.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred_tags.yml b/specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred_tags.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred_tags.yml rename to specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred_tags.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Secondary_non_matching.json b/specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Secondary_non_matching.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Secondary_non_matching.json rename to specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Secondary_non_matching.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Secondary_non_matching.yml b/specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Secondary_non_matching.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Secondary_non_matching.yml rename to specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/Secondary_non_matching.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/write/SecondaryPreferred.json b/specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/write/SecondaryPreferred.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/write/SecondaryPreferred.json rename to specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/write/SecondaryPreferred.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/write/SecondaryPreferred.yml b/specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/write/SecondaryPreferred.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/write/SecondaryPreferred.yml rename to specifications/server-selection/tests/server_selection/ReplicaSetWithPrimary/write/SecondaryPreferred.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/read/Nearest.json b/specifications/server-selection/tests/server_selection/Sharded/read/Nearest.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/read/Nearest.json rename to specifications/server-selection/tests/server_selection/Sharded/read/Nearest.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/read/Nearest.yml b/specifications/server-selection/tests/server_selection/Sharded/read/Nearest.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/read/Nearest.yml rename to specifications/server-selection/tests/server_selection/Sharded/read/Nearest.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/read/Primary.json b/specifications/server-selection/tests/server_selection/Sharded/read/Primary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/read/Primary.json rename to specifications/server-selection/tests/server_selection/Sharded/read/Primary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/read/Primary.yml b/specifications/server-selection/tests/server_selection/Sharded/read/Primary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/read/Primary.yml rename to specifications/server-selection/tests/server_selection/Sharded/read/Primary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/read/PrimaryPreferred.json b/specifications/server-selection/tests/server_selection/Sharded/read/PrimaryPreferred.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/read/PrimaryPreferred.json rename to specifications/server-selection/tests/server_selection/Sharded/read/PrimaryPreferred.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/read/PrimaryPreferred.yml b/specifications/server-selection/tests/server_selection/Sharded/read/PrimaryPreferred.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/read/PrimaryPreferred.yml rename to specifications/server-selection/tests/server_selection/Sharded/read/PrimaryPreferred.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/read/Secondary.json b/specifications/server-selection/tests/server_selection/Sharded/read/Secondary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/read/Secondary.json rename to specifications/server-selection/tests/server_selection/Sharded/read/Secondary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/read/Secondary.yml b/specifications/server-selection/tests/server_selection/Sharded/read/Secondary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/read/Secondary.yml rename to specifications/server-selection/tests/server_selection/Sharded/read/Secondary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/read/SecondaryPreferred.json b/specifications/server-selection/tests/server_selection/Sharded/read/SecondaryPreferred.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/read/SecondaryPreferred.json rename to specifications/server-selection/tests/server_selection/Sharded/read/SecondaryPreferred.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/read/SecondaryPreferred.yml b/specifications/server-selection/tests/server_selection/Sharded/read/SecondaryPreferred.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/read/SecondaryPreferred.yml rename to specifications/server-selection/tests/server_selection/Sharded/read/SecondaryPreferred.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/write/Nearest.json b/specifications/server-selection/tests/server_selection/Sharded/write/Nearest.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/write/Nearest.json rename to specifications/server-selection/tests/server_selection/Sharded/write/Nearest.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/write/Nearest.yml b/specifications/server-selection/tests/server_selection/Sharded/write/Nearest.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/write/Nearest.yml rename to specifications/server-selection/tests/server_selection/Sharded/write/Nearest.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/write/Primary.json b/specifications/server-selection/tests/server_selection/Sharded/write/Primary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/write/Primary.json rename to specifications/server-selection/tests/server_selection/Sharded/write/Primary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/write/Primary.yml b/specifications/server-selection/tests/server_selection/Sharded/write/Primary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/write/Primary.yml rename to specifications/server-selection/tests/server_selection/Sharded/write/Primary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/write/PrimaryPreferred.json b/specifications/server-selection/tests/server_selection/Sharded/write/PrimaryPreferred.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/write/PrimaryPreferred.json rename to specifications/server-selection/tests/server_selection/Sharded/write/PrimaryPreferred.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/write/PrimaryPreferred.yml b/specifications/server-selection/tests/server_selection/Sharded/write/PrimaryPreferred.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/write/PrimaryPreferred.yml rename to specifications/server-selection/tests/server_selection/Sharded/write/PrimaryPreferred.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/write/Secondary.json b/specifications/server-selection/tests/server_selection/Sharded/write/Secondary.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/write/Secondary.json rename to specifications/server-selection/tests/server_selection/Sharded/write/Secondary.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/write/Secondary.yml b/specifications/server-selection/tests/server_selection/Sharded/write/Secondary.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/write/Secondary.yml rename to specifications/server-selection/tests/server_selection/Sharded/write/Secondary.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/write/SecondaryPreferred.json b/specifications/server-selection/tests/server_selection/Sharded/write/SecondaryPreferred.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/write/SecondaryPreferred.json rename to specifications/server-selection/tests/server_selection/Sharded/write/SecondaryPreferred.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/write/SecondaryPreferred.yml b/specifications/server-selection/tests/server_selection/Sharded/write/SecondaryPreferred.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Sharded/write/SecondaryPreferred.yml rename to specifications/server-selection/tests/server_selection/Sharded/write/SecondaryPreferred.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Single/read/SecondaryPreferred.json b/specifications/server-selection/tests/server_selection/Single/read/SecondaryPreferred.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Single/read/SecondaryPreferred.json rename to specifications/server-selection/tests/server_selection/Single/read/SecondaryPreferred.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Single/read/SecondaryPreferred.yml b/specifications/server-selection/tests/server_selection/Single/read/SecondaryPreferred.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Single/read/SecondaryPreferred.yml rename to specifications/server-selection/tests/server_selection/Single/read/SecondaryPreferred.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Single/write/SecondaryPreferred.json b/specifications/server-selection/tests/server_selection/Single/write/SecondaryPreferred.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Single/write/SecondaryPreferred.json rename to specifications/server-selection/tests/server_selection/Single/write/SecondaryPreferred.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Single/write/SecondaryPreferred.yml b/specifications/server-selection/tests/server_selection/Single/write/SecondaryPreferred.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Single/write/SecondaryPreferred.yml rename to specifications/server-selection/tests/server_selection/Single/write/SecondaryPreferred.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Unknown/read/SecondaryPreferred.json b/specifications/server-selection/tests/server_selection/Unknown/read/SecondaryPreferred.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Unknown/read/SecondaryPreferred.json rename to specifications/server-selection/tests/server_selection/Unknown/read/SecondaryPreferred.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Unknown/read/SecondaryPreferred.yml b/specifications/server-selection/tests/server_selection/Unknown/read/SecondaryPreferred.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Unknown/read/SecondaryPreferred.yml rename to specifications/server-selection/tests/server_selection/Unknown/read/SecondaryPreferred.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Unknown/write/SecondaryPreferred.json b/specifications/server-selection/tests/server_selection/Unknown/write/SecondaryPreferred.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Unknown/write/SecondaryPreferred.json rename to specifications/server-selection/tests/server_selection/Unknown/write/SecondaryPreferred.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Unknown/write/SecondaryPreferred.yml b/specifications/server-selection/tests/server_selection/Unknown/write/SecondaryPreferred.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/tests/server_selection/Unknown/write/SecondaryPreferred.yml rename to specifications/server-selection/tests/server_selection/Unknown/write/SecondaryPreferred.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/sessions/tests/README.rst b/specifications/sessions/tests/README.rst similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/sessions/tests/README.rst rename to specifications/sessions/tests/README.rst diff --git a/tests/MongoDB.Driver.Tests/Specifications/sessions/tests/legacy/dirty-session-errors.json b/specifications/sessions/tests/legacy/dirty-session-errors.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/sessions/tests/legacy/dirty-session-errors.json rename to specifications/sessions/tests/legacy/dirty-session-errors.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/sessions/tests/legacy/dirty-session-errors.yml b/specifications/sessions/tests/legacy/dirty-session-errors.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/sessions/tests/legacy/dirty-session-errors.yml rename to specifications/sessions/tests/legacy/dirty-session-errors.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/sessions/tests/legacy/server-support.json b/specifications/sessions/tests/legacy/server-support.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/sessions/tests/legacy/server-support.json rename to specifications/sessions/tests/legacy/server-support.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/sessions/tests/legacy/server-support.yml b/specifications/sessions/tests/legacy/server-support.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/sessions/tests/legacy/server-support.yml rename to specifications/sessions/tests/legacy/server-support.yml diff --git a/specifications/sessions/tests/unified/implicit-sessions-default-causal-consistency.json b/specifications/sessions/tests/unified/implicit-sessions-default-causal-consistency.json new file mode 100644 index 00000000000..517c8ebc63b --- /dev/null +++ b/specifications/sessions/tests/unified/implicit-sessions-default-causal-consistency.json @@ -0,0 +1,318 @@ +{ + "description": "implicit sessions default causal consistency", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.2", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "implicit-cc-tests" + } + }, + { + "collection": { + "id": "collectionDefault", + "database": "database0", + "collectionName": "coll-default" + } + }, + { + "collection": { + "id": "collectionSnapshot", + "database": "database0", + "collectionName": "coll-snapshot", + "collectionOptions": { + "readConcern": { + "level": "snapshot" + } + } + } + }, + { + "collection": { + "id": "collectionlinearizable", + "database": "database0", + "collectionName": "coll-linearizable", + "collectionOptions": { + "readConcern": { + "level": "linearizable" + } + } + } + } + ], + "initialData": [ + { + "collectionName": "coll-default", + "databaseName": "implicit-cc-tests", + "documents": [ + { + "_id": 1, + "x": "default" + } + ] + }, + { + "collectionName": "coll-snapshot", + "databaseName": "implicit-cc-tests", + "documents": [ + { + "_id": 1, + "x": "snapshot" + } + ] + }, + { + "collectionName": "coll-linearizable", + "databaseName": "implicit-cc-tests", + "documents": [ + { + "_id": 1, + "x": "linearizable" + } + ] + } + ], + "tests": [ + { + "description": "readConcern is not sent on retried read in implicit session when readConcern level is not specified", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 11600 + } + } + } + }, + { + "name": "find", + "object": "collectionDefault", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 1, + "x": "default" + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll-default", + "filter": {}, + "readConcern": { + "$$exists": false + } + }, + "databaseName": "implicit-cc-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "coll-default", + "filter": {}, + "readConcern": { + "$$exists": false + } + }, + "databaseName": "implicit-cc-tests" + } + } + ] + } + ] + }, + { + "description": "afterClusterTime is not sent on retried read in implicit session when readConcern level is snapshot", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 11600 + } + } + } + }, + { + "name": "find", + "object": "collectionSnapshot", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 1, + "x": "snapshot" + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll-snapshot", + "filter": {}, + "readConcern": { + "level": "snapshot", + "afterClusterTime": { + "$$exists": false + } + } + }, + "databaseName": "implicit-cc-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "coll-snapshot", + "filter": {}, + "readConcern": { + "level": "snapshot", + "afterClusterTime": { + "$$exists": false + } + } + }, + "databaseName": "implicit-cc-tests" + } + } + ] + } + ] + }, + { + "description": "afterClusterTime is not sent on retried read in implicit session when readConcern level is linearizable", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 11600 + } + } + } + }, + { + "name": "find", + "object": "collectionlinearizable", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 1, + "x": "linearizable" + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll-linearizable", + "filter": {}, + "readConcern": { + "level": "linearizable", + "afterClusterTime": { + "$$exists": false + } + } + }, + "databaseName": "implicit-cc-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "coll-linearizable", + "filter": {}, + "readConcern": { + "level": "linearizable", + "afterClusterTime": { + "$$exists": false + } + } + }, + "databaseName": "implicit-cc-tests" + } + } + ] + } + ] + } + ] +} diff --git a/specifications/sessions/tests/unified/implicit-sessions-default-causal-consistency.yml b/specifications/sessions/tests/unified/implicit-sessions-default-causal-consistency.yml new file mode 100644 index 00000000000..052c6dac203 --- /dev/null +++ b/specifications/sessions/tests/unified/implicit-sessions-default-causal-consistency.yml @@ -0,0 +1,119 @@ +description: "implicit sessions default causal consistency" + +schemaVersion: "1.3" + +runOnRequirements: + - minServerVersion: "4.2" + topologies: [replicaset, sharded, load-balanced] + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: [commandStartedEvent] + - database: + id: &database0 database0 + client: *client0 + databaseName: &databaseName implicit-cc-tests + - collection: + id: &collectionDefault collectionDefault + database: *database0 + collectionName: &collectionNameDefault coll-default + - collection: + id: &collectionSnapshot collectionSnapshot + database: *database0 + collectionName: &collectionNameSnapshot coll-snapshot + collectionOptions: + readConcern: { level: snapshot } + - collection: + id: &collectionlinearizable collectionlinearizable + database: *database0 + collectionName: &collectionNamelinearizable coll-linearizable + collectionOptions: + readConcern: { level: linearizable } + +initialData: + - collectionName: *collectionNameDefault + databaseName: *databaseName + documents: + - { _id: 1, x: default } + - collectionName: *collectionNameSnapshot + databaseName: *databaseName + documents: + - { _id: 1, x: snapshot } + - collectionName: *collectionNamelinearizable + databaseName: *databaseName + documents: + - { _id: 1, x: linearizable } + +tests: + - description: "readConcern is not sent on retried read in implicit session when readConcern level is not specified" + operations: + - &failPointCommand + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [find] + errorCode: 11600 #InterruptedAtShutdown + - name: find + object: *collectionDefault + arguments: + filter: {} + expectResult: [{ _id: 1, x: default }] + expectEvents: + - client: *client0 + events: + - commandStartedEvent: &commandStartedEventDefault + command: + find: *collectionNameDefault + filter: {} + readConcern: { $$exists: false } + databaseName: *databaseName + - commandStartedEvent: *commandStartedEventDefault + + - description: "afterClusterTime is not sent on retried read in implicit session when readConcern level is snapshot" + runOnRequirements: + - minServerVersion: "5.0" + operations: + - *failPointCommand + - name: find + object: *collectionSnapshot + arguments: + filter: {} + expectResult: [{ _id: 1, x: snapshot }] + expectEvents: + - client: *client0 + events: + - commandStartedEvent: &commandStartedEventSnapshot + command: + find: *collectionNameSnapshot + filter: {} + readConcern: + { level: snapshot, afterClusterTime: { $$exists: false } } + databaseName: *databaseName + - commandStartedEvent: *commandStartedEventSnapshot + + - description: "afterClusterTime is not sent on retried read in implicit session when readConcern level is linearizable" + operations: + - *failPointCommand + - name: find + object: *collectionlinearizable + arguments: + filter: {} + expectResult: [{ _id: 1, x: linearizable }] + expectEvents: + - client: *client0 + events: + - commandStartedEvent: &commandStartedEventLinearizable + command: + find: *collectionNamelinearizable + filter: {} + readConcern: + { level: linearizable, afterClusterTime: { $$exists: false } } + databaseName: *databaseName + - commandStartedEvent: *commandStartedEventLinearizable diff --git a/tests/MongoDB.Driver.Tests/Specifications/sessions/tests/unified/snapshot-sessions-not-supported-client-error.json b/specifications/sessions/tests/unified/snapshot-sessions-not-supported-client-error.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/sessions/tests/unified/snapshot-sessions-not-supported-client-error.json rename to specifications/sessions/tests/unified/snapshot-sessions-not-supported-client-error.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/sessions/tests/unified/snapshot-sessions-not-supported-client-error.yml b/specifications/sessions/tests/unified/snapshot-sessions-not-supported-client-error.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/sessions/tests/unified/snapshot-sessions-not-supported-client-error.yml rename to specifications/sessions/tests/unified/snapshot-sessions-not-supported-client-error.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/sessions/tests/unified/snapshot-sessions-not-supported-server-error.json b/specifications/sessions/tests/unified/snapshot-sessions-not-supported-server-error.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/sessions/tests/unified/snapshot-sessions-not-supported-server-error.json rename to specifications/sessions/tests/unified/snapshot-sessions-not-supported-server-error.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/sessions/tests/unified/snapshot-sessions-not-supported-server-error.yml b/specifications/sessions/tests/unified/snapshot-sessions-not-supported-server-error.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/sessions/tests/unified/snapshot-sessions-not-supported-server-error.yml rename to specifications/sessions/tests/unified/snapshot-sessions-not-supported-server-error.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/sessions/tests/unified/snapshot-sessions-unsupported-ops.json b/specifications/sessions/tests/unified/snapshot-sessions-unsupported-ops.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/sessions/tests/unified/snapshot-sessions-unsupported-ops.json rename to specifications/sessions/tests/unified/snapshot-sessions-unsupported-ops.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/sessions/tests/unified/snapshot-sessions-unsupported-ops.yml b/specifications/sessions/tests/unified/snapshot-sessions-unsupported-ops.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/sessions/tests/unified/snapshot-sessions-unsupported-ops.yml rename to specifications/sessions/tests/unified/snapshot-sessions-unsupported-ops.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/sessions/tests/unified/snapshot-sessions.json b/specifications/sessions/tests/unified/snapshot-sessions.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/sessions/tests/unified/snapshot-sessions.json rename to specifications/sessions/tests/unified/snapshot-sessions.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/sessions/tests/unified/snapshot-sessions.yml b/specifications/sessions/tests/unified/snapshot-sessions.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/sessions/tests/unified/snapshot-sessions.yml rename to specifications/sessions/tests/unified/snapshot-sessions.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/README.rst b/specifications/transactions-convenient-api/tests/README.rst similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/README.rst rename to specifications/transactions-convenient-api/tests/README.rst diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/callback-aborts.json b/specifications/transactions-convenient-api/tests/callback-aborts.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/callback-aborts.json rename to specifications/transactions-convenient-api/tests/callback-aborts.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/callback-aborts.yml b/specifications/transactions-convenient-api/tests/callback-aborts.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/callback-aborts.yml rename to specifications/transactions-convenient-api/tests/callback-aborts.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/callback-commits.json b/specifications/transactions-convenient-api/tests/callback-commits.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/callback-commits.json rename to specifications/transactions-convenient-api/tests/callback-commits.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/callback-commits.yml b/specifications/transactions-convenient-api/tests/callback-commits.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/callback-commits.yml rename to specifications/transactions-convenient-api/tests/callback-commits.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/callback-retry.json b/specifications/transactions-convenient-api/tests/callback-retry.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/callback-retry.json rename to specifications/transactions-convenient-api/tests/callback-retry.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/callback-retry.yml b/specifications/transactions-convenient-api/tests/callback-retry.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/callback-retry.yml rename to specifications/transactions-convenient-api/tests/callback-retry.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/commit-retry.json b/specifications/transactions-convenient-api/tests/commit-retry.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/commit-retry.json rename to specifications/transactions-convenient-api/tests/commit-retry.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/commit-retry.yml b/specifications/transactions-convenient-api/tests/commit-retry.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/commit-retry.yml rename to specifications/transactions-convenient-api/tests/commit-retry.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/commit-transienttransactionerror-4.2.json b/specifications/transactions-convenient-api/tests/commit-transienttransactionerror-4.2.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/commit-transienttransactionerror-4.2.json rename to specifications/transactions-convenient-api/tests/commit-transienttransactionerror-4.2.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/commit-transienttransactionerror-4.2.yml b/specifications/transactions-convenient-api/tests/commit-transienttransactionerror-4.2.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/commit-transienttransactionerror-4.2.yml rename to specifications/transactions-convenient-api/tests/commit-transienttransactionerror-4.2.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/commit-transienttransactionerror.json b/specifications/transactions-convenient-api/tests/commit-transienttransactionerror.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/commit-transienttransactionerror.json rename to specifications/transactions-convenient-api/tests/commit-transienttransactionerror.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/commit-transienttransactionerror.yml b/specifications/transactions-convenient-api/tests/commit-transienttransactionerror.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/commit-transienttransactionerror.yml rename to specifications/transactions-convenient-api/tests/commit-transienttransactionerror.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/commit-writeconcernerror.json b/specifications/transactions-convenient-api/tests/commit-writeconcernerror.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/commit-writeconcernerror.json rename to specifications/transactions-convenient-api/tests/commit-writeconcernerror.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/commit-writeconcernerror.yml b/specifications/transactions-convenient-api/tests/commit-writeconcernerror.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/commit-writeconcernerror.yml rename to specifications/transactions-convenient-api/tests/commit-writeconcernerror.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/commit.json b/specifications/transactions-convenient-api/tests/commit.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/commit.json rename to specifications/transactions-convenient-api/tests/commit.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/commit.yml b/specifications/transactions-convenient-api/tests/commit.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/commit.yml rename to specifications/transactions-convenient-api/tests/commit.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/transaction-options.json b/specifications/transactions-convenient-api/tests/transaction-options.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/transaction-options.json rename to specifications/transactions-convenient-api/tests/transaction-options.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/transaction-options.yml b/specifications/transactions-convenient-api/tests/transaction-options.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/tests/transaction-options.yml rename to specifications/transactions-convenient-api/tests/transaction-options.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/README.rst b/specifications/transactions/tests/README.rst similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/README.rst rename to specifications/transactions/tests/README.rst diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/abort.json b/specifications/transactions/tests/legacy/abort.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/abort.json rename to specifications/transactions/tests/legacy/abort.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/abort.yml b/specifications/transactions/tests/legacy/abort.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/abort.yml rename to specifications/transactions/tests/legacy/abort.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/bulk.json b/specifications/transactions/tests/legacy/bulk.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/bulk.json rename to specifications/transactions/tests/legacy/bulk.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/bulk.yml b/specifications/transactions/tests/legacy/bulk.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/bulk.yml rename to specifications/transactions/tests/legacy/bulk.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/causal-consistency.json b/specifications/transactions/tests/legacy/causal-consistency.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/causal-consistency.json rename to specifications/transactions/tests/legacy/causal-consistency.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/causal-consistency.yml b/specifications/transactions/tests/legacy/causal-consistency.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/causal-consistency.yml rename to specifications/transactions/tests/legacy/causal-consistency.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/commit.json b/specifications/transactions/tests/legacy/commit.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/commit.json rename to specifications/transactions/tests/legacy/commit.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/commit.yml b/specifications/transactions/tests/legacy/commit.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/commit.yml rename to specifications/transactions/tests/legacy/commit.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/count.json b/specifications/transactions/tests/legacy/count.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/count.json rename to specifications/transactions/tests/legacy/count.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/count.yml b/specifications/transactions/tests/legacy/count.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/count.yml rename to specifications/transactions/tests/legacy/count.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/create-collection.json b/specifications/transactions/tests/legacy/create-collection.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/create-collection.json rename to specifications/transactions/tests/legacy/create-collection.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/create-collection.yml b/specifications/transactions/tests/legacy/create-collection.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/create-collection.yml rename to specifications/transactions/tests/legacy/create-collection.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/create-index.json b/specifications/transactions/tests/legacy/create-index.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/create-index.json rename to specifications/transactions/tests/legacy/create-index.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/create-index.yml b/specifications/transactions/tests/legacy/create-index.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/create-index.yml rename to specifications/transactions/tests/legacy/create-index.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/delete.json b/specifications/transactions/tests/legacy/delete.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/delete.json rename to specifications/transactions/tests/legacy/delete.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/delete.yml b/specifications/transactions/tests/legacy/delete.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/delete.yml rename to specifications/transactions/tests/legacy/delete.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/error-labels.json b/specifications/transactions/tests/legacy/error-labels.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/error-labels.json rename to specifications/transactions/tests/legacy/error-labels.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/error-labels.yml b/specifications/transactions/tests/legacy/error-labels.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/error-labels.yml rename to specifications/transactions/tests/legacy/error-labels.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/errors-client.json b/specifications/transactions/tests/legacy/errors-client.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/errors-client.json rename to specifications/transactions/tests/legacy/errors-client.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/errors-client.yml b/specifications/transactions/tests/legacy/errors-client.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/errors-client.yml rename to specifications/transactions/tests/legacy/errors-client.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/errors.json b/specifications/transactions/tests/legacy/errors.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/errors.json rename to specifications/transactions/tests/legacy/errors.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/errors.yml b/specifications/transactions/tests/legacy/errors.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/errors.yml rename to specifications/transactions/tests/legacy/errors.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/findOneAndDelete.json b/specifications/transactions/tests/legacy/findOneAndDelete.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/findOneAndDelete.json rename to specifications/transactions/tests/legacy/findOneAndDelete.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/findOneAndDelete.yml b/specifications/transactions/tests/legacy/findOneAndDelete.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/findOneAndDelete.yml rename to specifications/transactions/tests/legacy/findOneAndDelete.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/findOneAndReplace.json b/specifications/transactions/tests/legacy/findOneAndReplace.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/findOneAndReplace.json rename to specifications/transactions/tests/legacy/findOneAndReplace.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/findOneAndReplace.yml b/specifications/transactions/tests/legacy/findOneAndReplace.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/findOneAndReplace.yml rename to specifications/transactions/tests/legacy/findOneAndReplace.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/findOneAndUpdate.json b/specifications/transactions/tests/legacy/findOneAndUpdate.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/findOneAndUpdate.json rename to specifications/transactions/tests/legacy/findOneAndUpdate.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/findOneAndUpdate.yml b/specifications/transactions/tests/legacy/findOneAndUpdate.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/findOneAndUpdate.yml rename to specifications/transactions/tests/legacy/findOneAndUpdate.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/insert.json b/specifications/transactions/tests/legacy/insert.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/insert.json rename to specifications/transactions/tests/legacy/insert.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/insert.yml b/specifications/transactions/tests/legacy/insert.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/insert.yml rename to specifications/transactions/tests/legacy/insert.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/isolation.json b/specifications/transactions/tests/legacy/isolation.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/isolation.json rename to specifications/transactions/tests/legacy/isolation.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/isolation.yml b/specifications/transactions/tests/legacy/isolation.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/isolation.yml rename to specifications/transactions/tests/legacy/isolation.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/mongos-pin-auto-tests.py b/specifications/transactions/tests/legacy/mongos-pin-auto-tests.py similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/mongos-pin-auto-tests.py rename to specifications/transactions/tests/legacy/mongos-pin-auto-tests.py diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/mongos-pin-auto.json b/specifications/transactions/tests/legacy/mongos-pin-auto.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/mongos-pin-auto.json rename to specifications/transactions/tests/legacy/mongos-pin-auto.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/mongos-pin-auto.yml b/specifications/transactions/tests/legacy/mongos-pin-auto.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/mongos-pin-auto.yml rename to specifications/transactions/tests/legacy/mongos-pin-auto.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/mongos-recovery-token.json b/specifications/transactions/tests/legacy/mongos-recovery-token.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/mongos-recovery-token.json rename to specifications/transactions/tests/legacy/mongos-recovery-token.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/mongos-recovery-token.yml b/specifications/transactions/tests/legacy/mongos-recovery-token.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/mongos-recovery-token.yml rename to specifications/transactions/tests/legacy/mongos-recovery-token.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/pin-mongos.json b/specifications/transactions/tests/legacy/pin-mongos.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/pin-mongos.json rename to specifications/transactions/tests/legacy/pin-mongos.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/pin-mongos.yml b/specifications/transactions/tests/legacy/pin-mongos.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/pin-mongos.yml rename to specifications/transactions/tests/legacy/pin-mongos.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/read-concern.json b/specifications/transactions/tests/legacy/read-concern.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/read-concern.json rename to specifications/transactions/tests/legacy/read-concern.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/read-concern.yml b/specifications/transactions/tests/legacy/read-concern.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/read-concern.yml rename to specifications/transactions/tests/legacy/read-concern.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/read-pref.json b/specifications/transactions/tests/legacy/read-pref.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/read-pref.json rename to specifications/transactions/tests/legacy/read-pref.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/read-pref.yml b/specifications/transactions/tests/legacy/read-pref.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/read-pref.yml rename to specifications/transactions/tests/legacy/read-pref.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/reads.json b/specifications/transactions/tests/legacy/reads.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/reads.json rename to specifications/transactions/tests/legacy/reads.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/reads.yml b/specifications/transactions/tests/legacy/reads.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/reads.yml rename to specifications/transactions/tests/legacy/reads.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/retryable-abort-errorLabels.json b/specifications/transactions/tests/legacy/retryable-abort-errorLabels.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/retryable-abort-errorLabels.json rename to specifications/transactions/tests/legacy/retryable-abort-errorLabels.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/retryable-abort-errorLabels.yml b/specifications/transactions/tests/legacy/retryable-abort-errorLabels.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/retryable-abort-errorLabels.yml rename to specifications/transactions/tests/legacy/retryable-abort-errorLabels.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/retryable-abort.json b/specifications/transactions/tests/legacy/retryable-abort.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/retryable-abort.json rename to specifications/transactions/tests/legacy/retryable-abort.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/retryable-abort.yml b/specifications/transactions/tests/legacy/retryable-abort.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/retryable-abort.yml rename to specifications/transactions/tests/legacy/retryable-abort.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/retryable-commit-errorLabels.json b/specifications/transactions/tests/legacy/retryable-commit-errorLabels.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/retryable-commit-errorLabels.json rename to specifications/transactions/tests/legacy/retryable-commit-errorLabels.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/retryable-commit-errorLabels.yml b/specifications/transactions/tests/legacy/retryable-commit-errorLabels.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/retryable-commit-errorLabels.yml rename to specifications/transactions/tests/legacy/retryable-commit-errorLabels.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/retryable-commit.json b/specifications/transactions/tests/legacy/retryable-commit.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/retryable-commit.json rename to specifications/transactions/tests/legacy/retryable-commit.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/retryable-commit.yml b/specifications/transactions/tests/legacy/retryable-commit.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/retryable-commit.yml rename to specifications/transactions/tests/legacy/retryable-commit.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/retryable-writes.json b/specifications/transactions/tests/legacy/retryable-writes.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/retryable-writes.json rename to specifications/transactions/tests/legacy/retryable-writes.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/retryable-writes.yml b/specifications/transactions/tests/legacy/retryable-writes.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/retryable-writes.yml rename to specifications/transactions/tests/legacy/retryable-writes.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/run-command.json b/specifications/transactions/tests/legacy/run-command.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/run-command.json rename to specifications/transactions/tests/legacy/run-command.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/run-command.yml b/specifications/transactions/tests/legacy/run-command.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/run-command.yml rename to specifications/transactions/tests/legacy/run-command.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/transaction-options-repl.json b/specifications/transactions/tests/legacy/transaction-options-repl.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/transaction-options-repl.json rename to specifications/transactions/tests/legacy/transaction-options-repl.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/transaction-options-repl.yml b/specifications/transactions/tests/legacy/transaction-options-repl.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/transaction-options-repl.yml rename to specifications/transactions/tests/legacy/transaction-options-repl.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/transaction-options.json b/specifications/transactions/tests/legacy/transaction-options.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/transaction-options.json rename to specifications/transactions/tests/legacy/transaction-options.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/transaction-options.yml b/specifications/transactions/tests/legacy/transaction-options.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/transaction-options.yml rename to specifications/transactions/tests/legacy/transaction-options.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/update.json b/specifications/transactions/tests/legacy/update.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/update.json rename to specifications/transactions/tests/legacy/update.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/update.yml b/specifications/transactions/tests/legacy/update.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/update.yml rename to specifications/transactions/tests/legacy/update.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/write-concern.json b/specifications/transactions/tests/legacy/write-concern.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/write-concern.json rename to specifications/transactions/tests/legacy/write-concern.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/write-concern.yml b/specifications/transactions/tests/legacy/write-concern.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/legacy/write-concern.yml rename to specifications/transactions/tests/legacy/write-concern.yml diff --git a/specifications/transactions/tests/unified/do-not-retry-read-in-transaction.json b/specifications/transactions/tests/unified/do-not-retry-read-in-transaction.json new file mode 100644 index 00000000000..6d9dc704b81 --- /dev/null +++ b/specifications/transactions/tests/unified/do-not-retry-read-in-transaction.json @@ -0,0 +1,115 @@ +{ + "description": "do not retry read in a transaction", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.0.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.2.0", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "uriOptions": { + "retryReads": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-read-in-transaction-test" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "tests": [ + { + "description": "find does not retry in a transaction", + "operations": [ + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "closeConnection": true + } + } + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": {}, + "session": "session0" + }, + "expectError": { + "isError": true, + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {}, + "startTransaction": true + }, + "commandName": "find", + "databaseName": "retryable-read-in-transaction-test" + } + } + ] + } + ] + } + ] +} diff --git a/specifications/transactions/tests/unified/do-not-retry-read-in-transaction.yml b/specifications/transactions/tests/unified/do-not-retry-read-in-transaction.yml new file mode 100644 index 00000000000..e30df0b2904 --- /dev/null +++ b/specifications/transactions/tests/unified/do-not-retry-read-in-transaction.yml @@ -0,0 +1,64 @@ +description: "do not retry read in a transaction" + +schemaVersion: "1.4" + +runOnRequirements: + - minServerVersion: "4.0.0" + topologies: [ replicaset ] + - minServerVersion: "4.2.0" + topologies: [ sharded, load-balanced ] + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: [commandStartedEvent] + uriOptions: { retryReads: true } + - database: + id: &database0 database0 + client: *client0 + databaseName: &databaseName retryable-read-in-transaction-test + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collectionName coll + - session: + id: &session0 session0 + client: *client0 + +tests: + - description: "find does not retry in a transaction" + operations: + + - name: startTransaction + object: *session0 + + - name: failPoint # fail the following find command + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [find] + closeConnection: true + + - name: find + object: *collection0 + arguments: + filter: {} + session: *session0 + expectError: + isError: true + errorLabelsContain: ["TransientTransactionError"] + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + find: *collectionName + filter: {} + startTransaction: true + commandName: find + databaseName: *databaseName diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/unified/mongos-unpin.json b/specifications/transactions/tests/unified/mongos-unpin.json similarity index 87% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/unified/mongos-unpin.json rename to specifications/transactions/tests/unified/mongos-unpin.json index 3268394c1b2..4f7ae43794a 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/unified/mongos-unpin.json +++ b/specifications/transactions/tests/unified/mongos-unpin.json @@ -108,6 +108,24 @@ "arguments": { "session": "session0" } + }, + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + } + }, + { + "name": "abortTransaction", + "object": "session0" } ] }, @@ -142,7 +160,7 @@ ] }, { - "description": "unpin after TransientTransactionError error on abort", + "description": "unpin after non-transient error on abort", "runOnRequirements": [ { "serverless": "forbid" @@ -192,11 +210,29 @@ "arguments": { "session": "session0" } + }, + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + } + }, + { + "name": "abortTransaction", + "object": "session0" } ] }, { - "description": "unpin after non-transient error on abort", + "description": "unpin after TransientTransactionError error on abort", "operations": [ { "name": "startTransaction", @@ -241,6 +277,24 @@ "arguments": { "session": "session0" } + }, + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + } + }, + { + "name": "abortTransaction", + "object": "session0" } ] }, diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/unified/mongos-unpin.yml b/specifications/transactions/tests/unified/mongos-unpin.yml similarity index 98% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/unified/mongos-unpin.yml rename to specifications/transactions/tests/unified/mongos-unpin.yml index 99b7fb51fbd..c13798acaa5 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/unified/mongos-unpin.yml +++ b/specifications/transactions/tests/unified/mongos-unpin.yml @@ -85,7 +85,7 @@ tests: - *abortTransaction - *assertNoPinnedServer - - description: unpin after TransientTransactionError error on abort + - description: unpin after non-transient error on abort runOnRequirements: # serverless proxy doesn't append error labels to errors in transactions # caused by failpoints (CLOUDP-88216) diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/unified/retryable-abort-handshake.json b/specifications/transactions/tests/unified/retryable-abort-handshake.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/unified/retryable-abort-handshake.json rename to specifications/transactions/tests/unified/retryable-abort-handshake.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/unified/retryable-abort-handshake.yml b/specifications/transactions/tests/unified/retryable-abort-handshake.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/unified/retryable-abort-handshake.yml rename to specifications/transactions/tests/unified/retryable-abort-handshake.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/unified/retryable-commit-handshake.json b/specifications/transactions/tests/unified/retryable-commit-handshake.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/unified/retryable-commit-handshake.json rename to specifications/transactions/tests/unified/retryable-commit-handshake.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/tests/unified/retryable-commit-handshake.yml b/specifications/transactions/tests/unified/retryable-commit-handshake.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/transactions/tests/unified/retryable-commit-handshake.yml rename to specifications/transactions/tests/unified/retryable-commit-handshake.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/README.rst b/specifications/unified-test-format/tests/README.rst similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/README.rst rename to specifications/unified-test-format/tests/README.rst diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/assertNumberConnectionsCheckedOut.json b/specifications/unified-test-format/tests/valid-fail/assertNumberConnectionsCheckedOut.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/assertNumberConnectionsCheckedOut.json rename to specifications/unified-test-format/tests/valid-fail/assertNumberConnectionsCheckedOut.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/assertNumberConnectionsCheckedOut.yml b/specifications/unified-test-format/tests/valid-fail/assertNumberConnectionsCheckedOut.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/assertNumberConnectionsCheckedOut.yml rename to specifications/unified-test-format/tests/valid-fail/assertNumberConnectionsCheckedOut.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/entity-bucket-database-undefined.json b/specifications/unified-test-format/tests/valid-fail/entity-bucket-database-undefined.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/entity-bucket-database-undefined.json rename to specifications/unified-test-format/tests/valid-fail/entity-bucket-database-undefined.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/entity-bucket-database-undefined.yml b/specifications/unified-test-format/tests/valid-fail/entity-bucket-database-undefined.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/entity-bucket-database-undefined.yml rename to specifications/unified-test-format/tests/valid-fail/entity-bucket-database-undefined.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/entity-client-apiVersion-unsupported.json b/specifications/unified-test-format/tests/valid-fail/entity-client-apiVersion-unsupported.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/entity-client-apiVersion-unsupported.json rename to specifications/unified-test-format/tests/valid-fail/entity-client-apiVersion-unsupported.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/entity-client-apiVersion-unsupported.yml b/specifications/unified-test-format/tests/valid-fail/entity-client-apiVersion-unsupported.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/entity-client-apiVersion-unsupported.yml rename to specifications/unified-test-format/tests/valid-fail/entity-client-apiVersion-unsupported.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/entity-collection-database-undefined.json b/specifications/unified-test-format/tests/valid-fail/entity-collection-database-undefined.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/entity-collection-database-undefined.json rename to specifications/unified-test-format/tests/valid-fail/entity-collection-database-undefined.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/entity-collection-database-undefined.yml b/specifications/unified-test-format/tests/valid-fail/entity-collection-database-undefined.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/entity-collection-database-undefined.yml rename to specifications/unified-test-format/tests/valid-fail/entity-collection-database-undefined.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/entity-database-client-undefined.json b/specifications/unified-test-format/tests/valid-fail/entity-database-client-undefined.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/entity-database-client-undefined.json rename to specifications/unified-test-format/tests/valid-fail/entity-database-client-undefined.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/entity-database-client-undefined.yml b/specifications/unified-test-format/tests/valid-fail/entity-database-client-undefined.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/entity-database-client-undefined.yml rename to specifications/unified-test-format/tests/valid-fail/entity-database-client-undefined.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/entity-findCursor-malformed.json b/specifications/unified-test-format/tests/valid-fail/entity-findCursor-malformed.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/entity-findCursor-malformed.json rename to specifications/unified-test-format/tests/valid-fail/entity-findCursor-malformed.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/entity-findCursor-malformed.yml b/specifications/unified-test-format/tests/valid-fail/entity-findCursor-malformed.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/entity-findCursor-malformed.yml rename to specifications/unified-test-format/tests/valid-fail/entity-findCursor-malformed.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/entity-findCursor.json b/specifications/unified-test-format/tests/valid-fail/entity-findCursor.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/entity-findCursor.json rename to specifications/unified-test-format/tests/valid-fail/entity-findCursor.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/entity-findCursor.yml b/specifications/unified-test-format/tests/valid-fail/entity-findCursor.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/entity-findCursor.yml rename to specifications/unified-test-format/tests/valid-fail/entity-findCursor.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/entity-session-client-undefined.json b/specifications/unified-test-format/tests/valid-fail/entity-session-client-undefined.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/entity-session-client-undefined.json rename to specifications/unified-test-format/tests/valid-fail/entity-session-client-undefined.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/entity-session-client-undefined.yml b/specifications/unified-test-format/tests/valid-fail/entity-session-client-undefined.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/entity-session-client-undefined.yml rename to specifications/unified-test-format/tests/valid-fail/entity-session-client-undefined.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/ignoreResultAndError-malformed.json b/specifications/unified-test-format/tests/valid-fail/ignoreResultAndError-malformed.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/ignoreResultAndError-malformed.json rename to specifications/unified-test-format/tests/valid-fail/ignoreResultAndError-malformed.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/ignoreResultAndError-malformed.yml b/specifications/unified-test-format/tests/valid-fail/ignoreResultAndError-malformed.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/ignoreResultAndError-malformed.yml rename to specifications/unified-test-format/tests/valid-fail/ignoreResultAndError-malformed.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/ignoreResultAndError.json b/specifications/unified-test-format/tests/valid-fail/ignoreResultAndError.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/ignoreResultAndError.json rename to specifications/unified-test-format/tests/valid-fail/ignoreResultAndError.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/ignoreResultAndError.yml b/specifications/unified-test-format/tests/valid-fail/ignoreResultAndError.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/ignoreResultAndError.yml rename to specifications/unified-test-format/tests/valid-fail/ignoreResultAndError.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/kmsProviders-missing_aws_kms_credentials.json b/specifications/unified-test-format/tests/valid-fail/kmsProviders-missing_aws_kms_credentials.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/kmsProviders-missing_aws_kms_credentials.json rename to specifications/unified-test-format/tests/valid-fail/kmsProviders-missing_aws_kms_credentials.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/kmsProviders-missing_aws_kms_credentials.yml b/specifications/unified-test-format/tests/valid-fail/kmsProviders-missing_aws_kms_credentials.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/kmsProviders-missing_aws_kms_credentials.yml rename to specifications/unified-test-format/tests/valid-fail/kmsProviders-missing_aws_kms_credentials.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/kmsProviders-missing_azure_kms_credentials.json b/specifications/unified-test-format/tests/valid-fail/kmsProviders-missing_azure_kms_credentials.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/kmsProviders-missing_azure_kms_credentials.json rename to specifications/unified-test-format/tests/valid-fail/kmsProviders-missing_azure_kms_credentials.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/kmsProviders-missing_azure_kms_credentials.yml b/specifications/unified-test-format/tests/valid-fail/kmsProviders-missing_azure_kms_credentials.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/kmsProviders-missing_azure_kms_credentials.yml rename to specifications/unified-test-format/tests/valid-fail/kmsProviders-missing_azure_kms_credentials.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/kmsProviders-missing_gcp_kms_credentials.json b/specifications/unified-test-format/tests/valid-fail/kmsProviders-missing_gcp_kms_credentials.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/kmsProviders-missing_gcp_kms_credentials.json rename to specifications/unified-test-format/tests/valid-fail/kmsProviders-missing_gcp_kms_credentials.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/kmsProviders-missing_gcp_kms_credentials.yml b/specifications/unified-test-format/tests/valid-fail/kmsProviders-missing_gcp_kms_credentials.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/kmsProviders-missing_gcp_kms_credentials.yml rename to specifications/unified-test-format/tests/valid-fail/kmsProviders-missing_gcp_kms_credentials.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/kmsProviders-no_kms.json b/specifications/unified-test-format/tests/valid-fail/kmsProviders-no_kms.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/kmsProviders-no_kms.json rename to specifications/unified-test-format/tests/valid-fail/kmsProviders-no_kms.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/kmsProviders-no_kms.yml b/specifications/unified-test-format/tests/valid-fail/kmsProviders-no_kms.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/kmsProviders-no_kms.yml rename to specifications/unified-test-format/tests/valid-fail/kmsProviders-no_kms.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/operation-failure.json b/specifications/unified-test-format/tests/valid-fail/operation-failure.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/operation-failure.json rename to specifications/unified-test-format/tests/valid-fail/operation-failure.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/operation-failure.yml b/specifications/unified-test-format/tests/valid-fail/operation-failure.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/operation-failure.yml rename to specifications/unified-test-format/tests/valid-fail/operation-failure.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/operation-unsupported.json b/specifications/unified-test-format/tests/valid-fail/operation-unsupported.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/operation-unsupported.json rename to specifications/unified-test-format/tests/valid-fail/operation-unsupported.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/operation-unsupported.yml b/specifications/unified-test-format/tests/valid-fail/operation-unsupported.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/operation-unsupported.yml rename to specifications/unified-test-format/tests/valid-fail/operation-unsupported.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/returnDocument-enum-invalid.json b/specifications/unified-test-format/tests/valid-fail/returnDocument-enum-invalid.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/returnDocument-enum-invalid.json rename to specifications/unified-test-format/tests/valid-fail/returnDocument-enum-invalid.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/returnDocument-enum-invalid.yml b/specifications/unified-test-format/tests/valid-fail/returnDocument-enum-invalid.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/returnDocument-enum-invalid.yml rename to specifications/unified-test-format/tests/valid-fail/returnDocument-enum-invalid.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/schemaVersion-unsupported.json b/specifications/unified-test-format/tests/valid-fail/schemaVersion-unsupported.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/schemaVersion-unsupported.json rename to specifications/unified-test-format/tests/valid-fail/schemaVersion-unsupported.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/schemaVersion-unsupported.yml b/specifications/unified-test-format/tests/valid-fail/schemaVersion-unsupported.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-fail/schemaVersion-unsupported.yml rename to specifications/unified-test-format/tests/valid-fail/schemaVersion-unsupported.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/assertNumberConnectionsCheckedOut.json b/specifications/unified-test-format/tests/valid-pass/assertNumberConnectionsCheckedOut.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/assertNumberConnectionsCheckedOut.json rename to specifications/unified-test-format/tests/valid-pass/assertNumberConnectionsCheckedOut.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/assertNumberConnectionsCheckedOut.yml b/specifications/unified-test-format/tests/valid-pass/assertNumberConnectionsCheckedOut.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/assertNumberConnectionsCheckedOut.yml rename to specifications/unified-test-format/tests/valid-pass/assertNumberConnectionsCheckedOut.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/entity-client-cmap-events.json b/specifications/unified-test-format/tests/valid-pass/entity-client-cmap-events.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/entity-client-cmap-events.json rename to specifications/unified-test-format/tests/valid-pass/entity-client-cmap-events.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/entity-client-cmap-events.yml b/specifications/unified-test-format/tests/valid-pass/entity-client-cmap-events.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/entity-client-cmap-events.yml rename to specifications/unified-test-format/tests/valid-pass/entity-client-cmap-events.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/entity-find-cursor.json b/specifications/unified-test-format/tests/valid-pass/entity-find-cursor.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/entity-find-cursor.json rename to specifications/unified-test-format/tests/valid-pass/entity-find-cursor.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/entity-find-cursor.yml b/specifications/unified-test-format/tests/valid-pass/entity-find-cursor.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/entity-find-cursor.yml rename to specifications/unified-test-format/tests/valid-pass/entity-find-cursor.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/expectedEventsForClient-eventType.json b/specifications/unified-test-format/tests/valid-pass/expectedEventsForClient-eventType.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/expectedEventsForClient-eventType.json rename to specifications/unified-test-format/tests/valid-pass/expectedEventsForClient-eventType.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/expectedEventsForClient-eventType.yml b/specifications/unified-test-format/tests/valid-pass/expectedEventsForClient-eventType.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/expectedEventsForClient-eventType.yml rename to specifications/unified-test-format/tests/valid-pass/expectedEventsForClient-eventType.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/expectedEventsForClient-ignoreExtraEvents.json b/specifications/unified-test-format/tests/valid-pass/expectedEventsForClient-ignoreExtraEvents.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/expectedEventsForClient-ignoreExtraEvents.json rename to specifications/unified-test-format/tests/valid-pass/expectedEventsForClient-ignoreExtraEvents.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/expectedEventsForClient-ignoreExtraEvents.yml b/specifications/unified-test-format/tests/valid-pass/expectedEventsForClient-ignoreExtraEvents.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/expectedEventsForClient-ignoreExtraEvents.yml rename to specifications/unified-test-format/tests/valid-pass/expectedEventsForClient-ignoreExtraEvents.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/ignoreResultAndError.json b/specifications/unified-test-format/tests/valid-pass/ignoreResultAndError.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/ignoreResultAndError.json rename to specifications/unified-test-format/tests/valid-pass/ignoreResultAndError.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/ignoreResultAndError.yml b/specifications/unified-test-format/tests/valid-pass/ignoreResultAndError.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/ignoreResultAndError.yml rename to specifications/unified-test-format/tests/valid-pass/ignoreResultAndError.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/kmsProviders-explicit_kms_credentials.json b/specifications/unified-test-format/tests/valid-pass/kmsProviders-explicit_kms_credentials.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/kmsProviders-explicit_kms_credentials.json rename to specifications/unified-test-format/tests/valid-pass/kmsProviders-explicit_kms_credentials.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/kmsProviders-explicit_kms_credentials.yml b/specifications/unified-test-format/tests/valid-pass/kmsProviders-explicit_kms_credentials.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/kmsProviders-explicit_kms_credentials.yml rename to specifications/unified-test-format/tests/valid-pass/kmsProviders-explicit_kms_credentials.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/kmsProviders-mixed_kms_credential_fields.json b/specifications/unified-test-format/tests/valid-pass/kmsProviders-mixed_kms_credential_fields.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/kmsProviders-mixed_kms_credential_fields.json rename to specifications/unified-test-format/tests/valid-pass/kmsProviders-mixed_kms_credential_fields.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/kmsProviders-mixed_kms_credential_fields.yml b/specifications/unified-test-format/tests/valid-pass/kmsProviders-mixed_kms_credential_fields.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/kmsProviders-mixed_kms_credential_fields.yml rename to specifications/unified-test-format/tests/valid-pass/kmsProviders-mixed_kms_credential_fields.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/kmsProviders-placeholder_kms_credentials.json b/specifications/unified-test-format/tests/valid-pass/kmsProviders-placeholder_kms_credentials.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/kmsProviders-placeholder_kms_credentials.json rename to specifications/unified-test-format/tests/valid-pass/kmsProviders-placeholder_kms_credentials.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/kmsProviders-placeholder_kms_credentials.yml b/specifications/unified-test-format/tests/valid-pass/kmsProviders-placeholder_kms_credentials.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/kmsProviders-placeholder_kms_credentials.yml rename to specifications/unified-test-format/tests/valid-pass/kmsProviders-placeholder_kms_credentials.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/kmsProviders-unconfigured_kms.json b/specifications/unified-test-format/tests/valid-pass/kmsProviders-unconfigured_kms.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/kmsProviders-unconfigured_kms.json rename to specifications/unified-test-format/tests/valid-pass/kmsProviders-unconfigured_kms.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/kmsProviders-unconfigured_kms.yml b/specifications/unified-test-format/tests/valid-pass/kmsProviders-unconfigured_kms.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/kmsProviders-unconfigured_kms.yml rename to specifications/unified-test-format/tests/valid-pass/kmsProviders-unconfigured_kms.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/observeSensitiveCommands.json b/specifications/unified-test-format/tests/valid-pass/observeSensitiveCommands.json similarity index 98% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/observeSensitiveCommands.json rename to specifications/unified-test-format/tests/valid-pass/observeSensitiveCommands.json index 411ca19c5d0..d3ae5665be9 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/observeSensitiveCommands.json +++ b/specifications/unified-test-format/tests/valid-pass/observeSensitiveCommands.json @@ -61,6 +61,11 @@ "tests": [ { "description": "getnonce is observed with observeSensitiveCommands=true", + "runOnRequirements": [ + { + "maxServerVersion": "6.1.99" + } + ], "operations": [ { "name": "runCommand", @@ -106,6 +111,11 @@ }, { "description": "getnonce is not observed with observeSensitiveCommands=false", + "runOnRequirements": [ + { + "maxServerVersion": "6.1.99" + } + ], "operations": [ { "name": "runCommand", @@ -127,6 +137,11 @@ }, { "description": "getnonce is not observed by default", + "runOnRequirements": [ + { + "maxServerVersion": "6.1.99" + } + ], "operations": [ { "name": "runCommand", diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/observeSensitiveCommands.yml b/specifications/unified-test-format/tests/valid-pass/observeSensitiveCommands.yml similarity index 96% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/observeSensitiveCommands.yml rename to specifications/unified-test-format/tests/valid-pass/observeSensitiveCommands.yml index 13db4137062..cd4d27a165b 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/observeSensitiveCommands.yml +++ b/specifications/unified-test-format/tests/valid-pass/observeSensitiveCommands.yml @@ -38,6 +38,8 @@ createEntities: tests: - description: "getnonce is observed with observeSensitiveCommands=true" + runOnRequirements: + - maxServerVersion: 6.1.99 # getnonce removed as of 6.2 via SERVER-71007 operations: - name: runCommand object: *databaseObserveSensitiveCommands @@ -57,6 +59,8 @@ tests: nonce: { $$exists: false } - description: "getnonce is not observed with observeSensitiveCommands=false" + runOnRequirements: + - maxServerVersion: 6.1.99 # getnonce removed as of 6.2 via SERVER-71007 operations: - name: runCommand object: *databaseDoNotObserveSensitiveCommands @@ -68,6 +72,8 @@ tests: events: [] - description: "getnonce is not observed by default" + runOnRequirements: + - maxServerVersion: 6.1.99 # getnonce removed as of 6.2 via SERVER-71007 operations: - name: runCommand object: *databaseDoNotObserveSensitiveCommandsByDefault diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-change-streams.json b/specifications/unified-test-format/tests/valid-pass/poc-change-streams.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-change-streams.json rename to specifications/unified-test-format/tests/valid-pass/poc-change-streams.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-change-streams.yml b/specifications/unified-test-format/tests/valid-pass/poc-change-streams.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-change-streams.yml rename to specifications/unified-test-format/tests/valid-pass/poc-change-streams.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-command-monitoring.json b/specifications/unified-test-format/tests/valid-pass/poc-command-monitoring.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-command-monitoring.json rename to specifications/unified-test-format/tests/valid-pass/poc-command-monitoring.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-command-monitoring.yml b/specifications/unified-test-format/tests/valid-pass/poc-command-monitoring.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-command-monitoring.yml rename to specifications/unified-test-format/tests/valid-pass/poc-command-monitoring.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-crud.json b/specifications/unified-test-format/tests/valid-pass/poc-crud.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-crud.json rename to specifications/unified-test-format/tests/valid-pass/poc-crud.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-crud.yml b/specifications/unified-test-format/tests/valid-pass/poc-crud.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-crud.yml rename to specifications/unified-test-format/tests/valid-pass/poc-crud.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-gridfs.json b/specifications/unified-test-format/tests/valid-pass/poc-gridfs.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-gridfs.json rename to specifications/unified-test-format/tests/valid-pass/poc-gridfs.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-gridfs.yml b/specifications/unified-test-format/tests/valid-pass/poc-gridfs.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-gridfs.yml rename to specifications/unified-test-format/tests/valid-pass/poc-gridfs.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-retryable-reads.json b/specifications/unified-test-format/tests/valid-pass/poc-retryable-reads.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-retryable-reads.json rename to specifications/unified-test-format/tests/valid-pass/poc-retryable-reads.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-retryable-reads.yml b/specifications/unified-test-format/tests/valid-pass/poc-retryable-reads.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-retryable-reads.yml rename to specifications/unified-test-format/tests/valid-pass/poc-retryable-reads.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-retryable-writes.json b/specifications/unified-test-format/tests/valid-pass/poc-retryable-writes.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-retryable-writes.json rename to specifications/unified-test-format/tests/valid-pass/poc-retryable-writes.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-retryable-writes.yml b/specifications/unified-test-format/tests/valid-pass/poc-retryable-writes.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-retryable-writes.yml rename to specifications/unified-test-format/tests/valid-pass/poc-retryable-writes.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-sessions.json b/specifications/unified-test-format/tests/valid-pass/poc-sessions.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-sessions.json rename to specifications/unified-test-format/tests/valid-pass/poc-sessions.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-sessions.yml b/specifications/unified-test-format/tests/valid-pass/poc-sessions.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-sessions.yml rename to specifications/unified-test-format/tests/valid-pass/poc-sessions.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-transactions-convenient-api.json b/specifications/unified-test-format/tests/valid-pass/poc-transactions-convenient-api.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-transactions-convenient-api.json rename to specifications/unified-test-format/tests/valid-pass/poc-transactions-convenient-api.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-transactions-convenient-api.yml b/specifications/unified-test-format/tests/valid-pass/poc-transactions-convenient-api.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-transactions-convenient-api.yml rename to specifications/unified-test-format/tests/valid-pass/poc-transactions-convenient-api.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-transactions-mongos-pin-auto.json b/specifications/unified-test-format/tests/valid-pass/poc-transactions-mongos-pin-auto.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-transactions-mongos-pin-auto.json rename to specifications/unified-test-format/tests/valid-pass/poc-transactions-mongos-pin-auto.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-transactions-mongos-pin-auto.yml b/specifications/unified-test-format/tests/valid-pass/poc-transactions-mongos-pin-auto.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-transactions-mongos-pin-auto.yml rename to specifications/unified-test-format/tests/valid-pass/poc-transactions-mongos-pin-auto.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-transactions.json b/specifications/unified-test-format/tests/valid-pass/poc-transactions.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-transactions.json rename to specifications/unified-test-format/tests/valid-pass/poc-transactions.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-transactions.yml b/specifications/unified-test-format/tests/valid-pass/poc-transactions.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/unified-test-format/tests/valid-pass/poc-transactions.yml rename to specifications/unified-test-format/tests/valid-pass/poc-transactions.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/README.rst b/specifications/uri-options/tests/README.rst similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/README.rst rename to specifications/uri-options/tests/README.rst diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/auth-options.json b/specifications/uri-options/tests/auth-options.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/auth-options.json rename to specifications/uri-options/tests/auth-options.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/auth-options.yml b/specifications/uri-options/tests/auth-options.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/auth-options.yml rename to specifications/uri-options/tests/auth-options.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/ca.pem b/specifications/uri-options/tests/ca.pem similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/ca.pem rename to specifications/uri-options/tests/ca.pem diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/cert.pem b/specifications/uri-options/tests/cert.pem similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/cert.pem rename to specifications/uri-options/tests/cert.pem diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/client.pem b/specifications/uri-options/tests/client.pem similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/client.pem rename to specifications/uri-options/tests/client.pem diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/compression-options.json b/specifications/uri-options/tests/compression-options.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/compression-options.json rename to specifications/uri-options/tests/compression-options.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/compression-options.yml b/specifications/uri-options/tests/compression-options.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/compression-options.yml rename to specifications/uri-options/tests/compression-options.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/concern-options.json b/specifications/uri-options/tests/concern-options.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/concern-options.json rename to specifications/uri-options/tests/concern-options.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/concern-options.yml b/specifications/uri-options/tests/concern-options.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/concern-options.yml rename to specifications/uri-options/tests/concern-options.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/connection-options.json b/specifications/uri-options/tests/connection-options.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/connection-options.json rename to specifications/uri-options/tests/connection-options.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/connection-options.yml b/specifications/uri-options/tests/connection-options.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/connection-options.yml rename to specifications/uri-options/tests/connection-options.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/connection-pool-options.json b/specifications/uri-options/tests/connection-pool-options.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/connection-pool-options.json rename to specifications/uri-options/tests/connection-pool-options.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/connection-pool-options.yml b/specifications/uri-options/tests/connection-pool-options.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/connection-pool-options.yml rename to specifications/uri-options/tests/connection-pool-options.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/read-preference-options.json b/specifications/uri-options/tests/read-preference-options.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/read-preference-options.json rename to specifications/uri-options/tests/read-preference-options.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/read-preference-options.yml b/specifications/uri-options/tests/read-preference-options.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/read-preference-options.yml rename to specifications/uri-options/tests/read-preference-options.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/single-threaded-options.json b/specifications/uri-options/tests/single-threaded-options.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/single-threaded-options.json rename to specifications/uri-options/tests/single-threaded-options.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/single-threaded-options.yml b/specifications/uri-options/tests/single-threaded-options.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/single-threaded-options.yml rename to specifications/uri-options/tests/single-threaded-options.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/srv-options.json b/specifications/uri-options/tests/srv-options.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/srv-options.json rename to specifications/uri-options/tests/srv-options.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/srv-options.yml b/specifications/uri-options/tests/srv-options.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/srv-options.yml rename to specifications/uri-options/tests/srv-options.yml diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/tls-options.json b/specifications/uri-options/tests/tls-options.json similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/tls-options.json rename to specifications/uri-options/tests/tls-options.json diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/tls-options.yml b/specifications/uri-options/tests/tls-options.yml similarity index 100% rename from tests/MongoDB.Driver.Core.Tests/Specifications/uri-options/tests/tls-options.yml rename to specifications/uri-options/tests/tls-options.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/versioned-api/tests/README.rst b/specifications/versioned-api/tests/README.rst similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/versioned-api/tests/README.rst rename to specifications/versioned-api/tests/README.rst diff --git a/tests/MongoDB.Driver.Tests/Specifications/versioned-api/tests/crud-api-version-1-strict.json b/specifications/versioned-api/tests/crud-api-version-1-strict.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/versioned-api/tests/crud-api-version-1-strict.json rename to specifications/versioned-api/tests/crud-api-version-1-strict.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/versioned-api/tests/crud-api-version-1-strict.yml b/specifications/versioned-api/tests/crud-api-version-1-strict.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/versioned-api/tests/crud-api-version-1-strict.yml rename to specifications/versioned-api/tests/crud-api-version-1-strict.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/versioned-api/tests/crud-api-version-1.json b/specifications/versioned-api/tests/crud-api-version-1.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/versioned-api/tests/crud-api-version-1.json rename to specifications/versioned-api/tests/crud-api-version-1.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/versioned-api/tests/crud-api-version-1.yml b/specifications/versioned-api/tests/crud-api-version-1.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/versioned-api/tests/crud-api-version-1.yml rename to specifications/versioned-api/tests/crud-api-version-1.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/versioned-api/tests/runcommand-helper-no-api-version-declared.json b/specifications/versioned-api/tests/runcommand-helper-no-api-version-declared.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/versioned-api/tests/runcommand-helper-no-api-version-declared.json rename to specifications/versioned-api/tests/runcommand-helper-no-api-version-declared.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/versioned-api/tests/runcommand-helper-no-api-version-declared.yml b/specifications/versioned-api/tests/runcommand-helper-no-api-version-declared.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/versioned-api/tests/runcommand-helper-no-api-version-declared.yml rename to specifications/versioned-api/tests/runcommand-helper-no-api-version-declared.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/versioned-api/tests/test-commands-deprecation-errors.json b/specifications/versioned-api/tests/test-commands-deprecation-errors.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/versioned-api/tests/test-commands-deprecation-errors.json rename to specifications/versioned-api/tests/test-commands-deprecation-errors.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/versioned-api/tests/test-commands-deprecation-errors.yml b/specifications/versioned-api/tests/test-commands-deprecation-errors.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/versioned-api/tests/test-commands-deprecation-errors.yml rename to specifications/versioned-api/tests/test-commands-deprecation-errors.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/versioned-api/tests/test-commands-strict-mode.json b/specifications/versioned-api/tests/test-commands-strict-mode.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/versioned-api/tests/test-commands-strict-mode.json rename to specifications/versioned-api/tests/test-commands-strict-mode.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/versioned-api/tests/test-commands-strict-mode.yml b/specifications/versioned-api/tests/test-commands-strict-mode.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/versioned-api/tests/test-commands-strict-mode.yml rename to specifications/versioned-api/tests/test-commands-strict-mode.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/versioned-api/tests/transaction-handling.json b/specifications/versioned-api/tests/transaction-handling.json similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/versioned-api/tests/transaction-handling.json rename to specifications/versioned-api/tests/transaction-handling.json diff --git a/tests/MongoDB.Driver.Tests/Specifications/versioned-api/tests/transaction-handling.yml b/specifications/versioned-api/tests/transaction-handling.yml similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/versioned-api/tests/transaction-handling.yml rename to specifications/versioned-api/tests/transaction-handling.yml diff --git a/tests/MongoDB.Driver.Tests/Specifications/wireversion-featurelist.rst b/specifications/wireversion-featurelist.rst similarity index 100% rename from tests/MongoDB.Driver.Tests/Specifications/wireversion-featurelist.rst rename to specifications/wireversion-featurelist.rst diff --git a/src/Directory.Build.props b/src/Directory.Build.props new file mode 100644 index 00000000000..7bfd170b9d0 --- /dev/null +++ b/src/Directory.Build.props @@ -0,0 +1,54 @@ + + + true + + + + netstandard2.0;netstandard2.1 + $(TargetFrameworks);net472 + 10.0 + true + ..\..\MongoDB.ruleset + + + + MongoDB Inc. + Copyright © 2010-present MongoDB Inc. + MongoDB Inc. + packageIcon.png + true + https://www.mongodb.com/docs/drivers/csharp/ + mongodb;mongo;nosql + License.txt + en-US + true + snupkg + true + + + + 0.0.0-local + + + + TRACE + + + + true + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + diff --git a/src/MongoDB.Bson/BsonExtensionMethods.cs b/src/MongoDB.Bson/BsonExtensionMethods.cs index bda37baf3a1..32844fdaa21 100644 --- a/src/MongoDB.Bson/BsonExtensionMethods.cs +++ b/src/MongoDB.Bson/BsonExtensionMethods.cs @@ -181,7 +181,7 @@ public static BsonDocument ToBsonDocument( /// The object. /// The JsonWriter settings. /// The serializer. - /// The serializastion context configurator. + /// The serialization context configurator. /// The serialization args. /// /// A JSON string. diff --git a/src/MongoDB.Bson/IO/ThreadStaticBuffer.cs b/src/MongoDB.Bson/IO/ThreadStaticBuffer.cs index ae4c99cb6fa..dea6f7aa9ad 100644 --- a/src/MongoDB.Bson/IO/ThreadStaticBuffer.cs +++ b/src/MongoDB.Bson/IO/ThreadStaticBuffer.cs @@ -80,25 +80,31 @@ public static RentedBuffer RentBuffer(int size) throw new ArgumentOutOfRangeException(nameof(size), size, $"Valid buffer size range is [1..{MaxAllocationSize}] bytes."); } - __isBufferRented = true; - if (__threadId == default) { __threadId = Thread.CurrentThread.ManagedThreadId; } + // Allocate space + RentedBuffer rentedBuffer; if (size > MaxSize) { - return new RentedBuffer(__threadId, new byte[size]); + rentedBuffer = new RentedBuffer(__threadId, new byte[size]); } - - if (__buffer == null || __buffer.Length < size) + else { - var newSize = size <= MinSize ? MinSize : PowerOf2.RoundUpToPowerOf2(size); - __buffer = new byte[newSize]; + if (__buffer == null || __buffer.Length < size) + { + var newSize = size <= MinSize ? MinSize : PowerOf2.RoundUpToPowerOf2(size); + __buffer = new byte[newSize]; + } + + rentedBuffer = new RentedBuffer(__threadId, __buffer); } - return new RentedBuffer(__threadId, __buffer); + // Set this last (in case of an exception in this method we want this to stay as-is.) + __isBufferRented = true; + return rentedBuffer; } } } diff --git a/src/MongoDB.Bson/MongoDB.Bson.csproj b/src/MongoDB.Bson/MongoDB.Bson.csproj index 7d50a45c756..bb415b81013 100644 --- a/src/MongoDB.Bson/MongoDB.Bson.csproj +++ b/src/MongoDB.Bson/MongoDB.Bson.csproj @@ -1,68 +1,23 @@ - - true - - netstandard2.0;netstandard2.1;net472 - netstandard2.0;netstandard2.1 - 9 - true - ..\..\MongoDBLegacy.ruleset MongoDB.Bson MongoDB.Bson - MongoDB Inc. - Copyright © 2010-present MongoDB Inc. Official MongoDB supported BSON library. See https://www.mongodb.com/docs/drivers/csharp/ for more details. - MongoDB Inc. - packageIcon.png - true MongoDB's Official Bson Library. - https://www.mongodb.com/docs/drivers/csharp/ - License.txt - mongodb;mongo;nosql;bson - en-US - true - snupkg - true - - - - 0.0.0-local - - - - TRACE - - - - - - - - true + $(PackageTags);bson - - - all - runtime; build; native; contentfiles; analyzers - - - - - - diff --git a/src/MongoDB.Bson/ObjectModel/BsonBinaryData.cs b/src/MongoDB.Bson/ObjectModel/BsonBinaryData.cs index 02a1b1df92a..b76fff33667 100644 --- a/src/MongoDB.Bson/ObjectModel/BsonBinaryData.cs +++ b/src/MongoDB.Bson/ObjectModel/BsonBinaryData.cs @@ -15,6 +15,7 @@ using System; using System.Linq; +using MongoDB.Shared; namespace MongoDB.Bson { @@ -349,12 +350,12 @@ public override int GetHashCode() // see Effective Java by Joshua Bloch // note: guidRepresentation is not considered when computing the hash code int hash = 17; - hash = 37 * hash + BsonType.GetHashCode(); + hash = 37 * hash + Hasher.GetHashCode(BsonType); foreach (byte b in _bytes) { hash = 37 * hash + b; } - hash = 37 * hash + _subType.GetHashCode(); + hash = 37 * hash + Hasher.GetHashCode(_subType); return hash; } diff --git a/src/MongoDB.Bson/ObjectModel/BsonBoolean.cs b/src/MongoDB.Bson/ObjectModel/BsonBoolean.cs index c1d6282eed6..1184d23208c 100644 --- a/src/MongoDB.Bson/ObjectModel/BsonBoolean.cs +++ b/src/MongoDB.Bson/ObjectModel/BsonBoolean.cs @@ -15,6 +15,7 @@ using System; using MongoDB.Bson.IO; +using MongoDB.Shared; namespace MongoDB.Bson { @@ -191,7 +192,7 @@ public override int GetHashCode() { // see Effective Java by Joshua Bloch int hash = 17; - hash = 37 * hash + BsonType.GetHashCode(); + hash = 37 * hash + Hasher.GetHashCode(BsonType); hash = 37 * hash + _value.GetHashCode(); return hash; } diff --git a/src/MongoDB.Bson/ObjectModel/BsonDateTime.cs b/src/MongoDB.Bson/ObjectModel/BsonDateTime.cs index 85e3c34867b..1d109657ed2 100644 --- a/src/MongoDB.Bson/ObjectModel/BsonDateTime.cs +++ b/src/MongoDB.Bson/ObjectModel/BsonDateTime.cs @@ -15,6 +15,7 @@ using System; using MongoDB.Bson.IO; +using MongoDB.Shared; namespace MongoDB.Bson { @@ -209,7 +210,7 @@ public override int GetHashCode() { // see Effective Java by Joshua Bloch int hash = 17; - hash = 37 * hash + BsonType.GetHashCode(); + hash = 37 * hash + Hasher.GetHashCode(BsonType); hash = 37 * hash + _millisecondsSinceEpoch.GetHashCode(); return hash; } diff --git a/src/MongoDB.Bson/ObjectModel/BsonDecimal128.cs b/src/MongoDB.Bson/ObjectModel/BsonDecimal128.cs index c34ee5096d1..b925d7f21dc 100644 --- a/src/MongoDB.Bson/ObjectModel/BsonDecimal128.cs +++ b/src/MongoDB.Bson/ObjectModel/BsonDecimal128.cs @@ -15,6 +15,7 @@ using System; using MongoDB.Bson.IO; +using MongoDB.Shared; namespace MongoDB.Bson { @@ -175,7 +176,7 @@ public override int GetHashCode() { // see Effective Java by Joshua Bloch int hash = 17; - hash = 37 * hash + BsonType.GetHashCode(); + hash = 37 * hash + Hasher.GetHashCode(BsonType); hash = 37 * hash + _value.GetHashCode(); return hash; } diff --git a/src/MongoDB.Bson/ObjectModel/BsonDouble.cs b/src/MongoDB.Bson/ObjectModel/BsonDouble.cs index 407b3c946ff..af10befca21 100644 --- a/src/MongoDB.Bson/ObjectModel/BsonDouble.cs +++ b/src/MongoDB.Bson/ObjectModel/BsonDouble.cs @@ -15,6 +15,7 @@ using System; using MongoDB.Bson.IO; +using MongoDB.Shared; namespace MongoDB.Bson { @@ -198,7 +199,7 @@ public override int GetHashCode() { // see Effective Java by Joshua Bloch int hash = 17; - hash = 37 * hash + BsonType.GetHashCode(); + hash = 37 * hash + Hasher.GetHashCode(BsonType); hash = 37 * hash + _value.GetHashCode(); return hash; } diff --git a/src/MongoDB.Bson/ObjectModel/BsonInt32.cs b/src/MongoDB.Bson/ObjectModel/BsonInt32.cs index 6c57461c31a..86fa49bab41 100644 --- a/src/MongoDB.Bson/ObjectModel/BsonInt32.cs +++ b/src/MongoDB.Bson/ObjectModel/BsonInt32.cs @@ -15,6 +15,7 @@ using System; using MongoDB.Bson.IO; +using MongoDB.Shared; namespace MongoDB.Bson { @@ -257,7 +258,7 @@ public override int GetHashCode() { // see Effective Java by Joshua Bloch int hash = 17; - hash = 37 * hash + BsonType.GetHashCode(); + hash = 37 * hash + Hasher.GetHashCode(BsonType); hash = 37 * hash + _value.GetHashCode(); return hash; } diff --git a/src/MongoDB.Bson/ObjectModel/BsonInt64.cs b/src/MongoDB.Bson/ObjectModel/BsonInt64.cs index 7a81dc637dd..fd4cd4c98b5 100644 --- a/src/MongoDB.Bson/ObjectModel/BsonInt64.cs +++ b/src/MongoDB.Bson/ObjectModel/BsonInt64.cs @@ -15,6 +15,7 @@ using System; using MongoDB.Bson.IO; +using MongoDB.Shared; namespace MongoDB.Bson { @@ -211,7 +212,7 @@ public override int GetHashCode() { // see Effective Java by Joshua Bloch int hash = 17; - hash = 37 * hash + BsonType.GetHashCode(); + hash = 37 * hash + Hasher.GetHashCode(BsonType); hash = 37 * hash + _value.GetHashCode(); return hash; } diff --git a/src/MongoDB.Bson/ObjectModel/BsonJavaScript.cs b/src/MongoDB.Bson/ObjectModel/BsonJavaScript.cs index 8887742c553..8fd98300e1c 100644 --- a/src/MongoDB.Bson/ObjectModel/BsonJavaScript.cs +++ b/src/MongoDB.Bson/ObjectModel/BsonJavaScript.cs @@ -14,6 +14,7 @@ */ using System; +using MongoDB.Shared; namespace MongoDB.Bson { @@ -163,7 +164,7 @@ public override int GetHashCode() { // see Effective Java by Joshua Bloch int hash = 17; - hash = 37 * hash + BsonType.GetHashCode(); + hash = 37 * hash + Hasher.GetHashCode(BsonType); hash = 37 * hash + _code.GetHashCode(); return hash; } diff --git a/src/MongoDB.Bson/ObjectModel/BsonMaxKey.cs b/src/MongoDB.Bson/ObjectModel/BsonMaxKey.cs index 83f78ca89dd..682a32c0521 100644 --- a/src/MongoDB.Bson/ObjectModel/BsonMaxKey.cs +++ b/src/MongoDB.Bson/ObjectModel/BsonMaxKey.cs @@ -14,6 +14,7 @@ */ using System; +using MongoDB.Shared; namespace MongoDB.Bson { @@ -122,7 +123,7 @@ public override bool Equals(object obj) /// The hash code. public override int GetHashCode() { - return BsonType.GetHashCode(); + return Hasher.GetHashCode(BsonType); } /// diff --git a/src/MongoDB.Bson/ObjectModel/BsonMinKey.cs b/src/MongoDB.Bson/ObjectModel/BsonMinKey.cs index 6b070cb072e..c29e786eea5 100644 --- a/src/MongoDB.Bson/ObjectModel/BsonMinKey.cs +++ b/src/MongoDB.Bson/ObjectModel/BsonMinKey.cs @@ -14,6 +14,7 @@ */ using System; +using MongoDB.Shared; namespace MongoDB.Bson { @@ -122,7 +123,7 @@ public override bool Equals(object obj) /// The hash code. public override int GetHashCode() { - return BsonType.GetHashCode(); + return Hasher.GetHashCode(BsonType); } /// diff --git a/src/MongoDB.Bson/ObjectModel/BsonNull.cs b/src/MongoDB.Bson/ObjectModel/BsonNull.cs index 5128a0afb6f..a585100dad0 100644 --- a/src/MongoDB.Bson/ObjectModel/BsonNull.cs +++ b/src/MongoDB.Bson/ObjectModel/BsonNull.cs @@ -14,6 +14,7 @@ */ using System; +using MongoDB.Shared; namespace MongoDB.Bson { @@ -124,7 +125,7 @@ public override bool Equals(object obj) /// The hash code. public override int GetHashCode() { - return BsonType.GetHashCode(); + return Hasher.GetHashCode(BsonType); } /// diff --git a/src/MongoDB.Bson/ObjectModel/BsonObjectId.cs b/src/MongoDB.Bson/ObjectModel/BsonObjectId.cs index d9c79f61afe..17fc99e1de6 100644 --- a/src/MongoDB.Bson/ObjectModel/BsonObjectId.cs +++ b/src/MongoDB.Bson/ObjectModel/BsonObjectId.cs @@ -14,6 +14,7 @@ */ using System; +using MongoDB.Shared; namespace MongoDB.Bson { @@ -337,7 +338,7 @@ public override bool Equals(object obj) public override int GetHashCode() { int hash = 17; - hash = 37 * hash + BsonType.GetHashCode(); + hash = 37 * hash + Hasher.GetHashCode(BsonType); hash = 37 * hash + _value.GetHashCode(); return hash; } diff --git a/src/MongoDB.Bson/ObjectModel/BsonRegularExpression.cs b/src/MongoDB.Bson/ObjectModel/BsonRegularExpression.cs index 013268c4ea9..ab754b913f7 100644 --- a/src/MongoDB.Bson/ObjectModel/BsonRegularExpression.cs +++ b/src/MongoDB.Bson/ObjectModel/BsonRegularExpression.cs @@ -15,6 +15,7 @@ using System; using System.Text.RegularExpressions; +using MongoDB.Shared; namespace MongoDB.Bson { @@ -243,7 +244,7 @@ public override int GetHashCode() { // see Effective Java by Joshua Bloch int hash = 17; - hash = 37 * hash + BsonType.GetHashCode(); + hash = 37 * hash + Hasher.GetHashCode(BsonType); hash = 37 * hash + _pattern.GetHashCode(); hash = 37 * hash + _options.GetHashCode(); return hash; diff --git a/src/MongoDB.Bson/ObjectModel/BsonString.cs b/src/MongoDB.Bson/ObjectModel/BsonString.cs index 2f4c464038f..24875d7af3f 100644 --- a/src/MongoDB.Bson/ObjectModel/BsonString.cs +++ b/src/MongoDB.Bson/ObjectModel/BsonString.cs @@ -15,6 +15,7 @@ using System; using MongoDB.Bson.IO; +using MongoDB.Shared; namespace MongoDB.Bson { @@ -195,7 +196,7 @@ public override int GetHashCode() { // see Effective Java by Joshua Bloch int hash = 17; - hash = 37 * hash + BsonType.GetHashCode(); + hash = 37 * hash + Hasher.GetHashCode(BsonType); hash = 37 * hash + _value.GetHashCode(); return hash; } diff --git a/src/MongoDB.Bson/ObjectModel/BsonTimestamp.cs b/src/MongoDB.Bson/ObjectModel/BsonTimestamp.cs index 6258feed4ae..c36b8a39635 100644 --- a/src/MongoDB.Bson/ObjectModel/BsonTimestamp.cs +++ b/src/MongoDB.Bson/ObjectModel/BsonTimestamp.cs @@ -15,6 +15,7 @@ using System; using MongoDB.Bson.IO; +using MongoDB.Shared; namespace MongoDB.Bson { @@ -184,7 +185,7 @@ public override int GetHashCode() { // see Effective Java by Joshua Bloch int hash = 17; - hash = 37 * hash + BsonType.GetHashCode(); + hash = 37 * hash + Hasher.GetHashCode(BsonType); hash = 37 * hash + _value.GetHashCode(); return hash; } diff --git a/src/MongoDB.Bson/ObjectModel/BsonTypeMapper.cs b/src/MongoDB.Bson/ObjectModel/BsonTypeMapper.cs index 1a7079493f0..48d7c888cb9 100644 --- a/src/MongoDB.Bson/ObjectModel/BsonTypeMapper.cs +++ b/src/MongoDB.Bson/ObjectModel/BsonTypeMapper.cs @@ -407,7 +407,7 @@ public static object MapToDotNetValue(BsonValue bsonValue, BsonTypeMapperOptions } else { - var message = string.Format("A BsonDocument can't be mapped to a {0}.", BsonUtils.GetFriendlyTypeName(options.MapBsonArrayTo)); + var message = string.Format("A BsonDocument can't be mapped to a {0}.", BsonUtils.GetFriendlyTypeName(options.MapBsonDocumentTo)); throw new NotSupportedException(message); } case BsonType.Double: diff --git a/src/MongoDB.Bson/ObjectModel/BsonUndefined.cs b/src/MongoDB.Bson/ObjectModel/BsonUndefined.cs index 42322d8e2a8..74942df14b1 100644 --- a/src/MongoDB.Bson/ObjectModel/BsonUndefined.cs +++ b/src/MongoDB.Bson/ObjectModel/BsonUndefined.cs @@ -14,6 +14,7 @@ */ using System; +using MongoDB.Shared; namespace MongoDB.Bson { @@ -123,7 +124,7 @@ public override bool Equals(object obj) /// The hash code. public override int GetHashCode() { - return BsonType.GetHashCode(); + return Hasher.GetHashCode(BsonType); } /// diff --git a/src/MongoDB.Bson/Serialization/BsonClassMap.cs b/src/MongoDB.Bson/Serialization/BsonClassMap.cs index 1b5ba072873..41d4cb82163 100644 --- a/src/MongoDB.Bson/Serialization/BsonClassMap.cs +++ b/src/MongoDB.Bson/Serialization/BsonClassMap.cs @@ -415,6 +415,112 @@ public static void RegisterClassMap(BsonClassMap classMap) } } + /// + /// Registers a class map if it is not already registered. + /// + /// The class. + /// True if this call registered the class map, false if the class map was already registered. + public static bool TryRegisterClassMap() + { + return TryRegisterClassMap(ClassMapFactory); + + static BsonClassMap ClassMapFactory() + { + var classMap = new BsonClassMap(); + classMap.AutoMap(); + return classMap; + } + } + + /// + /// Registers a class map if it is not already registered. + /// + /// The class. + /// The class map. + /// True if this call registered the class map, false if the class map was already registered. + public static bool TryRegisterClassMap(BsonClassMap classMap) + { + if (classMap == null) + { + throw new ArgumentNullException(nameof(classMap)); + } + + return TryRegisterClassMap(ClassMapFactory); + + BsonClassMap ClassMapFactory() + { + return classMap; + } + } + + /// + /// Registers a class map if it is not already registered. + /// + /// The class. + /// The class map initializer (only called if the class map is not already registered). + /// True if this call registered the class map, false if the class map was already registered. + public static bool TryRegisterClassMap(Action> classMapInitializer) + { + if (classMapInitializer == null) + { + throw new ArgumentNullException(nameof(classMapInitializer)); + } + + return TryRegisterClassMap(ClassMapFactory); + + BsonClassMap ClassMapFactory() + { + return new BsonClassMap(classMapInitializer); + } + } + + /// + /// Registers a class map if it is not already registered. + /// + /// The class. + /// The class map factory (only called if the class map is not already registered). + /// True if this call registered the class map, false if the class map was already registered. + public static bool TryRegisterClassMap(Func> classMapFactory) + { + if (classMapFactory == null) + { + throw new ArgumentNullException(nameof(classMapFactory)); + } + + BsonSerializer.ConfigLock.EnterReadLock(); + try + { + if (__classMaps.ContainsKey(typeof(TClass))) + { + return false; + } + } + finally + { + BsonSerializer.ConfigLock.ExitReadLock(); + } + + BsonSerializer.ConfigLock.EnterWriteLock(); + try + { + if (__classMaps.ContainsKey(typeof(TClass))) + { + return false; + } + else + { + // create a classMap for TClass and register it + var classMap = classMapFactory(); + RegisterClassMap(classMap); + return true; + } + } + finally + { + BsonSerializer.ConfigLock.ExitWriteLock(); + } + } + // public methods /// /// Automaps the class. @@ -1312,6 +1418,15 @@ public BsonClassMap(Action> classMapInitializer) classMapInitializer(this); } + /// + /// Initializes a new instance of the BsonClassMap class. + /// + /// The base class map. + public BsonClassMap(BsonClassMap baseClassMap) + : base(typeof(TClass), baseClassMap) + { + } + // public methods /// /// Creates an instance. diff --git a/src/MongoDB.Bson/Serialization/BsonSerializer.cs b/src/MongoDB.Bson/Serialization/BsonSerializer.cs index 15d4858c3e7..5c027786d87 100644 --- a/src/MongoDB.Bson/Serialization/BsonSerializer.cs +++ b/src/MongoDB.Bson/Serialization/BsonSerializer.cs @@ -678,6 +678,28 @@ public static void Serialize( serializer.Serialize(context, args, value); } + /// + /// Tries to register a serializer for a type. + /// + /// The serializer. + /// The type. + /// True if the serializer was registered on this call, false if the same serializer was already registered on a previous call, throws an exception if a different serializer was already registered. + public static bool TryRegisterSerializer(Type type, IBsonSerializer serializer) + { + return __serializerRegistry.TryRegisterSerializer(type, serializer); + } + + /// + /// Tries to register a serializer for a type. + /// + /// The type. + /// The serializer. + /// True if the serializer was registered on this call, false if the same serializer was already registered on a previous call, throws an exception if a different serializer was already registered. + public static bool TryRegisterSerializer(IBsonSerializer serializer) + { + return TryRegisterSerializer(typeof(T), serializer); + } + // internal static methods internal static void EnsureKnownTypesAreRegistered(Type nominalType) { diff --git a/src/MongoDB.Bson/Serialization/BsonSerializerRegistry.cs b/src/MongoDB.Bson/Serialization/BsonSerializerRegistry.cs index 4ff54ec6134..429386f6019 100644 --- a/src/MongoDB.Bson/Serialization/BsonSerializerRegistry.cs +++ b/src/MongoDB.Bson/Serialization/BsonSerializerRegistry.cs @@ -93,17 +93,7 @@ public void RegisterSerializer(Type type, IBsonSerializer serializer) { throw new ArgumentNullException("serializer"); } - var typeInfo = type.GetTypeInfo(); - if (typeof(BsonValue).GetTypeInfo().IsAssignableFrom(type)) - { - var message = string.Format("A serializer cannot be registered for type {0} because it is a subclass of BsonValue.", BsonUtils.GetFriendlyTypeName(type)); - throw new BsonSerializationException(message); - } - if (typeInfo.IsGenericType && typeInfo.ContainsGenericParameters) - { - var message = string.Format("Generic type {0} has unassigned type parameters.", BsonUtils.GetFriendlyTypeName(type)); - throw new ArgumentException(message, "type"); - } + EnsureRegisteringASerializerForThisTypeIsAllowed(type); if (!_cache.TryAdd(type, serializer)) { @@ -127,6 +117,40 @@ public void RegisterSerializationProvider(IBsonSerializationProvider serializati _serializationProviders.Push(serializationProvider); } + /// + /// Tries to register the serializer. + /// + /// The type. + /// The serializer. + /// True if the serializer was registered on this call, false if the same serializer was already registered on a previous call, throws an exception if a different serializer was already registered. + public bool TryRegisterSerializer(Type type, IBsonSerializer serializer) + { + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + if (serializer == null) + { + throw new ArgumentNullException(nameof(serializer)); + } + EnsureRegisteringASerializerForThisTypeIsAllowed(type); + + if (_cache.TryAdd(type, serializer)) + { + return true; + } + else + { + var existingSerializer = _cache[type]; + if (!existingSerializer.Equals(serializer)) + { + var message = $"There is already a different serializer registered for type {BsonUtils.GetFriendlyTypeName(type)}."; + throw new BsonSerializationException(message); + } + return false; + } + } + // private methods private IBsonSerializer CreateSerializer(Type type) { @@ -153,5 +177,20 @@ private IBsonSerializer CreateSerializer(Type type) var message = string.Format("No serializer found for type {0}.", type.FullName); throw new BsonSerializationException(message); } + + private void EnsureRegisteringASerializerForThisTypeIsAllowed(Type type) + { + var typeInfo = type.GetTypeInfo(); + if (typeof(BsonValue).GetTypeInfo().IsAssignableFrom(type)) + { + var message = string.Format("A serializer cannot be registered for type {0} because it is a subclass of BsonValue.", BsonUtils.GetFriendlyTypeName(type)); + throw new BsonSerializationException(message); + } + if (typeInfo.IsGenericType && typeInfo.ContainsGenericParameters) + { + var message = string.Format("Generic type {0} has unassigned type parameters.", BsonUtils.GetFriendlyTypeName(type)); + throw new ArgumentException(message, "type"); + } + } } } diff --git a/src/MongoDB.Bson/Serialization/CollectionsSerializationProvider.cs b/src/MongoDB.Bson/Serialization/CollectionsSerializationProvider.cs index 9f8fd09a12e..c9496929852 100644 --- a/src/MongoDB.Bson/Serialization/CollectionsSerializationProvider.cs +++ b/src/MongoDB.Bson/Serialization/CollectionsSerializationProvider.cs @@ -204,10 +204,9 @@ private IBsonSerializer GetCollectionSerializer(Type type, IBsonSerializerRegist if (typeInfo.IsInterface) { - var hashSetDefinition = typeof(HashSet<>); - var hashSetType = hashSetDefinition.MakeGenericType(itemType); - var serializerDefinition = typeof(ImpliedImplementationInterfaceSerializer<,>); - return CreateGenericSerializer(serializerDefinition, new[] { type, hashSetType }, serializerRegistry); + var serializerDefinition = typeof(IEnumerableDeserializingAsCollectionSerializer<,,>); + var collectionType = typeof(HashSet<>).MakeGenericType(itemType); + return CreateGenericSerializer(serializerDefinition, new[] { type, itemType, collectionType }, serializerRegistry); } else { @@ -232,12 +231,12 @@ private IBsonSerializer GetCollectionSerializer(Type type, IBsonSerializerRegist } else if (typeInfo.IsInterface) { - var listDefinition = typeof(List<>); - var listType = listDefinition.MakeGenericType(itemType); + var listType = typeof(List<>).MakeGenericType(itemType); if (typeInfo.IsAssignableFrom(listType)) { - var serializerDefinition = typeof(ImpliedImplementationInterfaceSerializer<,>); - return CreateGenericSerializer(serializerDefinition, new[] { type, listType }, serializerRegistry); + var serializerDefinition = typeof(IEnumerableDeserializingAsCollectionSerializer<,,>); + var collectionType = typeof(List<>).MakeGenericType(itemType); + return CreateGenericSerializer(serializerDefinition, new[] { type, itemType, collectionType }, serializerRegistry); } } diff --git a/src/MongoDB.Bson/Serialization/IRepresentationConfigurable.cs b/src/MongoDB.Bson/Serialization/IRepresentationConfigurable.cs index e333e61a4f1..26e8cbf5bae 100644 --- a/src/MongoDB.Bson/Serialization/IRepresentationConfigurable.cs +++ b/src/MongoDB.Bson/Serialization/IRepresentationConfigurable.cs @@ -18,16 +18,19 @@ namespace MongoDB.Bson.Serialization /// /// Represents a serializer that has a Representation property. /// - public interface IRepresentationConfigurable + public interface IHasRepresentationSerializer { /// /// Gets the representation. /// - /// - /// The representation. - /// BsonType Representation { get; } + } + /// + /// Represents a serializer whose representation can be configured. + /// + public interface IRepresentationConfigurable : IHasRepresentationSerializer + { /// /// Returns a serializer that has been reconfigured with the specified representation. /// diff --git a/src/MongoDB.Bson/Serialization/PrimitiveSerializationProvider.cs b/src/MongoDB.Bson/Serialization/PrimitiveSerializationProvider.cs index 441c759e89f..e799e216865 100644 --- a/src/MongoDB.Bson/Serialization/PrimitiveSerializationProvider.cs +++ b/src/MongoDB.Bson/Serialization/PrimitiveSerializationProvider.cs @@ -18,6 +18,7 @@ using System.Globalization; using System.Net; using System.Reflection; +using System.Text.RegularExpressions; using MongoDB.Bson.Serialization.Serializers; namespace MongoDB.Bson.Serialization @@ -53,6 +54,7 @@ static PrimitiveSerializationProvider() { typeof(Nullable<>), typeof(NullableSerializer<>) }, { typeof(Object), typeof(ObjectSerializer) }, { typeof(ObjectId), typeof(ObjectIdSerializer) }, + { typeof(Regex), typeof(RegexSerializer) }, { typeof(SByte), typeof(SByteSerializer) }, { typeof(Single), typeof(SingleSerializer) }, { typeof(String), typeof(StringSerializer) }, diff --git a/src/MongoDB.Bson/Serialization/Serializers/BsonClassMapSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/BsonClassMapSerializer.cs index a3c400fef4e..52265b9fd15 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/BsonClassMapSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/BsonClassMapSerializer.cs @@ -29,7 +29,9 @@ namespace MongoDB.Bson.Serialization public class BsonClassMapSerializer : SerializerBase, IBsonIdProvider, IBsonDocumentSerializer, IBsonPolymorphicSerializer { // private fields - private BsonClassMap _classMap; + private readonly BsonClassMap _classMap; + private readonly bool _isReferenceType; + private readonly bool _isValueType; // constructors /// @@ -53,6 +55,8 @@ public BsonClassMapSerializer(BsonClassMap classMap) } _classMap = classMap; + _isValueType = _classMap.ClassType.IsValueType; + _isReferenceType = !_isValueType; } // public properties @@ -78,12 +82,6 @@ public override TClass Deserialize(BsonDeserializationContext context, BsonDeser { var bsonReader = context.Reader; - if (_classMap.ClassType.GetTypeInfo().IsValueType) - { - var message = string.Format("Value class {0} cannot be deserialized.", _classMap.ClassType.FullName); - throw new BsonSerializationException(message); - } - if (bsonReader.GetCurrentBsonType() == Bson.BsonType.Null) { bsonReader.ReadNull(); @@ -137,8 +135,19 @@ public TClass DeserializeClass(BsonDeserializationContext context) else { // for mutable classes we deserialize the values directly into the result object + if (_isValueType) + { + var message = string.Format("Value class {0} cannot be deserialized without a constructor.", _classMap.ClassType.FullName); + throw new BsonSerializationException(message); + } + document = (TClass)_classMap.CreateInstance(); + if (document == null) + { + throw new BsonSerializationException($"{nameof(BsonClassMap)} did not provide an instance of {typeof(TClass).Name}."); + } + supportsInitialization = document as ISupportInitialize; if (supportsInitialization != null) { @@ -164,7 +173,7 @@ public TClass DeserializeClass(BsonDeserializationContext context) var memberMap = allMemberMaps[memberMapIndex]; if (memberMapIndex != extraElementsMemberMapIndex) { - if (document != null) + if (_isReferenceType && document != null) { if (memberMap.IsReadOnly) { @@ -184,7 +193,7 @@ public TClass DeserializeClass(BsonDeserializationContext context) } else { - if (document != null) + if (_isReferenceType && document != null) { DeserializeExtraElementMember(context, document, elementName, memberMap); } @@ -206,7 +215,7 @@ public TClass DeserializeClass(BsonDeserializationContext context) if (extraElementsMemberMapIndex >= 0) { var extraElementsMemberMap = _classMap.ExtraElementsMemberMap; - if (document != null) + if (_isReferenceType && document != null) { DeserializeExtraElementMember(context, document, elementName, extraElementsMemberMap); } @@ -258,7 +267,7 @@ public TClass DeserializeClass(BsonDeserializationContext context) throw new FormatException(message); } - if (document != null) + if (_isReferenceType && document != null) { memberMap.ApplyDefaultValue(document); } @@ -280,7 +289,7 @@ public TClass DeserializeClass(BsonDeserializationContext context) } } - if (document != null) + if (_isReferenceType && document != null) { if (supportsInitialization != null) { @@ -426,6 +435,12 @@ private TClass CreateInstanceUsingCreator(Dictionary values) var creatorMap = ChooseBestCreator(values); var document = creatorMap.CreateInstance(values); // removes values consumed + if (values.Count > 0 && _isValueType) + { + var message = string.Format("Value class {0} cannot be deserialized unless all values can be passed to a constructor.", _classMap.ClassType.FullName); + throw new BsonSerializationException(message); + } + var supportsInitialization = document as ISupportInitialize; if (supportsInitialization != null) { @@ -636,13 +651,23 @@ private void SerializeDiscriminator(BsonSerializationContext context, Type nomin private void SerializeMember(BsonSerializationContext context, object obj, BsonMemberMap memberMap) { - if (memberMap != _classMap.ExtraElementsMemberMap) + try { - SerializeNormalMember(context, obj, memberMap); + if (memberMap != _classMap.ExtraElementsMemberMap) + { + SerializeNormalMember(context, obj, memberMap); + } + else + { + SerializeExtraElements(context, obj, memberMap); + } } - else + catch (Exception ex) { - SerializeExtraElements(context, obj, memberMap); + var message = string.Format( + "An error occurred while serializing the {0} {1} of class {2}: {3}", // terminating period provided by nested message + memberMap.MemberName, (memberMap.MemberInfo is FieldInfo) ? "field" : "property", memberMap.ClassMap.ClassType.FullName, ex.Message); + throw new BsonSerializationException(message, ex); } } diff --git a/src/MongoDB.Bson/Serialization/Serializers/DateTimeOffsetSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/DateTimeOffsetSerializer.cs index 27780654d71..48ecf234a86 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/DateTimeOffsetSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/DateTimeOffsetSerializer.cs @@ -105,8 +105,8 @@ public override DateTimeOffset Deserialize(BsonDeserializationContext context, B { case BsonType.Array: bsonReader.ReadStartArray(); - ticks = bsonReader.ReadInt64(); - offset = TimeSpan.FromMinutes(bsonReader.ReadInt32()); + ticks = _int64Serializer.Deserialize(context); + offset = TimeSpan.FromMinutes(_int32Serializer.Deserialize(context)); bsonReader.ReadEndArray(); return new DateTimeOffset(ticks, offset); diff --git a/src/MongoDB.Bson/Serialization/Serializers/DictionarySerializerBase.cs b/src/MongoDB.Bson/Serialization/Serializers/DictionarySerializerBase.cs index 73b45873c5c..9b04916444a 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/DictionarySerializerBase.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/DictionarySerializerBase.cs @@ -16,7 +16,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Linq.Expressions; using MongoDB.Bson.IO; using MongoDB.Bson.Serialization.Options; @@ -59,7 +58,7 @@ public DictionarySerializerBase() /// /// The dictionary representation. public DictionarySerializerBase(DictionaryRepresentation dictionaryRepresentation) - : this(dictionaryRepresentation, new ObjectSerializer(), new ObjectSerializer()) + : this(dictionaryRepresentation, BsonSerializer.LookupSerializer(), BsonSerializer.LookupSerializer()) { } diff --git a/src/MongoDB.Bson/Serialization/Serializers/DiscriminatedInterfaceSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/DiscriminatedInterfaceSerializer.cs index 3382f84f3a2..f59caa60836 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/DiscriminatedInterfaceSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/DiscriminatedInterfaceSerializer.cs @@ -23,11 +23,24 @@ namespace MongoDB.Bson.Serialization.Serializers /// Represents a serializer for Interfaces. /// /// The type of the interface. - public class DiscriminatedInterfaceSerializer : SerializerBase // where TInterface is an interface + public class DiscriminatedInterfaceSerializer : SerializerBase, IBsonDocumentSerializer // where TInterface is an interface { + #region static + private static IBsonSerializer CreateInterfaceSerializer() + { + var classMapDefinition = typeof(BsonClassMap<>); + var classMapType = classMapDefinition.MakeGenericType(typeof(TInterface)); + var classMap = (BsonClassMap)Activator.CreateInstance(classMapType); + classMap.AutoMap(); + classMap.Freeze(); + return new BsonClassMapSerializer(classMap); + } + #endregion + // private fields private readonly Type _interfaceType; private readonly IDiscriminatorConvention _discriminatorConvention; + private readonly IBsonSerializer _interfaceSerializer; private readonly IBsonSerializer _objectSerializer; // constructors @@ -46,6 +59,18 @@ public DiscriminatedInterfaceSerializer() /// interfaceType /// interfaceType public DiscriminatedInterfaceSerializer(IDiscriminatorConvention discriminatorConvention) + : this(discriminatorConvention, CreateInterfaceSerializer()) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The discriminator convention. + /// The interface serializer (necessary to support LINQ queries). + /// interfaceType + /// interfaceType + public DiscriminatedInterfaceSerializer(IDiscriminatorConvention discriminatorConvention, IBsonSerializer interfaceSerializer) { var interfaceTypeInfo = typeof(TInterface).GetTypeInfo(); if (!interfaceTypeInfo.IsInterface) @@ -56,7 +81,8 @@ public DiscriminatedInterfaceSerializer(IDiscriminatorConvention discriminatorCo _interfaceType = typeof(TInterface); _discriminatorConvention = discriminatorConvention; - _objectSerializer = new ObjectSerializer(_discriminatorConvention); + _objectSerializer = ((ObjectSerializer)BsonSerializer.LookupSerializer()).WithDiscriminatorConvention(_discriminatorConvention); + _interfaceSerializer = interfaceSerializer; } // public methods @@ -110,5 +136,17 @@ public override void Serialize(BsonSerializationContext context, BsonSerializati _objectSerializer.Serialize(context, args, value); } } + + /// + public bool TryGetMemberSerializationInfo(string memberName, out BsonSerializationInfo serializationInfo) + { + if (_interfaceSerializer is IBsonDocumentSerializer documentSerializer) + { + return documentSerializer.TryGetMemberSerializationInfo(memberName, out serializationInfo); + } + + serializationInfo = null; + return false; + } } } diff --git a/src/MongoDB.Bson/Serialization/Serializers/EnumSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/EnumSerializer.cs index d74dcdb7d4a..477bd318e61 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/EnumSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/EnumSerializer.cs @@ -65,6 +65,11 @@ public EnumSerializer(BsonType representation) throw new BsonSerializationException(message); } + if (representation == 0) + { + representation = GetRepresentationForUnderlyingType(); + } + _representation = representation; _underlyingTypeCode = Type.GetTypeCode(Enum.GetUnderlyingType(typeof(TEnum))); } @@ -102,6 +107,20 @@ public override TEnum Deserialize(BsonDeserializationContext context, BsonDeseri } } + /// + public override bool Equals(object obj) + { + return + obj is EnumSerializer other && + _representation == other.Representation; + } + + /// + public override int GetHashCode() + { + return _representation.GetHashCode(); + } + /// /// Serializes a value. /// @@ -114,16 +133,6 @@ public override void Serialize(BsonSerializationContext context, BsonSerializati switch (_representation) { - case 0: - if (_underlyingTypeCode == TypeCode.Int64 || _underlyingTypeCode == TypeCode.UInt64) - { - goto case BsonType.Int64; - } - else - { - goto case BsonType.Int32; - } - case BsonType.Int32: bsonWriter.WriteInt32(ConvertEnumToInt32(value)); break; @@ -148,6 +157,11 @@ public override void Serialize(BsonSerializationContext context, BsonSerializati /// The reconfigured serializer. public EnumSerializer WithRepresentation(BsonType representation) { + if (representation == 0) + { + representation = GetRepresentationForUnderlyingType(); + } + if (representation == _representation) { return this; @@ -243,6 +257,12 @@ private TEnum ConvertInt64ToEnum(long value) } } + private BsonType GetRepresentationForUnderlyingType() + { + var underlyingType = Enum.GetUnderlyingType(typeof(TEnum)); + return (underlyingType == typeof(long) || underlyingType == typeof(ulong)) ? BsonType.Int64 : BsonType.Int32; + } + private TEnum ConvertStringToEnum(string value) { if (Enum.TryParse(value, ignoreCase: false, out var result)) diff --git a/src/MongoDB.Bson/Serialization/Serializers/IEnumerableDeserializingAsCollectionSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/IEnumerableDeserializingAsCollectionSerializer.cs new file mode 100644 index 00000000000..d2ca097339a --- /dev/null +++ b/src/MongoDB.Bson/Serialization/Serializers/IEnumerableDeserializingAsCollectionSerializer.cs @@ -0,0 +1,173 @@ +/* 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; + +namespace MongoDB.Bson.Serialization.Serializers +{ + /// + /// Represents a serializer for IEnumerable and any other derived interface implemented by TCollection. + /// + /// The type of an IEnumerable interface. + /// The type of the items. + /// The type of the collection used when deserializing. + public class IEnumerableDeserializingAsCollectionSerializer : + SerializerBase, + IBsonArraySerializer, + IChildSerializerConfigurable + where TIEnumerable : class, IEnumerable // TIEnumerable must be an interface + where TCollection : class, ICollection, new() + { + #region static + private static void EnsureTIEnumerableIsAnInterface() + { + if (!typeof(TIEnumerable).IsInterface) + { + // this constraint cannot be specified at compile time + throw new ArgumentException($"The {nameof(TIEnumerable)} type argument is not an interface: {typeof(TIEnumerable)}.", nameof(TIEnumerable)); + } + } + #endregion + + // private fields + private readonly Lazy> _lazyItemSerializer; + + // constructors + /// + /// Initializes a new instance of the IEnumerableDeserializingAsCollectionSerializer class. + /// + public IEnumerableDeserializingAsCollectionSerializer() + : this(BsonSerializer.SerializerRegistry) + { + } + + /// + /// Initializes a new instance of the IEnumerableDeserializingAsCollectionSerializer class. + /// + /// The item serializer. + public IEnumerableDeserializingAsCollectionSerializer(IBsonSerializer itemSerializer) + { + EnsureTIEnumerableIsAnInterface(); + if (itemSerializer == null) + { + throw new ArgumentNullException(nameof(itemSerializer)); + } + + _lazyItemSerializer = new Lazy>(() => itemSerializer); + } + + /// + /// Initializes a new instance of the IEnumerableDeserializingAsCollectionSerializer class. + /// + /// The serializer registry. + public IEnumerableDeserializingAsCollectionSerializer(IBsonSerializerRegistry serializerRegistry) + { + EnsureTIEnumerableIsAnInterface(); + if (serializerRegistry == null) + { + throw new ArgumentNullException(nameof(serializerRegistry)); + } + + _lazyItemSerializer = new Lazy>(serializerRegistry.GetSerializer); + } + + // public properties + /// + /// Gets the item serializer. + /// + /// + /// The item serializer. + /// + public IBsonSerializer ItemSerializer => _lazyItemSerializer.Value; + + // public methods + /// + public override TIEnumerable Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) + { + var reader = context.Reader; + + if (reader.GetCurrentBsonType() == BsonType.Null) + { + reader.ReadNull(); + return null; + } + else + { + reader.ReadStartArray(); + var collection = new TCollection(); + var itemSerializer = _lazyItemSerializer.Value; + while (reader.ReadBsonType() != 0) + { + var item = itemSerializer.Deserialize(context); + collection.Add(item); + } + reader.ReadEndArray(); + return (TIEnumerable)(object)collection; + } + } + + /// + public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, TIEnumerable value) + { + var writer = context.Writer; + + if (value == null) + { + writer.WriteNull(); + } + else + { + writer.WriteStartArray(); + var itemSerializer = _lazyItemSerializer.Value; + foreach (var item in value) + { + itemSerializer.Serialize(context, item); + } + writer.WriteEndArray(); + } + } + + /// + /// Tries to get the serialization info for the individual items of the array. + /// + /// The serialization information. + /// + /// The serialization info for the items. + /// + public bool TryGetItemSerializationInfo(out BsonSerializationInfo serializationInfo) + { + var itemSerializer = _lazyItemSerializer.Value; + serializationInfo = new BsonSerializationInfo(null, itemSerializer, itemSerializer.ValueType); + return true; + } + + /// + /// Returns a serializer that has been reconfigured with the specified item serializer. + /// + /// The item serializer. + /// The reconfigured serializer. + public IEnumerableDeserializingAsCollectionSerializer WithItemSerializer(IBsonSerializer itemSerializer) + { + return new IEnumerableDeserializingAsCollectionSerializer(itemSerializer); + } + + // explicit interface implementations + IBsonSerializer IChildSerializerConfigurable.ChildSerializer => ItemSerializer; + + IBsonSerializer IChildSerializerConfigurable.WithChildSerializer(IBsonSerializer childSerializer) + => WithItemSerializer((IBsonSerializer)childSerializer); + } +} diff --git a/src/MongoDB.Bson/Serialization/Serializers/NullableGenericSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/NullableGenericSerializer.cs index 3032c1c1280..a527d915b95 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/NullableGenericSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/NullableGenericSerializer.cs @@ -14,18 +14,46 @@ */ using System; -using MongoDB.Bson.IO; -using MongoDB.Bson.Serialization.Attributes; namespace MongoDB.Bson.Serialization.Serializers { + /// + /// An interface used by the LINQ3 translators to access the value serializer without needing to use reflection. + /// + public interface INullableSerializer + { + /// + /// Gets the value serializer. + /// + IBsonSerializer ValueSerializer { get; } + } + + /// + /// Static factory class for NullableSerializers. + /// + public static class NullableSerializer + { + /// + /// Creates a NullableSerializer. + /// + /// The value serializer. + /// A NullableSerializer + public static IBsonSerializer Create(IBsonSerializer valueSerializer) + { + var valueType = valueSerializer.ValueType; + var nullableSerializerType = typeof(NullableSerializer<>).MakeGenericType(valueType); + return (IBsonSerializer)Activator.CreateInstance(nullableSerializerType, valueSerializer); + } + } + /// /// Represents a serializer for nullable values. /// /// The underlying type. public class NullableSerializer : SerializerBase>, - IChildSerializerConfigurable + IChildSerializerConfigurable, + INullableSerializer where T : struct { // private fields @@ -68,6 +96,15 @@ public NullableSerializer(IBsonSerializerRegistry serializerRegistry) _lazySerializer = new Lazy>(() => serializerRegistry.GetSerializer()); } + // public properties + /// + /// Gets the value serializer. + /// + public IBsonSerializer ValueSerializer => _lazySerializer.Value; + + // explicitly implemented properties + IBsonSerializer INullableSerializer.ValueSerializer => ValueSerializer; + // public methods /// /// Deserializes a value. @@ -91,6 +128,17 @@ public NullableSerializer(IBsonSerializerRegistry serializerRegistry) } } + /// + public override bool Equals(object obj) + { + return + obj is NullableSerializer other && + ValueSerializer.Equals(other.ValueSerializer); + } + + /// + public override int GetHashCode() => ValueSerializer.GetHashCode(); + /// /// Serializes a value. /// diff --git a/src/MongoDB.Bson/Serialization/Serializers/ObjectSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/ObjectSerializer.cs index 1415d0160fe..cdfce696933 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/ObjectSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/ObjectSerializer.cs @@ -14,8 +14,11 @@ */ using System; +using System.Collections.Generic; +using System.Linq; using MongoDB.Bson.IO; using MongoDB.Bson.Serialization.Conventions; +using MongoDB.Shared; namespace MongoDB.Bson.Serialization.Serializers { @@ -24,10 +27,36 @@ namespace MongoDB.Bson.Serialization.Serializers /// public class ObjectSerializer : ClassSerializerBase { + #region static // private static fields + private static readonly Func __allAllowedTypes = t => true; private static readonly ObjectSerializer __instance = new ObjectSerializer(); + private static readonly Func __noAllowedTypes = t => false; + + // public static properties + /// + /// An allowed types function that returns true for all types. + /// + public static Func AllAllowedTypes => __allAllowedTypes; + + /// + /// An allowed types function that returns true for framework types known to be safe. + /// + public static Func DefaultAllowedTypes => DefaultFrameworkAllowedTypes.AllowedTypes; + + /// + /// Gets the standard instance. + /// + public static ObjectSerializer Instance => __instance; + + /// + /// An allowed types function that returns false for all types. + /// + public static Func NoAllowedTypes => __noAllowedTypes; + #endregion // private fields + private readonly Func _allowedTypes; private readonly IDiscriminatorConvention _discriminatorConvention; private readonly GuidRepresentation _guidRepresentation; private readonly GuidSerializer _guidSerializer; @@ -57,27 +86,50 @@ public ObjectSerializer(IDiscriminatorConvention discriminatorConvention) /// The discriminator convention. /// The Guid representation. public ObjectSerializer(IDiscriminatorConvention discriminatorConvention, GuidRepresentation guidRepresentation) + : this(discriminatorConvention, guidRepresentation, DefaultFrameworkAllowedTypes.AllowedTypes) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// A delegate that determines what types are allowed. + public ObjectSerializer(Func allowedTypes) + : this(BsonSerializer.LookupDiscriminatorConvention(typeof(object)), allowedTypes) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The discriminator convention. + /// A delegate that determines what types are allowed. + public ObjectSerializer(IDiscriminatorConvention discriminatorConvention, Func allowedTypes) + : this(discriminatorConvention, GuidRepresentation.Unspecified, allowedTypes) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The discriminator convention. + /// The Guid representation. + /// A delegate that determines what types are allowed. + public ObjectSerializer(IDiscriminatorConvention discriminatorConvention, GuidRepresentation guidRepresentation, Func allowedTypes) { if (discriminatorConvention == null) { throw new ArgumentNullException("discriminatorConvention"); } + if (allowedTypes == null) + { + throw new ArgumentNullException(nameof(allowedTypes)); + } _discriminatorConvention = discriminatorConvention; _guidRepresentation = guidRepresentation; _guidSerializer = new GuidSerializer(_guidRepresentation); - } - - // public static properties - /// - /// Gets the standard instance. - /// - /// - /// The standard instance. - /// - public static ObjectSerializer Instance - { - get { return __instance; } + _allowedTypes = allowedTypes; } // public methods @@ -165,6 +217,21 @@ public override object Deserialize(BsonDeserializationContext context, BsonDeser } } + /// + public override bool Equals(object obj) => + obj is ObjectSerializer other && + GetType() == other.GetType() && + _allowedTypes.Equals(other._allowedTypes) && + _discriminatorConvention.Equals(other._discriminatorConvention) && + _guidRepresentation == other._guidRepresentation; + + /// + public override int GetHashCode() => + new Hasher() + .Hash(_discriminatorConvention) + .Hash(_guidRepresentation) + .GetHashCode(); + /// /// Serializes a value. /// @@ -264,12 +331,27 @@ public override void Serialize(BsonSerializationContext context, BsonSerializati } } + /// + /// Returns a new ObjectSerializer configured the same but with the specified discriminator convention. + /// + /// The discriminator convention. + /// An ObjectSerializer with the specified discriminator convention. + public ObjectSerializer WithDiscriminatorConvention(IDiscriminatorConvention discriminatorConvention) + { + return new ObjectSerializer(discriminatorConvention, _guidRepresentation, _allowedTypes); + } + // private methods private object DeserializeDiscriminatedValue(BsonDeserializationContext context, BsonDeserializationArgs args) { var bsonReader = context.Reader; var actualType = _discriminatorConvention.GetActualType(bsonReader, typeof(object)); + if (!_allowedTypes(actualType)) + { + throw new BsonSerializationException($"Type {actualType.FullName} is not configured as an allowed type for this instance of ObjectSerializer."); + } + if (actualType == typeof(object)) { var type = bsonReader.GetCurrentBsonType(); @@ -333,6 +415,11 @@ private object DeserializeDiscriminatedValue(BsonDeserializationContext context, private void SerializeDiscriminatedValue(BsonSerializationContext context, BsonSerializationArgs args, object value, Type actualType) { + if (!_allowedTypes(actualType)) + { + throw new BsonSerializationException($"Type {actualType.FullName} is not configured as an allowed type for this instance of ObjectSerializer."); + } + var serializer = BsonSerializer.LookupSerializer(actualType); var polymorphicSerializer = serializer as IBsonPolymorphicSerializer; @@ -361,5 +448,101 @@ private void SerializeDiscriminatedValue(BsonSerializationContext context, BsonS } } } + + // nested types + private static class DefaultFrameworkAllowedTypes + { + private readonly static HashSet __allowedTypes = new HashSet + { + typeof(System.Boolean), + typeof(System.Byte), + typeof(System.Char), + typeof(System.Collections.ArrayList), + typeof(System.Collections.BitArray), + typeof(System.Collections.Hashtable), + typeof(System.Collections.Queue), + typeof(System.Collections.SortedList), + typeof(System.Collections.Specialized.ListDictionary), + typeof(System.Collections.Specialized.OrderedDictionary), + typeof(System.Collections.Stack), + typeof(System.DateTime), + typeof(System.DateTimeOffset), + typeof(System.Decimal), + typeof(System.Double), + typeof(System.Dynamic.ExpandoObject), + typeof(System.Guid), + typeof(System.Int16), + typeof(System.Int32), + typeof(System.Int64), + typeof(System.Net.DnsEndPoint), + typeof(System.Net.EndPoint), + typeof(System.Net.IPAddress), + typeof(System.Net.IPEndPoint), + typeof(System.Net.IPHostEntry), + typeof(System.Object), + typeof(System.SByte), + typeof(System.Single), + typeof(System.String), + typeof(System.Text.RegularExpressions.Regex), + typeof(System.TimeSpan), + typeof(System.UInt16), + typeof(System.UInt32), + typeof(System.UInt64), + typeof(System.Uri), + typeof(System.Version) + }; + + private readonly static HashSet __allowedGenericTypes = new HashSet + { + typeof(System.Collections.Generic.Dictionary<,>), + typeof(System.Collections.Generic.HashSet<>), + typeof(System.Collections.Generic.KeyValuePair<,>), + typeof(System.Collections.Generic.LinkedList<>), + typeof(System.Collections.Generic.List<>), + typeof(System.Collections.Generic.Queue<>), + typeof(System.Collections.Generic.SortedDictionary<,>), + typeof(System.Collections.Generic.SortedList<,>), + typeof(System.Collections.Generic.SortedSet<>), + typeof(System.Collections.Generic.Stack<>), + typeof(System.Collections.ObjectModel.Collection<>), + typeof(System.Collections.ObjectModel.KeyedCollection<,>), + typeof(System.Collections.ObjectModel.ObservableCollection<>), + typeof(System.Collections.ObjectModel.ReadOnlyCollection<>), + typeof(System.Collections.ObjectModel.ReadOnlyDictionary<,>), + typeof(System.Collections.ObjectModel.ReadOnlyObservableCollection<>), + typeof(System.Nullable<>), + typeof(System.Tuple<>), + typeof(System.Tuple<,>), + typeof(System.Tuple<,,>), + typeof(System.Tuple<,,,>), + typeof(System.Tuple<,,,,>), + typeof(System.Tuple<,,,,,>), + typeof(System.Tuple<,,,,,,>), + typeof(System.Tuple<,,,,,,,>), + typeof(System.ValueTuple<,,,,,,,>), + typeof(System.ValueTuple<>), + typeof(System.ValueTuple<,>), + typeof(System.ValueTuple<,,>), + typeof(System.ValueTuple<,,,>), + typeof(System.ValueTuple<,,,,>), + typeof(System.ValueTuple<,,,,,>), + typeof(System.ValueTuple<,,,,,,>), + typeof(System.ValueTuple<,,,,,,,>) + }; + + public static bool AllowedTypes(Type type) + { + return type.IsConstructedGenericType ? IsAllowedGenericType(type) : IsAllowedType(type); + + static bool IsAllowedType(Type type) => + __allowedTypes.Contains(type) || + type.IsArray && AllowedTypes(type.GetElementType()) || + type.IsEnum; + + static bool IsAllowedGenericType(Type type) => + __allowedGenericTypes.Contains(type.GetGenericTypeDefinition()) && + type.GetGenericArguments().All(AllowedTypes); + } + } } } diff --git a/src/MongoDB.Bson/Serialization/Serializers/RawBsonDocumentSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/RawBsonDocumentSerializer.cs index 55c06ba20c0..beba07d8893 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/RawBsonDocumentSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/RawBsonDocumentSerializer.cs @@ -19,7 +19,7 @@ namespace MongoDB.Bson.Serialization.Serializers /// /// Represents a serializer for RawBsonDocuments. /// - public class RawBsonDocumentSerializer : BsonValueSerializerBase + public class RawBsonDocumentSerializer : BsonValueSerializerBase, IBsonDocumentSerializer { // private static fields private static readonly RawBsonDocumentSerializer __instance = new RawBsonDocumentSerializer(); @@ -70,5 +70,15 @@ protected override void SerializeValue(BsonSerializationContext context, BsonSer var bsonWriter = context.Writer; bsonWriter.WriteRawBsonDocument(value.Slice); } + + /// + public bool TryGetMemberSerializationInfo(string memberName, out BsonSerializationInfo serializationInfo) + { + serializationInfo = new BsonSerializationInfo( + memberName, + BsonValueSerializer.Instance, + typeof(BsonValue)); + return true; + } } } diff --git a/src/MongoDB.Bson/Serialization/Serializers/RegexSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/RegexSerializer.cs index d944d1d3d70..a6c9661ebcf 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/RegexSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/RegexSerializer.cs @@ -90,6 +90,10 @@ public override Regex Deserialize(BsonDeserializationContext context, BsonDeseri var bsonType = reader.GetCurrentBsonType(); switch (bsonType) { + case BsonType.Null: + reader.ReadNull(); + return null; + case BsonType.RegularExpression: return reader.ReadRegularExpression().ToRegex(); @@ -111,6 +115,12 @@ public override void Serialize(BsonSerializationContext context, BsonSerializati { var writer = context.Writer; + if (value == null) + { + writer.WriteNull(); + return; + } + switch (_representation) { case BsonType.RegularExpression: diff --git a/src/MongoDB.Bson/Serialization/TypeNameDiscriminator.cs b/src/MongoDB.Bson/Serialization/TypeNameDiscriminator.cs index a0c0d81aeb9..4032179f934 100644 --- a/src/MongoDB.Bson/Serialization/TypeNameDiscriminator.cs +++ b/src/MongoDB.Bson/Serialization/TypeNameDiscriminator.cs @@ -27,17 +27,25 @@ namespace MongoDB.Bson.Serialization public static class TypeNameDiscriminator { // private static fields - private static Assembly[] __wellKnownAssemblies; + private static readonly HashSet __wellKnownAssemblies = new HashSet(); // static constructor static TypeNameDiscriminator() { - __wellKnownAssemblies = new Assembly[] + var representativeTypes = new Type[] { - typeof(object).GetTypeInfo().Assembly, // mscorlib - typeof(Queue<>).GetTypeInfo().Assembly, // System - typeof(HashSet<>).GetTypeInfo().Assembly // System.Core + // the actual locations of these types varies depending on the target framework + typeof(object), + typeof(Queue<>), + typeof(HashSet<>), + typeof(SortedSet<>) }; + + foreach (var type in representativeTypes) + { + var assembly = type.GetTypeInfo().Assembly; + __wellKnownAssemblies.Add(assembly); + } } // public static methods diff --git a/src/MongoDB.Driver.Core/ChangeStreamDocument.cs b/src/MongoDB.Driver.Core/ChangeStreamDocument.cs index d5338c587d3..189fae5bc5f 100644 --- a/src/MongoDB.Driver.Core/ChangeStreamDocument.cs +++ b/src/MongoDB.Driver.Core/ChangeStreamDocument.cs @@ -92,6 +92,37 @@ public ChangeStreamDocument( /// public DatabaseNamespace DatabaseNamespace => GetValue(nameof(DatabaseNamespace), null); + /// + /// Gets the disambiguated paths if present. + /// + /// + /// The disambiguated paths. + /// + /// + /// + /// A document containing a map that associates an update path to an array containing the path components used in the update document. This data + /// can be used in combination with the other fields in an to determine the + /// actual path in the document that was updated. This is necessary in cases where a key contains dot-separated strings (i.e. { "a.b": "c" }) or + /// a document contains a numeric literal string key (i.e. { "a": { "0": "a" } }). Note that in this scenario, the numeric key can't be the top + /// level key because { "0": "a" } is not ambiguous - update paths would simply be '0' which is unambiguous because BSON documents cannot have + /// arrays at the top level. Each entry in the document maps an update path to an array which contains the actual path used when the document + /// was updated. For example, given a document with the following shape { "a": { "0": 0 } } and an update of { $inc: { "a.0": 1 } }, + /// would look like the following: + /// + /// + /// { + /// "a.0": ["a", "0"] + /// } + /// + /// + /// In each array, all elements will be returned as strings with the exception of array indices, which will be returned as 32-bit integers. + /// + /// + /// Added in MongoDB version 6.1.0. + /// + /// + public BsonDocument DisambiguatedPaths => GetValue(nameof(DisambiguatedPaths), null); + /// /// Gets the document key. /// diff --git a/src/MongoDB.Driver.Core/ChangeStreamDocumentSerializer.cs b/src/MongoDB.Driver.Core/ChangeStreamDocumentSerializer.cs index 973f0dca06a..61e3cc850c0 100644 --- a/src/MongoDB.Driver.Core/ChangeStreamDocumentSerializer.cs +++ b/src/MongoDB.Driver.Core/ChangeStreamDocumentSerializer.cs @@ -44,6 +44,7 @@ public ChangeStreamDocumentSerializer( RegisterMember("CollectionNamespace", "ns", ChangeStreamDocumentCollectionNamespaceSerializer.Instance); RegisterMember("CollectionUuid", "ui", GuidSerializer.StandardInstance); RegisterMember("DatabaseNamespace", "ns", ChangeStreamDocumentDatabaseNamespaceSerializer.Instance); + RegisterMember("DisambiguatedPaths", "disambiguatedPaths", BsonDocumentSerializer.Instance); RegisterMember("DocumentKey", "documentKey", BsonDocumentSerializer.Instance); RegisterMember("FullDocument", "fullDocument", _documentSerializer); RegisterMember("FullDocumentBeforeChange", "fullDocumentBeforeChange", _documentSerializer); diff --git a/src/MongoDB.Driver.Core/Core/Authentication/External/AwsAuthenticationCredentialsProvider.cs b/src/MongoDB.Driver.Core/Core/Authentication/External/AwsAuthenticationCredentialsProvider.cs new file mode 100644 index 00000000000..64ab6552a6a --- /dev/null +++ b/src/MongoDB.Driver.Core/Core/Authentication/External/AwsAuthenticationCredentialsProvider.cs @@ -0,0 +1,105 @@ +/* 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.Security; +using System.Threading; +using System.Threading.Tasks; +using Amazon.Runtime; +using MongoDB.Bson; +using MongoDB.Driver.Core.Misc; + +namespace MongoDB.Driver.Core.Authentication.External +{ + internal sealed class AwsCredentials : IExternalCredentials + { + private readonly string _accessKeyId; + private readonly SecureString _secretAccessKey; + private readonly string _sessionToken; + + public AwsCredentials(string accessKeyId, string secretAccessKey, string sessionToken) + : this(accessKeyId, SecureStringHelper.ToSecureString(Ensure.IsNotNull(secretAccessKey, nameof(secretAccessKey))), sessionToken) + { + } + + public AwsCredentials(string accessKeyId, SecureString secretAccessKey, string sessionToken) + { + _accessKeyId = Ensure.IsNotNull(accessKeyId, nameof(accessKeyId)); + _secretAccessKey = Ensure.IsNotNull(secretAccessKey, nameof(secretAccessKey)); + _sessionToken = sessionToken; // can be null + } + + public string AccessKeyId => _accessKeyId; + + /// + /// Expiration and caching related logic happens on AWS.SDK side. + /// + public DateTime? Expiration => (DateTime?)null; + public SecureString SecretAccessKey => _secretAccessKey; + public string SessionToken => _sessionToken; + public bool ShouldBeRefreshed => true; + + public BsonDocument GetKmsCredentials() => + new BsonDocument + { + { "accessKeyId", _accessKeyId }, + { "secretAccessKey", SecureStringHelper.ToInsecureString(_secretAccessKey) }, + { "sessionToken", _sessionToken, _sessionToken != null } + }; + } + + internal class AwsAuthenticationCredentialsProvider : IExternalAuthenticationCredentialsProvider, ICredentialsCache + { + private readonly object _lock = new object(); + + public void Clear() + { + lock (_lock) + { + FallbackCredentialsFactory.Reset(); + } + } + + public AwsCredentials CreateCredentialsFromExternalSource(CancellationToken cancellationToken) + { + AWSCredentials credentialsSource; + lock (_lock) + { + // returns cached credentials source immediately. Only if cached source unavailable, makes quite heavy steps + credentialsSource = FallbackCredentialsFactory.GetCredentials(); + } + var immutableCredentials = credentialsSource.GetCredentials(); + return CreateAwsCredentials(immutableCredentials); + } + + public async Task CreateCredentialsFromExternalSourceAsync(CancellationToken cancellationToken) + { + AWSCredentials credentialsSource; + lock (_lock) + { + // returns cached credentials source immediately. Only if cached source unavailable, makes quite heavy steps + credentialsSource = FallbackCredentialsFactory.GetCredentials(); + } + var immutableCredentials = await credentialsSource.GetCredentialsAsync().ConfigureAwait(false); + return CreateAwsCredentials(immutableCredentials); + } + + private AwsCredentials CreateAwsCredentials(ImmutableCredentials immutableCredentials) + { + var token = immutableCredentials.Token; + return new AwsCredentials(immutableCredentials.AccessKey, immutableCredentials.SecretKey, string.IsNullOrEmpty(token) ? null : token); + } + } +} diff --git a/src/MongoDB.Driver.Core/Core/Authentication/External/AzureAuthenticationCredentialsProvider.cs b/src/MongoDB.Driver.Core/Core/Authentication/External/AzureAuthenticationCredentialsProvider.cs new file mode 100644 index 00000000000..b46697e6572 --- /dev/null +++ b/src/MongoDB.Driver.Core/Core/Authentication/External/AzureAuthenticationCredentialsProvider.cs @@ -0,0 +1,111 @@ +/* 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.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using MongoDB.Bson; +using MongoDB.Driver.Core.Misc; + +namespace MongoDB.Driver.Core.Authentication.External +{ + internal sealed class AzureCredentials : IExternalCredentials + { + // credentials are considered expired when: Expiration - now < 1 mins + private static readonly TimeSpan __overlapWhereExpired = TimeSpan.FromMinutes(1); + + private readonly DateTime? _expiration; + private readonly string _accessToken; + + public AzureCredentials(string accessToken, DateTime? expiration) + { + _accessToken = Ensure.IsNotNull(accessToken, nameof(accessToken)); + _expiration = expiration; // can be null + } + + public string AccessToken => _accessToken; + public DateTime? Expiration => _expiration; + public bool ShouldBeRefreshed => _expiration.HasValue ? (_expiration.Value - DateTime.UtcNow) < __overlapWhereExpired : true; + + public BsonDocument GetKmsCredentials() => new BsonDocument("accessToken", _accessToken); + } + + internal sealed class AzureAuthenticationCredentialsProvider : IExternalAuthenticationCredentialsProvider + { + private readonly AzureHttpClientHelper _azureHttpClientHelper; + + public AzureAuthenticationCredentialsProvider(IHttpClientWrapper httpClientWrapper) => _azureHttpClientHelper = new AzureHttpClientHelper(httpClientWrapper); + + public AzureCredentials CreateCredentialsFromExternalSource(CancellationToken cancellationToken) => + CreateCredentialsFromExternalSourceAsync(cancellationToken).GetAwaiter().GetResult(); + + public async Task CreateCredentialsFromExternalSourceAsync(CancellationToken cancellationToken) + { + var startTime = DateTime.UtcNow; + var response = await _azureHttpClientHelper.GetIMDSesponseAsync(cancellationToken).ConfigureAwait(false); + return CreateAzureCredentialsFromAzureIMDSResponse(response, startTime); + } + + private AzureCredentials CreateAzureCredentialsFromAzureIMDSResponse(string azureResponse, DateTime startTime) + { + if (!BsonDocument.TryParse(azureResponse, out var parsedResponse)) + { + throw new InvalidOperationException("Azure IMDS response must be in Json format."); + } + var accessToken = parsedResponse.GetValue("access_token", null)?.AsString; + if (accessToken == null) + { + throw new InvalidOperationException("Azure IMDS response must contain access_token."); + } + var expiresIn = parsedResponse.GetValue("expires_in", null)?.AsString; + if (!int.TryParse(expiresIn, out var expiresInSeconds)) + { + var messageDetails = expiresIn?.ToString() ?? "null"; + throw new InvalidOperationException($"Azure IMDS response must contain 'expires_in' integer, but was {messageDetails}."); + } + var expirationDateTime = startTime.AddSeconds(expiresInSeconds); + + return new AzureCredentials(accessToken, expirationDateTime); + } + + // nested types + private class AzureHttpClientHelper + { + #region static + private static readonly Uri __IMDSRequestUri = new Uri( + baseUri: new Uri("http://169.254.169.254"), + relativeUri: "metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://vault.azure.net"); + #endregion + + private readonly IHttpClientWrapper _httpClientWrapper; + + public AzureHttpClientHelper(IHttpClientWrapper httpClientWrapper) => _httpClientWrapper = Ensure.IsNotNull(httpClientWrapper, nameof(httpClientWrapper)); + + public async Task GetIMDSesponseAsync(CancellationToken cancellationToken) + { + var credentialsRequest = new HttpRequestMessage + { + RequestUri = __IMDSRequestUri, + Method = HttpMethod.Get + }; + credentialsRequest.Headers.Add("Metadata", "true"); + credentialsRequest.Headers.Add("Accept", "application/json"); + + return await _httpClientWrapper.GetHttpContentAsync(credentialsRequest, "Failed to acquire IMDS access token.", cancellationToken).ConfigureAwait(false); + } + } + } +} diff --git a/src/MongoDB.Driver.Core/Core/Authentication/External/CacheableCredentialsProvider.cs b/src/MongoDB.Driver.Core/Core/Authentication/External/CacheableCredentialsProvider.cs new file mode 100644 index 00000000000..75310ac37ad --- /dev/null +++ b/src/MongoDB.Driver.Core/Core/Authentication/External/CacheableCredentialsProvider.cs @@ -0,0 +1,98 @@ +/* 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.Threading; +using System.Threading.Tasks; +using MongoDB.Driver.Core.Misc; + +namespace MongoDB.Driver.Core.Authentication.External +{ + internal interface ICredentialsCache where TCredentials : IExternalCredentials + { + void Clear(); + } + + internal sealed class CacheableCredentialsProvider : IExternalAuthenticationCredentialsProvider, ICredentialsCache + where TCredentials : IExternalCredentials + { + private TCredentials _cachedCredentials; + private readonly IExternalAuthenticationCredentialsProvider _provider; + + public CacheableCredentialsProvider(IExternalAuthenticationCredentialsProvider provider) + { + _provider = Ensure.IsNotNull(provider, nameof(provider)); + } + + public TCredentials Credentials => _cachedCredentials; + + public TCredentials CreateCredentialsFromExternalSource(CancellationToken cancellationToken = default) + { + var cachedCredentials = _cachedCredentials; + if (IsValidCache(cachedCredentials)) + { + return cachedCredentials; + } + else + { + Clear(); + try + { + cachedCredentials = _provider.CreateCredentialsFromExternalSource(cancellationToken); + if (cachedCredentials.Expiration.HasValue) // allows caching + { + _cachedCredentials = cachedCredentials; + } + return cachedCredentials; + } + catch + { + Clear(); + throw; + } + } + } + + public async Task CreateCredentialsFromExternalSourceAsync(CancellationToken cancellationToken = default) + { + var cachedCredentials = _cachedCredentials; + if (IsValidCache(cachedCredentials)) + { + return cachedCredentials; + } + else + { + Clear(); + try + { + cachedCredentials = await _provider.CreateCredentialsFromExternalSourceAsync(cancellationToken).ConfigureAwait(false); + if (cachedCredentials.Expiration.HasValue) // allows caching + { + _cachedCredentials = cachedCredentials; + } + return cachedCredentials; + } + catch + { + Clear(); + throw; + } + } + } + + // private methods + private bool IsValidCache(TCredentials credentials) => credentials != null && !credentials.ShouldBeRefreshed; + public void Clear() => _cachedCredentials = default; + } +} diff --git a/src/MongoDB.Driver.Core/Core/Authentication/External/ExternalCredentialsAuthenticators.cs b/src/MongoDB.Driver.Core/Core/Authentication/External/ExternalCredentialsAuthenticators.cs new file mode 100644 index 00000000000..ff9814a4429 --- /dev/null +++ b/src/MongoDB.Driver.Core/Core/Authentication/External/ExternalCredentialsAuthenticators.cs @@ -0,0 +1,52 @@ +/* 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 MongoDB.Driver.Core.Misc; + +namespace MongoDB.Driver.Core.Authentication.External +{ + internal sealed class ExternalCredentialsAuthenticators + { + #region static + private static Lazy __instance = new Lazy(() => new ExternalCredentialsAuthenticators(), isThreadSafe: true); + // public static + public static ExternalCredentialsAuthenticators Instance => __instance.Value; + #endregion + + private readonly IHttpClientWrapper _httpClientWrapper; + private readonly Lazy> _awsExternalAuthenticationCredentialsProvider; + private readonly Lazy> _azureExternalAuthenticationCredentialsProvider; + private readonly Lazy> _gcpExternalAuthenticationCredentialsProvider; + + internal ExternalCredentialsAuthenticators() : this(new HttpClientWrapper()) + { + } + + internal ExternalCredentialsAuthenticators(IHttpClientWrapper httpClientWrapper) + { + _httpClientWrapper = Ensure.IsNotNull(httpClientWrapper, nameof(httpClientWrapper)); + _awsExternalAuthenticationCredentialsProvider = new Lazy>(() => new AwsAuthenticationCredentialsProvider(), isThreadSafe: true); + _azureExternalAuthenticationCredentialsProvider = new Lazy>(() => new CacheableCredentialsProvider(new AzureAuthenticationCredentialsProvider(_httpClientWrapper)), isThreadSafe: true); + _gcpExternalAuthenticationCredentialsProvider = new Lazy>(() => new GcpAuthenticationCredentialsProvider(_httpClientWrapper), isThreadSafe: true); + } + + public IExternalAuthenticationCredentialsProvider Aws => _awsExternalAuthenticationCredentialsProvider.Value; + public IExternalAuthenticationCredentialsProvider Azure => _azureExternalAuthenticationCredentialsProvider.Value; + public IExternalAuthenticationCredentialsProvider Gcp => _gcpExternalAuthenticationCredentialsProvider.Value; + + internal IHttpClientWrapper HttpClientWrapper => _httpClientWrapper; + } +} diff --git a/src/MongoDB.Driver.Core/Core/Authentication/External/GcpAuthenticationCredentialsProvider.cs b/src/MongoDB.Driver.Core/Core/Authentication/External/GcpAuthenticationCredentialsProvider.cs new file mode 100644 index 00000000000..163b168f06c --- /dev/null +++ b/src/MongoDB.Driver.Core/Core/Authentication/External/GcpAuthenticationCredentialsProvider.cs @@ -0,0 +1,85 @@ +/* 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.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using MongoDB.Bson; +using MongoDB.Driver.Core.Misc; + +namespace MongoDB.Driver.Core.Authentication.External +{ + internal sealed class GcpCredentials : IExternalCredentials + { + private readonly string _accessToken; + + public GcpCredentials(string accessToken) => _accessToken = accessToken; + + public string AccessToken => _accessToken; + + public DateTime? Expiration => null; + + public bool ShouldBeRefreshed => true; + + public BsonDocument GetKmsCredentials() => new BsonDocument("accessToken", _accessToken); + } + + internal sealed class GcpAuthenticationCredentialsProvider : IExternalAuthenticationCredentialsProvider + { + private readonly GcpHttpClientHelper _gcpHttpClientHelper; + + public GcpAuthenticationCredentialsProvider(IHttpClientWrapper httpClientWrapper) => _gcpHttpClientHelper = new GcpHttpClientHelper(httpClientWrapper); + + public GcpCredentials CreateCredentialsFromExternalSource(CancellationToken cancellationToken) => + CreateCredentialsFromExternalSourceAsync(cancellationToken).GetAwaiter().GetResult(); + + public async Task CreateCredentialsFromExternalSourceAsync(CancellationToken cancellationToken) + { + var accessToken = await _gcpHttpClientHelper.GetAccessTokenAsync(cancellationToken).ConfigureAwait(false); + return new GcpCredentials(accessToken); + } + + // nested types + private class GcpHttpClientHelper + { + // private static + private static readonly string __defaultGceMetadataHost = "metadata.google.internal"; + private readonly IHttpClientWrapper _httpClientWrapper; + + public GcpHttpClientHelper(IHttpClientWrapper httpClientWrapper) => _httpClientWrapper = Ensure.IsNotNull(httpClientWrapper, nameof(httpClientWrapper)); + + public async Task GetAccessTokenAsync(CancellationToken cancellationToken) + { + var host = Environment.GetEnvironmentVariable("GCE_METADATA_HOST") ?? __defaultGceMetadataHost; + + var tokenRequest = new HttpRequestMessage + { + RequestUri = new Uri($"http://{host}/computeMetadata/v1/instance/service-accounts/default/token"), + Method = HttpMethod.Get + }; + tokenRequest.Headers.Add("Metadata-Flavor", "Google"); + + var response = await _httpClientWrapper.GetHttpContentAsync(tokenRequest, "Failed to acquire gce metadata credentials.", cancellationToken).ConfigureAwait(false); + if (string.IsNullOrEmpty(response)) + { + throw new MongoClientException($"The metadata host response is empty."); + } + var parsedResponse = BsonDocument.Parse(response); + return parsedResponse.GetValue("access_token", defaultValue: null)?.AsString ?? throw new MongoClientException($"The metadata host response {response} doesn't contain access_token."); + } + } + } +} diff --git a/src/MongoDB.Driver.Core/Core/Authentication/External/HttpClientWrapper.cs b/src/MongoDB.Driver.Core/Core/Authentication/External/HttpClientWrapper.cs new file mode 100644 index 00000000000..b826e6dc811 --- /dev/null +++ b/src/MongoDB.Driver.Core/Core/Authentication/External/HttpClientWrapper.cs @@ -0,0 +1,66 @@ +/* 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.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using MongoDB.Driver.Core.Misc; + +namespace MongoDB.Driver.Core.Authentication.External +{ + internal interface IHttpClientWrapper + { + Task GetHttpContentAsync(HttpRequestMessage request, string exceptionMessage, CancellationToken cancellationToken); + } + + internal sealed class HttpClientWrapper : IHttpClientWrapper + { + #region static + public static HttpClient CreateHttpClient() => new HttpClient() { Timeout = TimeSpan.FromSeconds(10) }; + #endregion + + private readonly HttpClient _httpClient; + + public HttpClientWrapper() : this(CreateHttpClient()) + { } + + internal HttpClientWrapper(HttpClient httpClient) + { + _httpClient = Ensure.IsNotNull(httpClient, nameof(httpClient)); + } + + public async Task GetHttpContentAsync(HttpRequestMessage request, string exceptionMessage, CancellationToken cancellationToken) + { + HttpResponseMessage response; + string content = null; + try + { + response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false); + content = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + response.EnsureSuccessStatusCode(); + return content; + } + catch (Exception ex) when (ex is OperationCanceledException or HttpRequestException) + { + if (content != null) + { + exceptionMessage = $"{exceptionMessage} Response body: {content}."; + } + throw new MongoClientException(exceptionMessage, ex); + } + } + } +} diff --git a/src/MongoDB.Driver.Core/Core/Authentication/External/IExternalAuthenticationCredentialsProvider.cs b/src/MongoDB.Driver.Core/Core/Authentication/External/IExternalAuthenticationCredentialsProvider.cs new file mode 100644 index 00000000000..cb696283fba --- /dev/null +++ b/src/MongoDB.Driver.Core/Core/Authentication/External/IExternalAuthenticationCredentialsProvider.cs @@ -0,0 +1,26 @@ +/* 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.Threading; +using System.Threading.Tasks; + +namespace MongoDB.Driver.Core.Authentication.External +{ + internal interface IExternalAuthenticationCredentialsProvider where TCredentials : IExternalCredentials + { + TCredentials CreateCredentialsFromExternalSource(CancellationToken cancellationToken = default); + Task CreateCredentialsFromExternalSourceAsync(CancellationToken cancellationToken = default); + } +} diff --git a/tests/MongoDB.Driver.Core.TestHelpers/Logging/ILogger.cs b/src/MongoDB.Driver.Core/Core/Authentication/External/IExternalCredentials.cs similarity index 65% rename from tests/MongoDB.Driver.Core.TestHelpers/Logging/ILogger.cs rename to src/MongoDB.Driver.Core/Core/Authentication/External/IExternalCredentials.cs index 6303b90f92d..7a1bb13b914 100644 --- a/tests/MongoDB.Driver.Core.TestHelpers/Logging/ILogger.cs +++ b/src/MongoDB.Driver.Core/Core/Authentication/External/IExternalCredentials.cs @@ -1,4 +1,4 @@ -/* Copyright 2021-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. @@ -13,14 +13,15 @@ * limitations under the License. */ -namespace MongoDB.Driver.Core.TestHelpers.Logging -{ - public interface ILogger - { - public void Log(LogLevel logLevel, string decoration, string format, params object[] arguments); - } +using System; +using MongoDB.Bson; - public interface ILogger : ILogger +namespace MongoDB.Driver.Core.Authentication.External +{ + internal interface IExternalCredentials { + DateTime? Expiration { get; } + bool ShouldBeRefreshed { get; } + BsonDocument GetKmsCredentials(); } } diff --git a/src/MongoDB.Driver.Core/Core/Authentication/MongoAWSAuthenticator.cs b/src/MongoDB.Driver.Core/Core/Authentication/MongoAWSAuthenticator.cs index 8441be7a5fb..5858930dc9f 100644 --- a/src/MongoDB.Driver.Core/Core/Authentication/MongoAWSAuthenticator.cs +++ b/src/MongoDB.Driver.Core/Core/Authentication/MongoAWSAuthenticator.cs @@ -16,14 +16,14 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Net.Http; using System.Security; +using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.Serialization; +using MongoDB.Driver.Core.Authentication.External; using MongoDB.Driver.Core.Connections; using MongoDB.Driver.Core.Misc; -using MongoDB.Shared; namespace MongoDB.Driver.Core.Authentication { @@ -53,6 +53,7 @@ private static MongoAWSMechanism CreateMechanism( UsernamePasswordCredential credential, IEnumerable> properties, IRandomByteGenerator randomByteGenerator, + IExternalAuthenticationCredentialsProvider externalAuthenticationCredentialsProvider, IClock clock) { if (credential.Source != "$external") @@ -60,7 +61,7 @@ private static MongoAWSMechanism CreateMechanism( throw new ArgumentException("MONGODB-AWS authentication may only use the $external source.", nameof(credential)); } - return CreateMechanism(credential.Username, credential.Password, properties, randomByteGenerator, clock); + return CreateMechanism(credential.Username, credential.Password, properties, randomByteGenerator, externalAuthenticationCredentialsProvider, clock); } private static MongoAWSMechanism CreateMechanism( @@ -68,18 +69,12 @@ private static MongoAWSMechanism CreateMechanism( SecureString password, IEnumerable> properties, IRandomByteGenerator randomByteGenerator, + IExternalAuthenticationCredentialsProvider externalAuthenticationCredentialsProvider, IClock clock) { var awsCredentials = CreateAwsCredentialsFromMongoCredentials(username, password, properties) ?? - CreateAwsCredentialsFromEnvironmentVariables() ?? - CreateAwsCredentialsFromEcsResponse() ?? - CreateAwsCredentialsFromEc2Response(); - - if (awsCredentials == null) - { - throw new InvalidOperationException("Unable to find credentials for MONGODB-AWS authentication."); - } + externalAuthenticationCredentialsProvider.CreateCredentialsFromExternalSource(); return new MongoAWSMechanism(awsCredentials, randomByteGenerator, clock); } @@ -109,60 +104,6 @@ private static AwsCredentials CreateAwsCredentialsFromMongoCredentials(string us return new AwsCredentials(accessKeyId: username, secretAccessKey: password, sessionToken); } - private static AwsCredentials CreateAwsCredentialsFromEnvironmentVariables() - { - var accessKeyId = Environment.GetEnvironmentVariable("AWS_ACCESS_KEY_ID"); - var secretAccessKey = Environment.GetEnvironmentVariable("AWS_SECRET_ACCESS_KEY"); - var sessionToken = Environment.GetEnvironmentVariable("AWS_SESSION_TOKEN"); - - if (accessKeyId == null && secretAccessKey == null && sessionToken == null) - { - return null; - } - if (secretAccessKey != null && accessKeyId == null) - { - throw new InvalidOperationException("When using MONGODB-AWS authentication if a secret access key is provided via environment variables then an access key ID must be provided also."); - } - if (accessKeyId != null && secretAccessKey == null) - { - throw new InvalidOperationException("When using MONGODB-AWS authentication if an access key ID is provided via environment variables then a secret access key must be provided also."); - } - if (sessionToken != null && (accessKeyId == null || secretAccessKey == null)) - { - throw new InvalidOperationException("When using MONGODB-AWS authentication if a session token is provided via environment variables then an access key ID and a secret access key must be provided also."); - } - - return new AwsCredentials(accessKeyId, SecureStringHelper.ToSecureString(secretAccessKey), sessionToken); - } - - private static AwsCredentials CreateAwsCredentialsFromEcsResponse() - { - var relativeUri = Environment.GetEnvironmentVariable("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"); - if (relativeUri == null) - { - return null; - } - - var response = AwsHttpClientHelper.GetECSResponseAsync(relativeUri).GetAwaiter().GetResult(); - var parsedResponse = BsonDocument.Parse(response); - var accessKeyId = parsedResponse.GetValue("AccessKeyId", null)?.AsString; - var secretAccessKey = parsedResponse.GetValue("SecretAccessKey", null)?.AsString; - var sessionToken = parsedResponse.GetValue("Token", null)?.AsString; - - return new AwsCredentials(accessKeyId, SecureStringHelper.ToSecureString(secretAccessKey), sessionToken); - } - - private static AwsCredentials CreateAwsCredentialsFromEc2Response() - { - var response = AwsHttpClientHelper.GetEC2ResponseAsync().GetAwaiter().GetResult(); - var parsedResponse = BsonDocument.Parse(response); - var accessKeyId = parsedResponse.GetValue("AccessKeyId", null)?.AsString; - var secretAccessKey = parsedResponse.GetValue("SecretAccessKey", null)?.AsString; - var sessionToken = parsedResponse.GetValue("Token", null)?.AsString; - - return new AwsCredentials(accessKeyId, SecureStringHelper.ToSecureString(secretAccessKey), sessionToken); - } - private static string ExtractSessionTokenFromMechanismProperties(IEnumerable> properties) { if (properties != null) @@ -194,18 +135,9 @@ private static void ValidateMechanismProperties(IEnumerable - /// Initializes a new instance of the class. - /// - /// The credentials. - /// The properties. - [Obsolete("Use the newest overload instead.")] - public MongoAWSAuthenticator(UsernamePasswordCredential credential, IEnumerable> properties) - : this(credential, properties, serverApi: null) - { - } + private readonly ICredentialsCache _credentialsCache; + // constructors /// /// Initializes a new instance of the class. /// @@ -216,18 +148,13 @@ public MongoAWSAuthenticator( UsernamePasswordCredential credential, IEnumerable> properties, ServerApi serverApi) - : this(credential, properties, new DefaultRandomByteGenerator(), SystemClock.Instance, serverApi) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The username. - /// The properties. - [Obsolete("Use the newest overload instead.")] - public MongoAWSAuthenticator(string username, IEnumerable> properties) - : this(username, properties, serverApi: null) + : this( + credential, + properties, + new DefaultRandomByteGenerator(), + ExternalCredentialsAuthenticators.Instance.Aws, + SystemClock.Instance, + serverApi) { } @@ -241,7 +168,13 @@ public MongoAWSAuthenticator( string username, IEnumerable> properties, ServerApi serverApi) - : this(username, properties, new DefaultRandomByteGenerator(), SystemClock.Instance, serverApi) + : this( + username, + properties, + new DefaultRandomByteGenerator(), + ExternalCredentialsAuthenticators.Instance.Aws, + SystemClock.Instance, + serverApi) { } @@ -249,20 +182,24 @@ internal MongoAWSAuthenticator( UsernamePasswordCredential credential, IEnumerable> properties, IRandomByteGenerator randomByteGenerator, + IExternalAuthenticationCredentialsProvider externalAuthenticationCredentialsProvider, IClock clock, ServerApi serverApi) - : base(CreateMechanism(credential, properties, randomByteGenerator, clock), serverApi) + : base(CreateMechanism(credential, properties, randomByteGenerator, externalAuthenticationCredentialsProvider, clock), serverApi) { + _credentialsCache = externalAuthenticationCredentialsProvider as ICredentialsCache; // can be null } internal MongoAWSAuthenticator( string username, IEnumerable> properties, IRandomByteGenerator randomByteGenerator, + IExternalAuthenticationCredentialsProvider externalAuthenticationCredentialsProvider, IClock clock, ServerApi serverApi) - : base(CreateMechanism(username, null, properties, randomByteGenerator, clock), serverApi) + : base(CreateMechanism(username, null, properties, randomByteGenerator, externalAuthenticationCredentialsProvider, clock), serverApi) { + _credentialsCache = externalAuthenticationCredentialsProvider as ICredentialsCache; // can be null } /// @@ -271,115 +208,35 @@ public override string DatabaseName get { return "$external"; } } - // nested classes - private class AwsCredentials - { - private readonly string _accessKeyId; - private readonly SecureString _secretAccessKey; - private readonly string _sessionToken; - - public AwsCredentials(string accessKeyId, SecureString secretAccessKey, string sessionToken) - { - _accessKeyId = Ensure.IsNotNull(accessKeyId, nameof(accessKeyId)); - _secretAccessKey = Ensure.IsNotNull(secretAccessKey, nameof(secretAccessKey)); - _sessionToken = sessionToken; // can be null - } - - public string AccessKeyId => _accessKeyId; - public SecureString SecretAccessKey => _secretAccessKey; - public string SessionToken => _sessionToken; - } - - private static class AwsHttpClientHelper + /// + public override void Authenticate(IConnection connection, ConnectionDescription description, CancellationToken cancellationToken) { - // private static - private static readonly Uri __ec2BaseUri = new Uri("http://169.254.169.254"); - private static readonly Uri __ecsBaseUri = new Uri("http://169.254.170.2"); - private static readonly Lazy __httpClientInstance = new Lazy(() => new HttpClient - { - Timeout = TimeSpan.FromSeconds(10) - }); - - public static async Task GetEC2ResponseAsync() - { - var tokenRequest = CreateTokenRequest(__ec2BaseUri); - var token = await GetHttpContentAsync(tokenRequest, "Failed to acquire EC2 token.").ConfigureAwait(false); - - var roleRequest = CreateRoleRequest(__ec2BaseUri, token); - var roleName = await GetHttpContentAsync(roleRequest, "Failed to acquire EC2 role name.").ConfigureAwait(false); - - var credentialsRequest = CreateCredentialsRequest(__ec2BaseUri, roleName, token); - var credentials = await GetHttpContentAsync(credentialsRequest, "Failed to acquire EC2 credentials.").ConfigureAwait(false); - - return credentials; - } - - public static async Task GetECSResponseAsync(string relativeUri) - { - var credentialsRequest = new HttpRequestMessage - { - RequestUri = new Uri(__ecsBaseUri, relativeUri), - Method = HttpMethod.Get - }; - - return await GetHttpContentAsync(credentialsRequest, "Failed to acquire ECS credentials.").ConfigureAwait(false); - } - - // private static methods - private static HttpRequestMessage CreateCredentialsRequest(Uri baseUri, string roleName, string token) + try { - var credentialsUri = new Uri(baseUri, "latest/meta-data/iam/security-credentials/"); - var credentialsRequest = new HttpRequestMessage - { - RequestUri = new Uri(credentialsUri, roleName), - Method = HttpMethod.Get - }; - credentialsRequest.Headers.Add("X-aws-ec2-metadata-token", token); - - return credentialsRequest; + base.Authenticate(connection, description, cancellationToken); } - - private static HttpRequestMessage CreateRoleRequest(Uri baseUri, string token) + catch { - var roleRequest = new HttpRequestMessage - { - RequestUri = new Uri(baseUri, "latest/meta-data/iam/security-credentials/"), - Method = HttpMethod.Get - }; - roleRequest.Headers.Add("X-aws-ec2-metadata-token", token); - - return roleRequest; + _credentialsCache?.Clear(); + throw; } + } - private static HttpRequestMessage CreateTokenRequest(Uri baseUri) + /// + public override async Task AuthenticateAsync(IConnection connection, ConnectionDescription description, CancellationToken cancellationToken) + { + try { - var tokenRequest = new HttpRequestMessage - { - RequestUri = new Uri(baseUri, "latest/api/token"), - Method = HttpMethod.Put, - }; - tokenRequest.Headers.Add("X-aws-ec2-metadata-token-ttl-seconds", "30"); - - return tokenRequest; + await base.AuthenticateAsync(connection, description, cancellationToken).ConfigureAwait(false); } - - private static async Task GetHttpContentAsync(HttpRequestMessage request, string exceptionMessage) + catch { - HttpResponseMessage response; - try - { - response = await __httpClientInstance.Value.SendAsync(request).ConfigureAwait(false); - response.EnsureSuccessStatusCode(); - } - catch (Exception ex) when (ex is OperationCanceledException || ex is MongoClientException) - { - throw new MongoClientException(exceptionMessage, ex); - } - - return await response.Content.ReadAsStringAsync().ConfigureAwait(false); + _credentialsCache?.Clear(); + throw; } } + // nested classes private class MongoAWSMechanism : ISaslMechanism { private readonly AwsCredentials _awsCredentials; diff --git a/src/MongoDB.Driver.Core/Core/Authentication/SaslAuthenticator.cs b/src/MongoDB.Driver.Core/Core/Authentication/SaslAuthenticator.cs index 75fc13c63d8..d05223480ca 100644 --- a/src/MongoDB.Driver.Core/Core/Authentication/SaslAuthenticator.cs +++ b/src/MongoDB.Driver.Core/Core/Authentication/SaslAuthenticator.cs @@ -75,7 +75,7 @@ public string Name // methods /// - public void Authenticate(IConnection connection, ConnectionDescription description, CancellationToken cancellationToken) + public virtual void Authenticate(IConnection connection, ConnectionDescription description, CancellationToken cancellationToken) { Ensure.IsNotNull(connection, nameof(connection)); Ensure.IsNotNull(description, nameof(description)); @@ -114,7 +114,7 @@ public void Authenticate(IConnection connection, ConnectionDescription descripti } /// - public async Task AuthenticateAsync(IConnection connection, ConnectionDescription description, CancellationToken cancellationToken) + public virtual async Task AuthenticateAsync(IConnection connection, ConnectionDescription description, CancellationToken cancellationToken) { Ensure.IsNotNull(connection, nameof(connection)); Ensure.IsNotNull(description, nameof(description)); diff --git a/src/MongoDB.Driver.Core/Core/Bindings/CoreSession.cs b/src/MongoDB.Driver.Core/Core/Bindings/CoreSession.cs index 1be2d69dda5..eca33510faf 100644 --- a/src/MongoDB.Driver.Core/Core/Bindings/CoreSession.cs +++ b/src/MongoDB.Driver.Core/Core/Bindings/CoreSession.cs @@ -513,6 +513,12 @@ private void EnsureStartTransactionCanBeCalled() private void EnsureTransactionsAreSupported() { + if (_cluster.Description.Type == ClusterType.LoadBalanced) + { + // LB always supports transactions + return; + } + var connectedDataBearingServers = _cluster.Description.Servers.Where(s => s.State == ServerState.Connected && s.IsDataBearing).ToList(); if (connectedDataBearingServers.Count == 0) diff --git a/src/MongoDB.Driver.Core/Core/Clusters/Cluster.cs b/src/MongoDB.Driver.Core/Core/Clusters/Cluster.cs index 4add0b293cd..188786f6b75 100644 --- a/src/MongoDB.Driver.Core/Core/Clusters/Cluster.cs +++ b/src/MongoDB.Driver.Core/Core/Clusters/Cluster.cs @@ -20,10 +20,12 @@ using System.Net; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters.ServerSelectors; using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.Events; +using MongoDB.Driver.Core.Logging; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; using MongoDB.Libmongocrypt; @@ -69,6 +71,8 @@ internal abstract class Cluster : ICluster private TaskCompletionSource _descriptionChangedTaskCompletionSource; private readonly object _descriptionLock = new object(); private readonly LatencyLimitingServerSelector _latencyLimitingServerSelector; + protected readonly EventLogger _clusterEventLogger; + protected readonly EventLogger _serverSelectionEventLogger; private Timer _rapidHeartbeatTimer; private readonly object _serverSelectionWaitQueueLock = new object(); private int _serverSelectionWaitQueueSize; @@ -78,13 +82,8 @@ internal abstract class Cluster : ICluster private readonly InterlockedInt32 _state; private readonly InterlockedInt32 _rapidHeartbeatTimerCallbackState; - private readonly Action _descriptionChangedEventHandler; - private readonly Action _selectingServerEventHandler; - private readonly Action _selectedServerEventHandler; - private readonly Action _selectingServerFailedEventHandler; - // constructors - protected Cluster(ClusterSettings settings, IClusterableServerFactory serverFactory, IEventSubscriber eventSubscriber) + protected Cluster(ClusterSettings settings, IClusterableServerFactory serverFactory, IEventSubscriber eventSubscriber, ILoggerFactory loggerFactory) { _settings = Ensure.IsNotNull(settings, nameof(settings)); Ensure.That(!_settings.LoadBalanced, "LoadBalanced mode is not supported."); @@ -100,13 +99,11 @@ protected Cluster(ClusterSettings settings, IClusterableServerFactory serverFact _rapidHeartbeatTimer = new Timer(RapidHeartbeatTimerCallback, null, Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan); - eventSubscriber.TryGetEventHandler(out _descriptionChangedEventHandler); - eventSubscriber.TryGetEventHandler(out _selectingServerEventHandler); - eventSubscriber.TryGetEventHandler(out _selectedServerEventHandler); - eventSubscriber.TryGetEventHandler(out _selectingServerFailedEventHandler); - _serverSessionPool = new CoreServerSessionPool(this); + _clusterEventLogger = loggerFactory.CreateEventLogger(eventSubscriber); + _serverSelectionEventLogger = loggerFactory.CreateEventLogger(eventSubscriber); + ClusterDescription CreateInitialDescription() { #pragma warning disable CS0618 // Type or member is obsolete @@ -169,6 +166,8 @@ protected virtual void Dispose(bool disposing) { if (_state.TryChange(State.Disposed)) { + _clusterEventLogger.Logger?.LogDebug(_clusterId, "Disposing"); + #pragma warning disable CS0618 // Type or member is obsolete var connectionModeSwitch = _description.ConnectionModeSwitch; var connectionMode = connectionModeSwitch == ConnectionModeSwitch.UseConnectionMode ? _description.ConnectionMode : default; @@ -188,6 +187,8 @@ protected virtual void Dispose(bool disposing) _rapidHeartbeatTimer.Dispose(); _cryptClient?.Dispose(); + + _clusterEventLogger.Logger?.LogDebug(_clusterId, "Disposed"); } } @@ -223,9 +224,17 @@ public virtual void Initialize() ThrowIfDisposed(); if (_state.TryChange(State.Initial, State.Open)) { + _clusterEventLogger.Logger?.LogDebug(_clusterId, "Initialized"); + if (_settings.CryptClientSettings != null) { _cryptClient = CryptClientCreator.CreateCryptClient(_settings.CryptClientSettings); + + _clusterEventLogger.Logger?.LogDebug( + StructuredLogTemplateProviders.ClusterId_Message_SharedLibraryVersion, + _clusterId, + "CryptClient created. Configured shared library version: ", + _cryptClient.CryptSharedLibraryVersion ?? "None"); } } } @@ -256,17 +265,12 @@ private void RapidHeartbeatTimerCallback(object args) protected void OnDescriptionChanged(ClusterDescription oldDescription, ClusterDescription newDescription, bool shouldClusterDescriptionChangedEventBePublished) { - if (shouldClusterDescriptionChangedEventBePublished && _descriptionChangedEventHandler != null) + if (shouldClusterDescriptionChangedEventBePublished) { - _descriptionChangedEventHandler(new ClusterDescriptionChangedEvent(oldDescription, newDescription)); + _clusterEventLogger.LogAndPublish(new ClusterDescriptionChangedEvent(oldDescription, newDescription)); } - var handler = DescriptionChanged; - if (handler != null) - { - var args = new ClusterDescriptionChangedEventArgs(oldDescription, newDescription); - handler(this, args); - } + DescriptionChanged?.Invoke(this, new ClusterDescriptionChangedEventArgs(oldDescription, newDescription)); } public IServer SelectServer(IServerSelector selector, CancellationToken cancellationToken) @@ -464,15 +468,11 @@ public void Dispose() public void HandleException(Exception exception) { - var selectingServerFailedEventHandler = _cluster._selectingServerFailedEventHandler; - if (selectingServerFailedEventHandler != null) - { - selectingServerFailedEventHandler(new ClusterSelectingServerFailedEvent( - _description, - _selector, - exception, - EventContext.OperationId)); - } + _cluster._serverSelectionEventLogger.LogAndPublish(new ClusterSelectingServerFailedEvent( + _description, + _selector, + exception, + EventContext.OperationId)); } public IServer SelectServer() @@ -485,15 +485,11 @@ public IServer SelectServer() if (!_serverSelectionWaitQueueEntered) { - var selectingServerEventHandler = _cluster._selectingServerEventHandler; - if (selectingServerEventHandler != null) - { - // this is our first time through... - selectingServerEventHandler(new ClusterSelectingServerEvent( - _description, - _selector, - EventContext.OperationId)); - } + // this is our first time through... + _cluster._serverSelectionEventLogger.LogAndPublish(new ClusterSelectingServerEvent( + _description, + _selector, + EventContext.OperationId)); } MongoIncompatibleDriverException.ThrowIfNotSupported(_description); @@ -530,7 +526,7 @@ public IServer SelectServer() { _stopwatch.Stop(); - _cluster._selectedServerEventHandler?.Invoke(new ClusterSelectedServerEvent( + _cluster._serverSelectionEventLogger.LogAndPublish(new ClusterSelectedServerEvent( _description, _selector, selectedServer.Description, diff --git a/src/MongoDB.Driver.Core/Core/Clusters/ClusterFactory.cs b/src/MongoDB.Driver.Core/Core/Clusters/ClusterFactory.cs index ed0ce03fb1a..31195e36e34 100644 --- a/src/MongoDB.Driver.Core/Core/Clusters/ClusterFactory.cs +++ b/src/MongoDB.Driver.Core/Core/Clusters/ClusterFactory.cs @@ -13,6 +13,7 @@ * limitations under the License. */ +using Microsoft.Extensions.Logging; using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.Events; using MongoDB.Driver.Core.Misc; @@ -24,15 +25,17 @@ internal class ClusterFactory : IClusterFactory { // fields private readonly IEventSubscriber _eventSubscriber; + private readonly ILoggerFactory _loggerFactory; private readonly IClusterableServerFactory _serverFactory; private readonly ClusterSettings _settings; // constructors - public ClusterFactory(ClusterSettings settings, IClusterableServerFactory serverFactory, IEventSubscriber eventSubscriber) + public ClusterFactory(ClusterSettings settings, IClusterableServerFactory serverFactory, IEventSubscriber eventSubscriber, ILoggerFactory loggerFactory) { _settings = Ensure.IsNotNull(settings, nameof(settings)); _serverFactory = Ensure.IsNotNull(serverFactory, nameof(serverFactory)); _eventSubscriber = Ensure.IsNotNull(eventSubscriber, nameof(eventSubscriber)); + _loggerFactory = loggerFactory; } // methods @@ -87,17 +90,17 @@ public ICluster CreateCluster() private MultiServerCluster CreateMultiServerCluster(ClusterSettings settings) { - return new MultiServerCluster(settings, _serverFactory, _eventSubscriber); + return new MultiServerCluster(settings, _serverFactory, _eventSubscriber, _loggerFactory); } private SingleServerCluster CreateSingleServerCluster(ClusterSettings settings) { - return new SingleServerCluster(settings, _serverFactory, _eventSubscriber); + return new SingleServerCluster(settings, _serverFactory, _eventSubscriber, _loggerFactory); } private LoadBalancedCluster CreateLoadBalancedCluster(ClusterSettings setting) { - return new LoadBalancedCluster(setting, _serverFactory, _eventSubscriber); + return new LoadBalancedCluster(setting, _serverFactory, _eventSubscriber, _loggerFactory); } } } diff --git a/src/MongoDB.Driver.Core/Core/Clusters/DnsMonitor.cs b/src/MongoDB.Driver.Core/Core/Clusters/DnsMonitor.cs index 8cb3cec4e4b..912c55548df 100644 --- a/src/MongoDB.Driver.Core/Core/Clusters/DnsMonitor.cs +++ b/src/MongoDB.Driver.Core/Core/Clusters/DnsMonitor.cs @@ -18,8 +18,10 @@ using System.Linq; using System.Net; using System.Threading; +using Microsoft.Extensions.Logging; using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.Events; +using MongoDB.Driver.Core.Logging; using MongoDB.Driver.Core.Misc; namespace MongoDB.Driver.Core.Clusters @@ -45,10 +47,15 @@ private static string EnsureLookupDomainNameIsValid(string lookupDomainName) private DnsMonitorState _state; private Exception _unhandledException; - private readonly Action _sdamInformationEventHandler; + private readonly EventLogger _eventLogger; // constructors - public DnsMonitor(IDnsMonitoringCluster cluster, IDnsResolver dnsResolver, string lookupDomainName, IEventSubscriber eventSubscriber, CancellationToken cancellationToken) + public DnsMonitor(IDnsMonitoringCluster cluster, + IDnsResolver dnsResolver, + string lookupDomainName, + IEventSubscriber eventSubscriber, + ILogger logger, + CancellationToken cancellationToken) { _cluster = Ensure.IsNotNull(cluster, nameof(cluster)); _dnsResolver = Ensure.IsNotNull(dnsResolver, nameof(dnsResolver)); @@ -57,7 +64,7 @@ public DnsMonitor(IDnsMonitoringCluster cluster, IDnsResolver dnsResolver, strin _service = "_mongodb._tcp." + _lookupDomainName; _state = DnsMonitorState.Created; - eventSubscriber?.TryGetEventHandler(out _sdamInformationEventHandler); + _eventLogger = logger.ToEventLogger(eventSubscriber); } // public properties @@ -94,12 +101,7 @@ private void ThreadStart() { _unhandledException = exception; - if (_sdamInformationEventHandler != null) - { - var message = $"Unhandled exception in DnsMonitor: {exception}."; - var sdamInformationEvent = new SdamInformationEvent(() => message); - _sdamInformationEventHandler(sdamInformationEvent); - } + _eventLogger.LogAndPublish(exception, new SdamInformationEvent("Unhandled exception in DnsMonitor: {0}.", exception)); _state = DnsMonitorState.Failed; return; @@ -144,12 +146,7 @@ private List GetValidEndPoints(List srvRecords) } else { - if (_sdamInformationEventHandler != null) - { - var message = $"Invalid host returned by DNS SRV lookup: {host}."; - var sdamInformationEvent = new SdamInformationEvent(() => message); - _sdamInformationEventHandler(sdamInformationEvent); - } + _eventLogger.LogAndPublish(new SdamInformationEvent("Invalid host returned by DNS SRV lookup: {0}.", host)); } } @@ -193,12 +190,7 @@ private void Monitor() } else { - if (_sdamInformationEventHandler != null) - { - var message = $"A DNS SRV query on \"{_service}\" returned no valid hosts."; - var sdamInformationEvent = new SdamInformationEvent(() => message); - _sdamInformationEventHandler(sdamInformationEvent); - } + _eventLogger.LogAndPublish(new SdamInformationEvent("A DNS SRV query on \"{0}\" returned no valid hosts.", _service)); } } diff --git a/src/MongoDB.Driver.Core/Core/Clusters/DnsMonitorFactory.cs b/src/MongoDB.Driver.Core/Core/Clusters/DnsMonitorFactory.cs index e22393433df..e9dfd6e2e27 100644 --- a/src/MongoDB.Driver.Core/Core/Clusters/DnsMonitorFactory.cs +++ b/src/MongoDB.Driver.Core/Core/Clusters/DnsMonitorFactory.cs @@ -14,7 +14,9 @@ */ using System.Threading; +using Microsoft.Extensions.Logging; using MongoDB.Driver.Core.Events; +using MongoDB.Driver.Core.Logging; using MongoDB.Driver.Core.Misc; namespace MongoDB.Driver.Core.Clusters @@ -22,16 +24,18 @@ namespace MongoDB.Driver.Core.Clusters internal class DnsMonitorFactory : IDnsMonitorFactory { private readonly IEventSubscriber _eventSubscriber; + private readonly ILoggerFactory _loggerFactory; - public DnsMonitorFactory(IEventSubscriber eventSubscriber) + public DnsMonitorFactory(IEventSubscriber eventSubscriber, ILoggerFactory loggerFactory) { _eventSubscriber = Ensure.IsNotNull(eventSubscriber, nameof(eventSubscriber)); + _loggerFactory = loggerFactory; } public IDnsMonitor CreateDnsMonitor(IDnsMonitoringCluster cluster, string lookupDomainName, CancellationToken cancellationToken) { var dnsResolver = DnsClientWrapper.Instance; - return new DnsMonitor(cluster, dnsResolver, lookupDomainName, _eventSubscriber, cancellationToken); + return new DnsMonitor(cluster, dnsResolver, lookupDomainName, _eventSubscriber, _loggerFactory?.CreateLogger(), cancellationToken); } } } diff --git a/src/MongoDB.Driver.Core/Core/Clusters/LoadBalancedCluster.cs b/src/MongoDB.Driver.Core/Core/Clusters/LoadBalancedCluster.cs index 7807a231bd2..9179da03c43 100644 --- a/src/MongoDB.Driver.Core/Core/Clusters/LoadBalancedCluster.cs +++ b/src/MongoDB.Driver.Core/Core/Clusters/LoadBalancedCluster.cs @@ -20,10 +20,12 @@ using System.Net; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters.ServerSelectors; using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.Events; +using MongoDB.Driver.Core.Logging; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; using MongoDB.Libmongocrypt; @@ -43,29 +45,25 @@ internal class LoadBalancedCluster : ICluster, IDnsMonitoringCluster private readonly IDnsMonitorFactory _dnsMonitorFactory; private Thread _dnsMonitorThread; private readonly CancellationTokenSource _dnsMonitorCancellationTokenSource; - private readonly IEventSubscriber _eventSubscriber; private IClusterableServer _server; private readonly IClusterableServerFactory _serverFactory; private readonly TaskCompletionSource _serverReadyTaskCompletionSource; private readonly ICoreServerSessionPool _serverSessionPool; private readonly ClusterSettings _settings; private readonly InterlockedInt32 _state; - - private readonly Action _closingEventHandler; - private readonly Action _closedEventHandler; - private readonly Action _openingEventHandler; - private readonly Action _openedEventHandler; - private readonly Action _descriptionChangedEventHandler; + private readonly EventLogger _eventLogger; public LoadBalancedCluster( ClusterSettings settings, IClusterableServerFactory serverFactory, - IEventSubscriber eventSubscriber) + IEventSubscriber eventSubscriber, + ILoggerFactory loggerFactory) : this( settings, serverFactory, eventSubscriber, - dnsMonitorFactory: new DnsMonitorFactory(new EventAggregator())) // should not trigger any events + loggerFactory, + dnsMonitorFactory: new DnsMonitorFactory(new EventAggregator(), loggerFactory)) // should not trigger any events { } @@ -73,6 +71,7 @@ public LoadBalancedCluster( ClusterSettings settings, IClusterableServerFactory serverFactory, IEventSubscriber eventSubscriber, + ILoggerFactory loggerFactory, IDnsMonitorFactory dnsMonitorFactory) { #pragma warning disable CS0618 // Type or member is obsolete @@ -110,12 +109,7 @@ public LoadBalancedCluster( #pragma warning restore CS0618 // Type or member is obsolete null); - _eventSubscriber = eventSubscriber; - eventSubscriber.TryGetEventHandler(out _closingEventHandler); - eventSubscriber.TryGetEventHandler(out _closedEventHandler); - eventSubscriber.TryGetEventHandler(out _openingEventHandler); - eventSubscriber.TryGetEventHandler(out _openedEventHandler); - eventSubscriber.TryGetEventHandler(out _descriptionChangedEventHandler); + _eventLogger = loggerFactory.CreateEventLogger(eventSubscriber); } public ClusterId ClusterId => _clusterId; @@ -149,14 +143,15 @@ private void Dispose(bool disposing) _dnsMonitorCancellationTokenSource.Cancel(); _dnsMonitorCancellationTokenSource.Dispose(); - _closingEventHandler?.Invoke(new ClusterClosingEvent(ClusterId)); + _eventLogger.LogAndPublish(new ClusterClosingEvent(ClusterId)); + var stopwatch = Stopwatch.StartNew(); if (_server != null) { _server.DescriptionChanged -= ServerDescriptionChangedHandler; _server.Dispose(); } - _closedEventHandler?.Invoke(new ClusterClosedEvent(ClusterId, stopwatch.Elapsed)); + _eventLogger.LogAndPublish(new ClusterClosedEvent(ClusterId, stopwatch.Elapsed)); } } } @@ -168,7 +163,7 @@ public void Initialize() if (_state.TryChange(State.Initial, State.Open)) { var stopwatch = Stopwatch.StartNew(); - _openingEventHandler?.Invoke(new ClusterOpeningEvent(ClusterId, Settings)); + _eventLogger.LogAndPublish(new ClusterOpeningEvent(ClusterId, Settings)); if (_settings.CryptClientSettings != null) { @@ -190,7 +185,7 @@ public void Initialize() _dnsMonitorThread = monitor.Start(); } - _openedEventHandler?.Invoke(new ClusterOpenedEvent(ClusterId, Settings, stopwatch.Elapsed)); + _eventLogger.LogAndPublish(new ClusterOpenedEvent(ClusterId, Settings, stopwatch.Elapsed)); } } @@ -263,10 +258,7 @@ private void UpdateClusterDescription(ClusterDescription newClusterDescription) void OnClusterDescriptionChanged(ClusterDescription oldDescription, ClusterDescription newDescription) { - if (_descriptionChangedEventHandler != null) - { - _descriptionChangedEventHandler(new ClusterDescriptionChangedEvent(oldDescription, newDescription)); - } + _eventLogger.LogAndPublish(new ClusterDescriptionChangedEvent(oldDescription, newDescription)); // used only in tests and legacy var handler = DescriptionChanged; diff --git a/src/MongoDB.Driver.Core/Core/Clusters/MultiServerCluster.cs b/src/MongoDB.Driver.Core/Core/Clusters/MultiServerCluster.cs index 50504382c16..f2393035b6f 100644 --- a/src/MongoDB.Driver.Core/Core/Clusters/MultiServerCluster.cs +++ b/src/MongoDB.Driver.Core/Core/Clusters/MultiServerCluster.cs @@ -19,6 +19,7 @@ using System.Linq; using System.Net; using System.Threading; +using Microsoft.Extensions.Logging; using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.Events; using MongoDB.Driver.Core.Misc; @@ -42,24 +43,14 @@ internal sealed class MultiServerCluster : Cluster, IDnsMonitoringCluster private readonly InterlockedInt32 _state; private readonly object _updateClusterDescriptionLock = new object(); - private readonly IEventSubscriber _eventSubscriber; - private readonly Action _closingEventHandler; - private readonly Action _closedEventHandler; - private readonly Action _openingEventHandler; - private readonly Action _openedEventHandler; - private readonly Action _addingServerEventHandler; - private readonly Action _addedServerEventHandler; - private readonly Action _removingServerEventHandler; - private readonly Action _removedServerEventHandler; - private readonly Action _sdamInformationEventHandler; - // constructors public MultiServerCluster( ClusterSettings settings, IClusterableServerFactory serverFactory, IEventSubscriber eventSubscriber, + ILoggerFactory loggerFactory, IDnsMonitorFactory dnsMonitorFactory = null) - : base(settings, serverFactory, eventSubscriber) + : base(settings, serverFactory, eventSubscriber, loggerFactory) { Ensure.IsGreaterThanZero(settings.EndPoints.Count, "settings.EndPoints.Count"); @@ -75,22 +66,12 @@ public MultiServerCluster( } #pragma warning restore CS0618 // Type or member is obsolete - _dnsMonitorFactory = dnsMonitorFactory ?? new DnsMonitorFactory(eventSubscriber); + _dnsMonitorFactory = dnsMonitorFactory ?? new DnsMonitorFactory(eventSubscriber, loggerFactory); _monitorServersCancellationTokenSource = new CancellationTokenSource(); _servers = new List(); _state = new InterlockedInt32(State.Initial); _replicaSetName = settings.ReplicaSetName; - _eventSubscriber = eventSubscriber; - eventSubscriber.TryGetEventHandler(out _closingEventHandler); - eventSubscriber.TryGetEventHandler(out _closedEventHandler); - eventSubscriber.TryGetEventHandler(out _openingEventHandler); - eventSubscriber.TryGetEventHandler(out _openedEventHandler); - eventSubscriber.TryGetEventHandler(out _addingServerEventHandler); - eventSubscriber.TryGetEventHandler(out _addedServerEventHandler); - eventSubscriber.TryGetEventHandler(out _removingServerEventHandler); - eventSubscriber.TryGetEventHandler(out _removedServerEventHandler); - eventSubscriber.TryGetEventHandler(out _sdamInformationEventHandler); } // methods @@ -101,10 +82,7 @@ protected override void Dispose(bool disposing) { if (disposing) { - if (_closingEventHandler != null) - { - _closingEventHandler(new ClusterClosingEvent(ClusterId)); - } + _clusterEventLogger.LogAndPublish(new ClusterClosingEvent(ClusterId)); stopwatch = Stopwatch.StartNew(); _monitorServersCancellationTokenSource.Cancel(); @@ -123,9 +101,9 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); - if (stopwatch != null && _closedEventHandler != null) + if (stopwatch != null) { - _closedEventHandler(new ClusterClosedEvent(ClusterId, stopwatch.Elapsed)); + _clusterEventLogger.LogAndPublish(new ClusterClosedEvent(ClusterId, stopwatch.Elapsed)); } } @@ -134,10 +112,7 @@ public override void Initialize() base.Initialize(); if (_state.TryChange(State.Initial, State.Open)) { - if (_openingEventHandler != null) - { - _openingEventHandler(new ClusterOpeningEvent(ClusterId, Settings)); - } + _clusterEventLogger.LogAndPublish(new ClusterOpeningEvent(ClusterId, Settings)); var stopwatch = Stopwatch.StartNew(); @@ -170,10 +145,7 @@ public override void Initialize() server.Initialize(); } - if (_openedEventHandler != null) - { - _openedEventHandler(new ClusterOpenedEvent(ClusterId, Settings, stopwatch.Elapsed)); - } + _clusterEventLogger.LogAndPublish(new ClusterOpenedEvent(ClusterId, Settings, stopwatch.Elapsed)); if (Settings.Scheme == ConnectionStringScheme.MongoDBPlusSrv) { @@ -264,23 +236,12 @@ private void ServerDescriptionChangedHandler(object sender, ServerDescriptionCha catch (Exception unexpectedException) { // if we catch an exception here it's because of a bug in the driver - var handler = _sdamInformationEventHandler; - if (handler != null) - { - try - { - handler.Invoke(new SdamInformationEvent(() => - string.Format( - "Unexpected exception in MultiServerCluster.ServerDescriptionChangedHandler: {0}", - unexpectedException.ToString()))); - } - catch - { - // ignore any exceptions thrown by the handler (note: event handlers aren't supposed to throw exceptions) - } - } - // TODO: should we reset the cluster state in some way? (the state is undefined since an unexpected exception was thrown) + _clusterEventLogger.LogAndPublish(new SdamInformationEvent( + "Unexpected exception in MultiServerCluster.ServerDescriptionChangedHandler: {0}", + unexpectedException), + unexpectedException); } + // TODO: should we reset the cluster state in some way? (the state is undefined since an unexpected exception was thrown) } private void ProcessServerDescriptionChanged(ServerDescriptionChangedEventArgs args) @@ -401,8 +362,7 @@ private ClusterDescription ProcessReplicaSetChange(ClusterDescription clusterDes var server = _servers.SingleOrDefault(x => EndPointHelper.Equals(args.NewServerDescription.EndPoint, x.EndPoint)); server.Invalidate("ReportedPrimaryIsStale", args.NewServerDescription.TopologyVersion); - _sdamInformationEventHandler?.Invoke(new SdamInformationEvent(() => - string.Format( + _clusterEventLogger.LogAndPublish(new SdamInformationEvent( @"Invalidating server: Setting ServerType to ""Unknown"" for {0} because it " + @"claimed to be the replica set primary for replica set ""{1}"" but sent a " + @"(setVersion, electionId) tuple of ({2}, {3}) that was less than than the " + @@ -412,7 +372,7 @@ private ClusterDescription ProcessReplicaSetChange(ClusterDescription clusterDes args.NewServerDescription.ReplicaSetConfig.Version, args.NewServerDescription.ElectionId, _maxElectionInfo.SetVersion, - _maxElectionInfo.ElectionId))); + _maxElectionInfo.ElectionId)); return clusterDescription.WithServerDescription( new ServerDescription(server.ServerId, server.EndPoint, "ReportedPrimaryIsStale")); @@ -424,8 +384,7 @@ private ClusterDescription ProcessReplicaSetChange(ClusterDescription clusterDes { if (_maxElectionInfo == null) { - _sdamInformationEventHandler?.Invoke(new SdamInformationEvent(() => - string.Format( + _clusterEventLogger.LogAndPublish(new SdamInformationEvent( @"Initializing (maxSetVersion, maxElectionId): Saving tuple " + @"(setVersion, electionId) of ({0}, {1}) as (maxSetVersion, maxElectionId) for " + @"replica set ""{2}"" because replica set primary {3} sent ({0}, {1}), the first " + @@ -434,7 +393,7 @@ private ClusterDescription ProcessReplicaSetChange(ClusterDescription clusterDes args.NewServerDescription.ElectionId, args.NewServerDescription.ReplicaSetConfig.Name, args.NewServerDescription.EndPoint, - args.NewServerDescription.ReplicaSetConfig.Name))); + args.NewServerDescription.ReplicaSetConfig.Name)); _maxElectionInfo = new ElectionInfo( args.NewServerDescription.ReplicaSetConfig.Version.Value, @@ -445,8 +404,7 @@ private ClusterDescription ProcessReplicaSetChange(ClusterDescription clusterDes if (_maxElectionInfo.SetVersion < args.NewServerDescription.ReplicaSetConfig.Version.Value) { var electionId = args.NewServerDescription.ElectionId ?? _maxElectionInfo.ElectionId; - _sdamInformationEventHandler?.Invoke(new SdamInformationEvent(() => - string.Format( + _clusterEventLogger.LogAndPublish(new SdamInformationEvent( @"Updating stale setVersion: Updating the current " + @"(maxSetVersion, maxElectionId) tuple from ({0}, {1}) to ({2}, {3}) for " + @"replica set ""{4}"" because replica set primary {5} sent ({6}, {7})—a larger " + @@ -458,7 +416,7 @@ private ClusterDescription ProcessReplicaSetChange(ClusterDescription clusterDes args.NewServerDescription.ReplicaSetConfig.Name, args.NewServerDescription.EndPoint, args.NewServerDescription.ReplicaSetConfig.Version, - args.NewServerDescription.ElectionId))); + args.NewServerDescription.ElectionId)); _maxElectionInfo = new ElectionInfo( args.NewServerDescription.ReplicaSetConfig.Version.Value, @@ -466,8 +424,7 @@ private ClusterDescription ProcessReplicaSetChange(ClusterDescription clusterDes } else // current primary is stale & setVersion is not stale ⇒ the electionId must be stale { - _sdamInformationEventHandler?.Invoke(new SdamInformationEvent(() => - string.Format( + _clusterEventLogger.LogAndPublish(new SdamInformationEvent( @"Updating stale electionId: Updating the current " + @"(maxSetVersion, maxElectionId) tuple from ({0}, {1}) to ({2}, {3}) for " + @"replica set ""{4}"" because replica set primary {5} sent ({6}, {7})—" + @@ -479,7 +436,7 @@ private ClusterDescription ProcessReplicaSetChange(ClusterDescription clusterDes args.NewServerDescription.ReplicaSetConfig.Name, args.NewServerDescription.EndPoint, args.NewServerDescription.ReplicaSetConfig.Version, - args.NewServerDescription.ElectionId))); + args.NewServerDescription.ElectionId)); _maxElectionInfo = new ElectionInfo( args.NewServerDescription.ReplicaSetConfig.Version.Value, @@ -626,10 +583,7 @@ private ClusterDescription EnsureServer(ClusterDescription clusterDescription, E return clusterDescription; } - if (_addingServerEventHandler != null) - { - _addingServerEventHandler(new ClusterAddingServerEvent(ClusterId, endPoint)); - } + _clusterEventLogger.LogAndPublish(new ClusterAddingServerEvent(ClusterId, endPoint)); stopwatch.Start(); server = CreateServer(endPoint); @@ -641,10 +595,7 @@ private ClusterDescription EnsureServer(ClusterDescription clusterDescription, E clusterDescription = clusterDescription.WithServerDescription(server.Description); stopwatch.Stop(); - if (_addedServerEventHandler != null) - { - _addedServerEventHandler(new ClusterAddedServerEvent(server.ServerId, stopwatch.Elapsed)); - } + _clusterEventLogger.LogAndPublish(new ClusterAddedServerEvent(server.ServerId, stopwatch.Elapsed)); return clusterDescription; } @@ -685,10 +636,7 @@ private ClusterDescription RemoveServer(ClusterDescription clusterDescription, E return clusterDescription; } - if (_removingServerEventHandler != null) - { - _removingServerEventHandler(new ClusterRemovingServerEvent(server.ServerId, reason)); - } + _clusterEventLogger.LogAndPublish(new ClusterRemovingServerEvent(server.ServerId, reason)); stopwatch.Start(); _servers.Remove(server); @@ -698,10 +646,7 @@ private ClusterDescription RemoveServer(ClusterDescription clusterDescription, E server.Dispose(); stopwatch.Stop(); - if (_removedServerEventHandler != null) - { - _removedServerEventHandler(new ClusterRemovedServerEvent(server.ServerId, reason, stopwatch.Elapsed)); - } + _clusterEventLogger.LogAndPublish(new ClusterRemovedServerEvent(server.ServerId, reason, stopwatch.Elapsed)); return clusterDescription.WithoutServerDescription(endPoint); } diff --git a/src/MongoDB.Driver.Core/Core/Clusters/SingleServerCluster.cs b/src/MongoDB.Driver.Core/Core/Clusters/SingleServerCluster.cs index a98f4a460f3..306094c8a10 100644 --- a/src/MongoDB.Driver.Core/Core/Clusters/SingleServerCluster.cs +++ b/src/MongoDB.Driver.Core/Core/Clusters/SingleServerCluster.cs @@ -16,6 +16,7 @@ using System; using System.Diagnostics; using System.Net; +using Microsoft.Extensions.Logging; using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.Events; using MongoDB.Driver.Core.Misc; @@ -33,33 +34,15 @@ internal sealed class SingleServerCluster : Cluster private readonly InterlockedInt32 _state; private readonly string _replicaSetName; - private readonly Action _closingEventHandler; - private readonly Action _closedEventHandler; - private readonly Action _openingEventHandler; - private readonly Action _openedEventHandler; - private readonly Action _addingServerEventHandler; - private readonly Action _addedServerEventHandler; - private readonly Action _removingServerEventHandler; - private readonly Action _removedServerEventHandler; - // constructor - internal SingleServerCluster(ClusterSettings settings, IClusterableServerFactory serverFactory, IEventSubscriber eventSubscriber) - : base(settings, serverFactory, eventSubscriber) + internal SingleServerCluster(ClusterSettings settings, IClusterableServerFactory serverFactory, IEventSubscriber eventSubscriber, ILoggerFactory loggerFactory) + : base(settings, serverFactory, eventSubscriber, loggerFactory) { Ensure.That(settings.SrvMaxHosts == 0, "srvMaxHosts cannot be used with a single server cluster."); Ensure.IsEqualTo(settings.EndPoints.Count, 1, "settings.EndPoints.Count"); _replicaSetName = settings.ReplicaSetName; // can be null _state = new InterlockedInt32(State.Initial); - - eventSubscriber.TryGetEventHandler(out _closingEventHandler); - eventSubscriber.TryGetEventHandler(out _closedEventHandler); - eventSubscriber.TryGetEventHandler(out _openingEventHandler); - eventSubscriber.TryGetEventHandler(out _openedEventHandler); - eventSubscriber.TryGetEventHandler(out _addingServerEventHandler); - eventSubscriber.TryGetEventHandler(out _addedServerEventHandler); - eventSubscriber.TryGetEventHandler(out _removingServerEventHandler); - eventSubscriber.TryGetEventHandler(out _removedServerEventHandler); } // methods @@ -70,26 +53,18 @@ protected override void Dispose(bool disposing) { if (disposing) { - if (_closingEventHandler != null) - { - _closingEventHandler(new ClusterClosingEvent(ClusterId)); - } + _clusterEventLogger.LogAndPublish(new ClusterClosingEvent(ClusterId)); + stopwatch = Stopwatch.StartNew(); if (_server != null) { - if (_removingServerEventHandler != null) - { - _removingServerEventHandler(new ClusterRemovingServerEvent(_server.ServerId, "Cluster is closing.")); - } + _clusterEventLogger.LogAndPublish(new ClusterRemovingServerEvent(_server.ServerId, "Removing server.")); _server.DescriptionChanged -= ServerDescriptionChanged; _server.Dispose(); - if (_removedServerEventHandler != null) - { - _removedServerEventHandler(new ClusterRemovedServerEvent(_server.ServerId, "Cluster is closing.", stopwatch.Elapsed)); - } + _clusterEventLogger.LogAndPublish(new ClusterRemovedServerEvent(_server.ServerId, "Server removed.", stopwatch.Elapsed)); } stopwatch.Stop(); } @@ -97,9 +72,9 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); - if (stopwatch != null && _closedEventHandler != null) + if (stopwatch != null) { - _closedEventHandler(new ClusterClosedEvent(ClusterId, stopwatch.Elapsed)); + _clusterEventLogger.LogAndPublish(new ClusterClosedEvent(ClusterId, stopwatch.Elapsed)); } } @@ -108,36 +83,26 @@ public override void Initialize() base.Initialize(); if (_state.TryChange(State.Initial, State.Open)) { - if (_openingEventHandler != null) - { - _openingEventHandler(new ClusterOpeningEvent(ClusterId, Settings)); - } + _clusterEventLogger.LogAndPublish(new ClusterOpeningEvent(ClusterId, Settings)); var stopwatch = Stopwatch.StartNew(); _server = CreateServer(Settings.EndPoints[0]); var newClusterDescription = Description .WithType(Settings.GetInitialClusterType()) .WithServerDescription(_server.Description); - if (_addingServerEventHandler != null) - { - _addingServerEventHandler(new ClusterAddingServerEvent(ClusterId, _server.EndPoint)); - } + + _clusterEventLogger.LogAndPublish(new ClusterAddingServerEvent(ClusterId, _server.EndPoint)); + _server.DescriptionChanged += ServerDescriptionChanged; stopwatch.Stop(); - if (_addedServerEventHandler != null) - { - _addedServerEventHandler(new ClusterAddedServerEvent(_server.ServerId, stopwatch.Elapsed)); - } + _clusterEventLogger.LogAndPublish(new ClusterAddedServerEvent(_server.ServerId, stopwatch.Elapsed)); UpdateClusterDescription(newClusterDescription); _server.Initialize(); - if (_openedEventHandler != null) - { - _openedEventHandler(new ClusterOpenedEvent(ClusterId, Settings, stopwatch.Elapsed)); - } + _clusterEventLogger.LogAndPublish(new ClusterOpenedEvent(ClusterId, Settings, stopwatch.Elapsed)); } } diff --git a/src/MongoDB.Driver.Core/Core/Compression/Snappy/README.txt b/src/MongoDB.Driver.Core/Core/Compression/Snappy/README.txt deleted file mode 100644 index 9929d65844b..00000000000 --- a/src/MongoDB.Driver.Core/Core/Compression/Snappy/README.txt +++ /dev/null @@ -1,13 +0,0 @@ -Scripts to build library from the source after cloning : https://github.com/google/snappy - -$ git submodule update --init -$ mkdir build -$ cd build && cmake ../ && make -$ cmake -DBUILD_SHARED_LIBS=ON .. -$ cmake --build . --target snappy - -Note related to issues during building at the current time: -- The latest release (1.1.9) cannot be built on macOS and linux due some building bugs (see the CSHARP-2813 PR and CSHARP-3819 for details), -so use the previous stable 1.1.8 release instead. -- Windows version can be built with 1.1.9 version, but it requires additional investigation how to configure options like x32 processor architecture. - diff --git a/src/MongoDB.Driver.Core/Core/Compression/Snappy/Snappy32NativeMethods.cs b/src/MongoDB.Driver.Core/Core/Compression/Snappy/Snappy32NativeMethods.cs deleted file mode 100644 index ba8a6cb2910..00000000000 --- a/src/MongoDB.Driver.Core/Core/Compression/Snappy/Snappy32NativeMethods.cs +++ /dev/null @@ -1,120 +0,0 @@ -/* Original work: - * Copyright (c) 2016 - David Rouyer rouyer.david@gmail.com Copyright (c) 2011 - 2014 Robert Važan, Google Inc. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * - * * Neither the name of Robert Važan nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Modified work: - * Copyright 2020–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.Runtime.InteropServices; -using MongoDB.Driver.Core.Misc; -using MongoDB.Driver.Core.NativeLibraryLoader; - -namespace MongoDB.Driver.Core.Compression.Snappy -{ - internal static class Snappy32NativeMethods - { - // private static fields - private static readonly Lazy __libraryLoader; - private static readonly Lazy __snappy_compress; - private static readonly Lazy __snappy_max_compressed_length; - private static readonly Lazy __snappy_uncompress; - private static readonly Lazy __snappy_uncompressed_length; - private static readonly Lazy __snappy_validate_compressed_buffer; - - // static constructor - static Snappy32NativeMethods() - { - var snappyLocator = new SnappyLocator(); - __libraryLoader = new Lazy(() => new LibraryLoader(snappyLocator), isThreadSafe: true); - - __snappy_compress = CreateLazyForDelegate(nameof(snappy_compress)); - __snappy_max_compressed_length = CreateLazyForDelegate(nameof(snappy_max_compressed_length)); - __snappy_uncompress = CreateLazyForDelegate(nameof(snappy_uncompress)); - __snappy_uncompressed_length = CreateLazyForDelegate(nameof(snappy_uncompressed_length)); - __snappy_validate_compressed_buffer = CreateLazyForDelegate(nameof(snappy_validate_compressed_buffer)); - } - - // public static methods - public static SnappyStatus snappy_compress(IntPtr input, uint input_length, IntPtr output, ref uint output_length) - { - return __snappy_compress.Value(input, input_length, output, ref output_length); - } - - public static uint snappy_max_compressed_length(uint input_length) - { - return __snappy_max_compressed_length.Value(input_length); - } - - public static SnappyStatus snappy_uncompress(IntPtr input, uint input_length, IntPtr output, ref uint output_length) - { - return __snappy_uncompress.Value(input, input_length, output, ref output_length); - } - - public static SnappyStatus snappy_uncompressed_length(IntPtr input, uint input_length, out uint output_length) - { - return __snappy_uncompressed_length.Value(input, input_length, out output_length); - } - - public static SnappyStatus snappy_validate_compressed_buffer(IntPtr input, uint input_length) - { - return __snappy_validate_compressed_buffer.Value(input, input_length); - } - - // private static methods - private static Lazy CreateLazyForDelegate(string name) - { - return new Lazy(() => __libraryLoader.Value.GetDelegate(name), isThreadSafe: true); - } - - // nested types - private class Delegates32 - { - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate SnappyStatus snappy_compress(IntPtr input, uint input_length, IntPtr output, ref uint output_length); - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate uint snappy_max_compressed_length(uint input_length); - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate SnappyStatus snappy_uncompress(IntPtr input, uint input_length, IntPtr output, ref uint output_length); - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate SnappyStatus snappy_uncompressed_length(IntPtr input, uint input_length, out uint output_length); - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate SnappyStatus snappy_validate_compressed_buffer(IntPtr input, uint input_length); - } - - private class SnappyLocator : RelativeLibraryLocatorBase - { - public override bool IsX32ModeSupported => OperatingSystemHelper.CurrentOperatingSystem == OperatingSystemPlatform.Windows; - public override string LibraryName => "Snappy"; - - public override string GetLibraryFileName(OperatingSystemPlatform currentPlatform) => - currentPlatform switch - { - OperatingSystemPlatform.Windows => "snappy32.dll", // supported only on windows - _ => throw new InvalidOperationException($"{LibraryName} is not supported on the current platform: {currentPlatform} in 32-bit mode."), - }; - } - } -} diff --git a/src/MongoDB.Driver.Core/Core/Compression/Snappy/Snappy64NativeMethods.cs b/src/MongoDB.Driver.Core/Core/Compression/Snappy/Snappy64NativeMethods.cs deleted file mode 100644 index 51b9f0b5514..00000000000 --- a/src/MongoDB.Driver.Core/Core/Compression/Snappy/Snappy64NativeMethods.cs +++ /dev/null @@ -1,116 +0,0 @@ -/* Original work: - * Copyright (c) 2016 - David Rouyer rouyer.david@gmail.com Copyright (c) 2011 - 2014 Robert Važan, Google Inc. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * - * * Neither the name of Robert Važan nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Modified work: - * Copyright 2020–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 MongoDB.Driver.Core.Misc; -using MongoDB.Driver.Core.NativeLibraryLoader; - -namespace MongoDB.Driver.Core.Compression.Snappy -{ - internal static class Snappy64NativeMethods - { - // private static fields - private static readonly Lazy __libraryLoader; - private static readonly Lazy __snappy_compress; - private static readonly Lazy __snappy_max_compressed_length; - private static readonly Lazy __snappy_uncompress; - private static readonly Lazy __snappy_uncompressed_length; - private static readonly Lazy __snappy_validate_compressed_buffer; - - // static constructor - static Snappy64NativeMethods() - { - var snappyLocator = new SnappyLocator(); - __libraryLoader = new Lazy(() => new LibraryLoader(snappyLocator), isThreadSafe: true); - - __snappy_compress = CreateLazyForDelegate(nameof(snappy_compress)); - __snappy_max_compressed_length = CreateLazyForDelegate(nameof(snappy_max_compressed_length)); - __snappy_uncompress = CreateLazyForDelegate(nameof(snappy_uncompress)); - __snappy_uncompressed_length = CreateLazyForDelegate(nameof(snappy_uncompressed_length)); - __snappy_validate_compressed_buffer = CreateLazyForDelegate(nameof(snappy_validate_compressed_buffer)); - } - - // public static methods - public static SnappyStatus snappy_compress(IntPtr input, ulong input_length, IntPtr output, ref ulong output_length) - { - return __snappy_compress.Value(input, input_length, output, ref output_length); - } - - public static ulong snappy_max_compressed_length(ulong input_length) - { - return __snappy_max_compressed_length.Value(input_length); - } - - public static SnappyStatus snappy_uncompress(IntPtr input, ulong input_length, IntPtr output, ref ulong output_length) - { - return __snappy_uncompress.Value(input, input_length, output, ref output_length); - } - - public static SnappyStatus snappy_uncompressed_length(IntPtr input, ulong input_length, out ulong output_length) - { - return __snappy_uncompressed_length.Value(input, input_length, out output_length); - } - - public static SnappyStatus snappy_validate_compressed_buffer(IntPtr input, ulong input_length) - { - return __snappy_validate_compressed_buffer.Value(input, input_length); - } - - // private static methods - private static Lazy CreateLazyForDelegate(string name) - { - return new Lazy(() => __libraryLoader.Value.GetDelegate(name), isThreadSafe: true); - } - - // nested types - private class Delegates64 - { - public delegate SnappyStatus snappy_compress(IntPtr input, ulong input_length, IntPtr output, ref ulong output_length); - public delegate ulong snappy_max_compressed_length(ulong input_length); - public delegate SnappyStatus snappy_uncompress(IntPtr input, ulong input_length, IntPtr output, ref ulong output_length); - public delegate SnappyStatus snappy_uncompressed_length(IntPtr input, ulong input_length, out ulong output_length); - public delegate SnappyStatus snappy_validate_compressed_buffer(IntPtr input, ulong input_length); - } - - private class SnappyLocator : RelativeLibraryLocatorBase - { - public override bool IsX32ModeSupported => false; - public override string LibraryName => "Snappy"; - - public override string GetLibraryFileName(OperatingSystemPlatform currentPlatform) => - currentPlatform switch - { - OperatingSystemPlatform.Windows => "snappy64.dll", - OperatingSystemPlatform.Linux => "libsnappy64.so", - OperatingSystemPlatform.MacOS => "libsnappy64.dylib", - _ => throw new InvalidOperationException($"{LibraryName} is not supported on the current platform: {currentPlatform}."), - }; - } - } -} diff --git a/src/MongoDB.Driver.Core/Core/Compression/Snappy/SnappyAdapter.cs b/src/MongoDB.Driver.Core/Core/Compression/Snappy/SnappyAdapter.cs deleted file mode 100644 index c4ae2a219c9..00000000000 --- a/src/MongoDB.Driver.Core/Core/Compression/Snappy/SnappyAdapter.cs +++ /dev/null @@ -1,145 +0,0 @@ -/* Copyright 2019–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 MongoDB.Driver.Core.NativeLibraryLoader; -using System; - -namespace MongoDB.Driver.Core.Compression.Snappy -{ - internal static class SnappyAdapter - { - public static SnappyStatus snappy_compress(byte[] input, int input_offset, int input_length, byte[] output, int output_offset, ref int output_length) - { - using (var pinnedInput = new PinnedBuffer(input, input_offset)) - using (var pinnedOutput = new PinnedBuffer(output, output_offset)) - { - return SnappyNativeMethodsAdapter.snappy_compress(pinnedInput.IntPtr, input_length, pinnedOutput.IntPtr, ref output_length); - } - } - - public static int snappy_max_compressed_length(int input_length) - { - return SnappyNativeMethodsAdapter.snappy_max_compressed_length(input_length); - } - - public static SnappyStatus snappy_uncompress(byte[] input, int input_offset, int input_length, byte[] output, int output_offset, ref int output_length) - { - using (var pinnedInput = new PinnedBuffer(input, input_offset)) - using (var pinnedOutput = new PinnedBuffer(output, output_offset)) - { - return SnappyNativeMethodsAdapter.snappy_uncompress(pinnedInput.IntPtr, input_length, pinnedOutput.IntPtr, ref output_length); - } - } - - public static SnappyStatus snappy_uncompressed_length(byte[] input, int input_offset, int input_length, out int output_length) - { - using (var pinnedInput = new PinnedBuffer(input, input_offset)) - { - return SnappyNativeMethodsAdapter.snappy_uncompressed_length(pinnedInput.IntPtr, input_length, out output_length); - } - } - - public static SnappyStatus snappy_validate_compressed_buffer(byte[] input, int input_offset, int input_length) - { - using (var pinnedInput = new PinnedBuffer(input, input_offset)) - { - return SnappyNativeMethodsAdapter.snappy_validate_compressed_buffer(pinnedInput.IntPtr, input_length); - } - } - - // nested types - private static class SnappyNativeMethodsAdapter - { - private static bool __is64BitProcess = Environment.Is64BitProcess; - - public static SnappyStatus snappy_compress(IntPtr input, int input_length, IntPtr output, ref int output_length) - { - SnappyStatus status; - if (__is64BitProcess) - { - var ulongOutput_length = (ulong)output_length; - status = Snappy64NativeMethods.snappy_compress(input, (ulong)input_length, output, ref ulongOutput_length); - output_length = (int)ulongOutput_length; - } - else - { - var uintOutput_length = (uint)output_length; - status = Snappy32NativeMethods.snappy_compress(input, (uint)input_length, output, ref uintOutput_length); - output_length = (int)uintOutput_length; - } - - return status; - } - - public static int snappy_max_compressed_length(int input_length) - { - if (__is64BitProcess) - { - return (int)Snappy64NativeMethods.snappy_max_compressed_length((ulong)input_length); - } - else - { - return (int)Snappy32NativeMethods.snappy_max_compressed_length((uint)input_length); - } - } - - public static SnappyStatus snappy_uncompress(IntPtr input, int input_length, IntPtr output, ref int output_length) - { - SnappyStatus status; - if (__is64BitProcess) - { - var ulongOutput_length = (ulong)output_length; - status = Snappy64NativeMethods.snappy_uncompress(input, (ulong)input_length, output, ref ulongOutput_length); - output_length = (int)ulongOutput_length; - } - else - { - var uintOutput_length = (uint)output_length; - status = Snappy32NativeMethods.snappy_uncompress(input, (uint)input_length, output, ref uintOutput_length); - output_length = (int)uintOutput_length; - } - return status; - } - - public static SnappyStatus snappy_uncompressed_length(IntPtr input, int input_length, out int output_length) - { - SnappyStatus status; - if (__is64BitProcess) - { - status = Snappy64NativeMethods.snappy_uncompressed_length(input, (ulong)input_length, out var ulongOutput_length); - output_length = (int)ulongOutput_length; - } - else - { - status = Snappy32NativeMethods.snappy_uncompressed_length(input, (uint)input_length, out var uintOutput_length); - output_length = (int)uintOutput_length; - } - return status; - } - - public static SnappyStatus snappy_validate_compressed_buffer(IntPtr input, int input_length) - { - if (__is64BitProcess) - { - return Snappy64NativeMethods.snappy_validate_compressed_buffer(input, (ulong)input_length); - } - else - { - return Snappy32NativeMethods.snappy_validate_compressed_buffer(input, (uint)input_length); - } - } - } - } -} diff --git a/src/MongoDB.Driver.Core/Core/Compression/Snappy/SnappyCodec.cs b/src/MongoDB.Driver.Core/Core/Compression/Snappy/SnappyCodec.cs deleted file mode 100644 index c10d97a624c..00000000000 --- a/src/MongoDB.Driver.Core/Core/Compression/Snappy/SnappyCodec.cs +++ /dev/null @@ -1,170 +0,0 @@ -/* Original work: - * Copyright (c) 2016 - David Rouyer rouyer.david@gmail.com Copyright (c) 2011 - 2014 Robert Važan, Google Inc. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * - * * Neither the name of Robert Važan nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Modified work: - * Copyright 2020–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.IO; -using MongoDB.Driver.Core.Misc; - -namespace MongoDB.Driver.Core.Compression.Snappy -{ - internal static class SnappyCodec - { - public static int Compress(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset, int outputLength) - { - Ensure.IsNotNull(input, nameof(input)); - Ensure.IsNotNull(output, nameof(output)); - EnsureBufferRangeIsValid(inputOffset, inputLength, input.Length); - EnsureBufferRangeIsValid(outputOffset, outputLength, output.Length); - - var status = SnappyAdapter.snappy_compress(input, inputOffset, inputLength, output, outputOffset, ref outputLength); - switch (status) - { - case SnappyStatus.Ok: - return outputLength; - case SnappyStatus.BufferTooSmall: - throw new ArgumentOutOfRangeException("Output array is too small."); - default: - throw new InvalidDataException("Invalid input."); - } - } - - public static byte[] Compress(byte[] input) - { - Ensure.IsNotNull(input, nameof(input)); - - var max = GetMaxCompressedLength(input.Length); - - var output = new byte[max]; - var outputLength = Compress(input, 0, input.Length, output, 0, output.Length); - if (outputLength == max) - return output; - var truncated = new byte[outputLength]; - Array.Copy(output, truncated, outputLength); - return truncated; - } - - public static int GetMaxCompressedLength(int inputLength) - { - return SnappyAdapter.snappy_max_compressed_length(inputLength); - } - - public static int GetUncompressedLength(byte[] input, int inputOffset, int inputLength) - { - Ensure.IsNotNull(input, nameof(input)); - EnsureBufferRangeIsValid(inputOffset, inputLength, input.Length); - if (inputLength == 0) - { - throw new InvalidDataException("Compressed block cannot be empty."); - } - - var status = SnappyAdapter.snappy_uncompressed_length(input, inputOffset, inputLength, out var outputLength); - switch (status) - { - case SnappyStatus.Ok: - return outputLength; - default: - throw new InvalidDataException("Input is not a valid snappy-compressed block."); - } - } - - public static int GetUncompressedLength(byte[] input) - { - Ensure.IsNotNull(input, nameof(input)); - - return GetUncompressedLength(input, 0, input.Length); - } - - public static int Uncompress(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset, int outputLength) - { - Ensure.IsNotNull(input, nameof(input)); - Ensure.IsNotNull(output, nameof(output)); - EnsureBufferRangeIsValid(inputOffset, inputLength, input.Length); - EnsureBufferRangeIsValid(outputOffset, outputLength, output.Length); - - if (inputLength == 0) - { - throw new InvalidDataException("Compressed block cannot be empty."); - } - - var status = SnappyAdapter.snappy_uncompress(input, inputOffset, inputLength, output, outputOffset, ref outputLength); - switch (status) - { - case SnappyStatus.Ok: - return outputLength; - case SnappyStatus.BufferTooSmall: - throw new ArgumentOutOfRangeException("Output array is too small."); - default: - throw new InvalidDataException("Input is not a valid snappy-compressed block."); - } - } - - public static byte[] Uncompress(byte[] input) - { - var max = GetUncompressedLength(input); - var output = new byte[max]; - var outputLength = Uncompress(input, 0, input.Length, output, 0, output.Length); - if (outputLength == max) - { - return output; - } - - var truncated = new byte[outputLength]; - Array.Copy(output, truncated, outputLength); - return truncated; - } - - public static bool Validate(byte[] input, int inputOffset, int inputLength) - { - Ensure.IsNotNull(input, nameof(input)); - EnsureBufferRangeIsValid(inputOffset, inputLength, input.Length); - if (inputLength == 0) - { - return false; - } - - return SnappyAdapter.snappy_validate_compressed_buffer(input, inputOffset, inputLength) == SnappyStatus.Ok; - } - - public static bool Validate(byte[] input) - { - Ensure.IsNotNull(input, nameof(input)); - - return Validate(input, 0, input.Length); - } - - // private static methods - private static void EnsureBufferRangeIsValid(int offset, int length, int bufferLength) - { - if (offset < 0 || length < 0 || offset + length > bufferLength) - { - throw new ArgumentOutOfRangeException("Selected range is outside the bounds of the buffer."); - } - } - } -} diff --git a/src/MongoDB.Driver.Core/Core/Compression/Snappy/SnappyStatus.cs b/src/MongoDB.Driver.Core/Core/Compression/Snappy/SnappyStatus.cs deleted file mode 100644 index 1a83ec79b89..00000000000 --- a/src/MongoDB.Driver.Core/Core/Compression/Snappy/SnappyStatus.cs +++ /dev/null @@ -1,38 +0,0 @@ -/* Original work: - * Copyright (c) 2016 - David Rouyer rouyer.david@gmail.com Copyright (c) 2011 - 2014 Robert Važan, Google Inc. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * - * * Neither the name of Robert Važan nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Modified work: - * Copyright 2020–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. - */ - -namespace MongoDB.Driver.Core.Compression.Snappy -{ - internal enum SnappyStatus - { - Ok = 0, - InvalidInput = 1, - BufferTooSmall = 2 - } -} diff --git a/src/MongoDB.Driver.Core/Core/Compression/Snappy/lib/linux/libsnappy64.so b/src/MongoDB.Driver.Core/Core/Compression/Snappy/lib/linux/libsnappy64.so deleted file mode 100644 index 584bedfc9fd..00000000000 Binary files a/src/MongoDB.Driver.Core/Core/Compression/Snappy/lib/linux/libsnappy64.so and /dev/null differ diff --git a/src/MongoDB.Driver.Core/Core/Compression/Snappy/lib/osx/libsnappy64.dylib b/src/MongoDB.Driver.Core/Core/Compression/Snappy/lib/osx/libsnappy64.dylib deleted file mode 100644 index bbce3c21e42..00000000000 Binary files a/src/MongoDB.Driver.Core/Core/Compression/Snappy/lib/osx/libsnappy64.dylib and /dev/null differ diff --git a/src/MongoDB.Driver.Core/Core/Compression/Snappy/lib/win/snappy32.dll b/src/MongoDB.Driver.Core/Core/Compression/Snappy/lib/win/snappy32.dll deleted file mode 100644 index afc82ca424c..00000000000 Binary files a/src/MongoDB.Driver.Core/Core/Compression/Snappy/lib/win/snappy32.dll and /dev/null differ diff --git a/src/MongoDB.Driver.Core/Core/Compression/Snappy/lib/win/snappy64.dll b/src/MongoDB.Driver.Core/Core/Compression/Snappy/lib/win/snappy64.dll deleted file mode 100644 index 36cd5fe795e..00000000000 Binary files a/src/MongoDB.Driver.Core/Core/Compression/Snappy/lib/win/snappy64.dll and /dev/null differ diff --git a/src/MongoDB.Driver.Core/Core/Compression/SnappyCompressor.cs b/src/MongoDB.Driver.Core/Core/Compression/SnappyCompressor.cs index 8d358799ee3..dbb11f48e59 100644 --- a/src/MongoDB.Driver.Core/Core/Compression/SnappyCompressor.cs +++ b/src/MongoDB.Driver.Core/Core/Compression/SnappyCompressor.cs @@ -13,9 +13,9 @@ * limitations under the License. */ +using Snappier; using System.IO; using System.Threading; -using MongoDB.Driver.Core.Compression.Snappy; using MongoDB.Driver.Core.Misc; namespace MongoDB.Driver.Core.Compression @@ -35,15 +35,9 @@ public void Compress(Stream input, Stream output) var uncompressedSize = (int)(input.Length - input.Position); var uncompressedBytes = new byte[uncompressedSize]; // does not include uncompressed message headers input.ReadBytes(uncompressedBytes, offset: 0, count: uncompressedSize, CancellationToken.None); - var maxCompressedSize = SnappyCodec.GetMaxCompressedLength(uncompressedSize); + var maxCompressedSize = Snappy.GetMaxCompressedLength(uncompressedSize); var compressedBytes = new byte[maxCompressedSize]; - var compressedSize = SnappyCodec.Compress( - input: uncompressedBytes, - inputOffset: 0, - inputLength: uncompressedSize, - output: compressedBytes, - outputOffset: 0, - outputLength: compressedBytes.Length); // output.Length - outputOffset + var compressedSize = Snappy.Compress(uncompressedBytes, compressedBytes); output.Write(compressedBytes, 0, compressedSize); } @@ -57,8 +51,10 @@ public void Decompress(Stream input, Stream output) var compressedSize = (int)(input.Length - input.Position); var compressedBytes = new byte[compressedSize]; input.ReadBytes(compressedBytes, offset: 0, count: compressedSize, CancellationToken.None); - var decompressedBytes = SnappyCodec.Uncompress(compressedBytes); - output.Write(decompressedBytes, offset: 0, count: decompressedBytes.Length); + var uncompressedSize = Snappy.GetUncompressedLength(compressedBytes); + var decompressedBytes = new byte[uncompressedSize]; + var decompressedSize = Snappy.Decompress(compressedBytes, decompressedBytes); + output.Write(decompressedBytes, offset: 0, count: decompressedSize); } } } diff --git a/src/MongoDB.Driver.Core/Core/Compression/Zstandard/NativeBufferInfo.cs b/src/MongoDB.Driver.Core/Core/Compression/Zstandard/NativeBufferInfo.cs deleted file mode 100644 index 08762e656ad..00000000000 --- a/src/MongoDB.Driver.Core/Core/Compression/Zstandard/NativeBufferInfo.cs +++ /dev/null @@ -1,57 +0,0 @@ -/* Original work: -* Copyright(c) 2016-present, Facebook, Inc. All rights reserved. -* -* Redistribution and use in source and binary forms, with or without modification, -* are permitted provided that the following conditions are met: -* -* * Redistributions of source code must retain the above copyright notice, this -* list of conditions and the following disclaimer. -* -* * Redistributions in binary form must reproduce the above copyright notice, -* this list of conditions and the following disclaimer in the documentation -* and/or other materials provided with the distribution. -* -* * Neither the name Facebook nor the names of its contributors may be used to -* endorse or promote products derived from this software without specific -* prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -* -* Modified work: -* Copyright 2020–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.Runtime.InteropServices; - -namespace MongoDB.Driver.Core.Compression.Zstandard -{ - [StructLayout(LayoutKind.Sequential)] - internal class NativeBufferInfo - { - public IntPtr DataPointer = IntPtr.Zero; - public ulong Size = 0; - public ulong Position = 0; - } -} diff --git a/src/MongoDB.Driver.Core/Core/Compression/Zstandard/PinnedBufferWalker.cs b/src/MongoDB.Driver.Core/Core/Compression/Zstandard/PinnedBufferWalker.cs deleted file mode 100644 index 84e3da7c1d1..00000000000 --- a/src/MongoDB.Driver.Core/Core/Compression/Zstandard/PinnedBufferWalker.cs +++ /dev/null @@ -1,71 +0,0 @@ -/* Copyright 2020–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.Runtime.InteropServices; -using MongoDB.Driver.Core.Misc; - -namespace MongoDB.Driver.Core.Compression.Zstandard -{ - internal class PinnedBufferWalker : IDisposable - { - private readonly byte[] _bytes; - private GCHandle _handle; // not readonly to prevent a temporary copy from being created when calling Free - private IntPtr _intPtr; - private int _offset; - - public PinnedBufferWalker(byte[] bytes, int offset) - { - _bytes = Ensure.IsNotNull(bytes, nameof(bytes)); - // The array must be pinned by using a GCHandle before it is passed to UnsafeAddrOfPinnedArrayElement. - // For maximum performance, this method does not validate the array passed to it; this can result in unexpected behavior. - _handle = GCHandle.Alloc(_bytes, GCHandleType.Pinned); - _offset = offset; - - RefreshIntPtr(); - } - - public IntPtr IntPtr => _intPtr; - - public int Offset - { - get => _offset; - set - { - _offset = value; - RefreshIntPtr(); - } - } - - // public methods - public void Dispose() - { - try - { - _handle.Free(); - } - catch - { - // ignore exceptions - } - } - - // private methods - private void RefreshIntPtr() - { - _intPtr = Marshal.UnsafeAddrOfPinnedArrayElement(_bytes, _offset); - } - } -} diff --git a/src/MongoDB.Driver.Core/Core/Compression/Zstandard/Zstandard64NativeMethods.cs b/src/MongoDB.Driver.Core/Core/Compression/Zstandard/Zstandard64NativeMethods.cs deleted file mode 100644 index e7e197d90c5..00000000000 --- a/src/MongoDB.Driver.Core/Core/Compression/Zstandard/Zstandard64NativeMethods.cs +++ /dev/null @@ -1,269 +0,0 @@ -/* Original work: -* Copyright(c) 2016-present, Facebook, Inc. All rights reserved. -* -* Redistribution and use in source and binary forms, with or without modification, -* are permitted provided that the following conditions are met: -* -* * Redistributions of source code must retain the above copyright notice, this -* list of conditions and the following disclaimer. -* -* * Redistributions in binary form must reproduce the above copyright notice, -* this list of conditions and the following disclaimer in the documentation -* and/or other materials provided with the distribution. -* -* * Neither the name Facebook nor the names of its contributors may be used to -* endorse or promote products derived from this software without specific -* prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -* -* Modified work: -* Copyright 2020–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.IO; -using System.Runtime.InteropServices; -using MongoDB.Driver.Core.Misc; -using MongoDB.Driver.Core.NativeLibraryLoader; - -namespace MongoDB.Driver.Core.Compression.Zstandard -{ - internal class Zstandard64NativeMethods - { - // private static fields - private static readonly Lazy __libraryLoader; - private static readonly Lazy __ZSTD_CStreamInSize; - private static readonly Lazy __ZSTD_CStreamOutSize; - private static readonly Lazy __ZSTD_createCStream; - - private static readonly Lazy __ZSTD_DStreamInSize; - private static readonly Lazy __ZSTD_DStreamOutSize; - private static readonly Lazy __ZSTD_createDStream; - - private static readonly Lazy __ZSTD_maxCLevel; - - private static readonly Lazy __ZSTD_flushStream; - private static readonly Lazy __ZSTD_endStream; - private static readonly Lazy __ZSTD_freeCStream; - private static readonly Lazy __ZSTD_freeDStream; - - private static readonly Lazy __ZSTD_initDStream; - private static readonly Lazy __ZSTD_decompressStream; - - private static readonly Lazy __ZSTD_initCStream; - private static readonly Lazy __ZSTD_compressStream; - - private static readonly Lazy __ZSTD_isError; - private static readonly Lazy __ZSTD_getErrorName; - - - // static constructor - static Zstandard64NativeMethods() - { - var zstandardLocator = new ZstandardLocator(); - __libraryLoader = new Lazy(() => new LibraryLoader(zstandardLocator), isThreadSafe: true); - - __ZSTD_CStreamInSize = CreateLazyForDelegate(nameof(ZSTD_CStreamInSize)); - __ZSTD_CStreamOutSize = CreateLazyForDelegate(nameof(ZSTD_CStreamOutSize)); - __ZSTD_createCStream = CreateLazyForDelegate(nameof(ZSTD_createCStream)); - - __ZSTD_DStreamInSize = CreateLazyForDelegate(nameof(ZSTD_DStreamInSize)); - __ZSTD_DStreamOutSize = CreateLazyForDelegate(nameof(ZSTD_DStreamOutSize)); - __ZSTD_createDStream = CreateLazyForDelegate(nameof(ZSTD_createDStream)); - - __ZSTD_maxCLevel = CreateLazyForDelegate(nameof(ZSTD_maxCLevel)); - - __ZSTD_flushStream = CreateLazyForDelegate(nameof(ZSTD_flushStream)); - __ZSTD_endStream = CreateLazyForDelegate(nameof(ZSTD_endStream)); - __ZSTD_freeCStream = CreateLazyForDelegate(nameof(ZSTD_freeCStream)); - __ZSTD_freeDStream = CreateLazyForDelegate(nameof(ZSTD_freeDStream)); - - __ZSTD_initDStream = CreateLazyForDelegate(nameof(ZSTD_initDStream)); - __ZSTD_decompressStream = CreateLazyForDelegate(nameof(ZSTD_decompressStream)); - - __ZSTD_initCStream = CreateLazyForDelegate(nameof(ZSTD_initCStream)); - __ZSTD_compressStream = CreateLazyForDelegate(nameof(ZSTD_compressStream)); - - __ZSTD_isError = CreateLazyForDelegate(nameof(ZSTD_isError)); - __ZSTD_getErrorName = CreateLazyForDelegate(nameof(ZSTD_getErrorName)); - } - - // public static methods - public static ulong ZSTD_CStreamInSize() - { - return __ZSTD_CStreamInSize.Value(); - } - - public static ulong ZSTD_CStreamOutSize() - { - return __ZSTD_CStreamOutSize.Value(); - } - - public static IntPtr ZSTD_createCStream() - { - return __ZSTD_createCStream.Value(); - } - - public static ulong ZSTD_DStreamInSize() - { - return __ZSTD_DStreamInSize.Value(); - } - - public static ulong ZSTD_DStreamOutSize() - { - return __ZSTD_DStreamOutSize.Value(); - } - - public static IntPtr ZSTD_createDStream() - { - return __ZSTD_createDStream.Value(); - } - - public static long ZSTD_maxCLevel() - { - return __ZSTD_maxCLevel.Value(); - } - - public static ulong ZSTD_flushStream(IntPtr zcs, NativeBufferInfo outputBuffer) - { - var result = __ZSTD_flushStream.Value(zcs, outputBuffer); - ThrowIfError(result); - return result; - } - - public static ulong ZSTD_endStream(IntPtr zcs, NativeBufferInfo outputBuffer) - { - var result = __ZSTD_endStream.Value(zcs, outputBuffer); - ThrowIfError(result); - return result; - } - - public static ulong ZSTD_freeCStream(IntPtr zcs) - { - return __ZSTD_freeCStream.Value(zcs); - } - - public static ulong ZSTD_freeDStream(IntPtr zds) - { - return __ZSTD_freeDStream.Value(zds); - } - - public static ulong ZSTD_initDStream(IntPtr zds) - { - return __ZSTD_initDStream.Value(zds); - } - - public static ulong ZSTD_decompressStream(IntPtr zds, NativeBufferInfo outputBuffer, NativeBufferInfo inputBuffer) - { - var result = __ZSTD_decompressStream.Value(zds, outputBuffer, inputBuffer); - ThrowIfError(result); - return result; - } - - public static ulong ZSTD_initCStream(IntPtr zcs, int compressionLevel) - { - var result = __ZSTD_initCStream.Value(zcs, compressionLevel); - ThrowIfError(result); - return result; - } - - public static ulong ZSTD_compressStream(IntPtr zcs, NativeBufferInfo outputBuffer, NativeBufferInfo inputBuffer) - { - var result = __ZSTD_compressStream.Value(zcs, outputBuffer, inputBuffer); - ThrowIfError(result); - return result; - } - - // private static methods - private static Lazy CreateLazyForDelegate(string name) - { - return new Lazy(() => __libraryLoader.Value.GetDelegate(name), isThreadSafe: true); - } - - private static void ThrowIfError(ulong code) - { - if (Zstandard64NativeMethods.ZSTD_isError(code)) - { - var errorPtr = Zstandard64NativeMethods.ZSTD_getErrorName(code); - var errorMsg = Marshal.PtrToStringAnsi(errorPtr); - throw new IOException(errorMsg); - } - } - - private static bool ZSTD_isError(ulong code) - { - return __ZSTD_isError.Value(code); - } - - private static IntPtr ZSTD_getErrorName(ulong code) - { - return __ZSTD_getErrorName.Value(code); - } - - // nested types - private class Delegates64 - { - public delegate ulong ZSTD_CStreamInSize(); - public delegate ulong ZSTD_CStreamOutSize(); - public delegate IntPtr ZSTD_createCStream(); - - public delegate ulong ZSTD_DStreamInSize(); - public delegate ulong ZSTD_DStreamOutSize(); - public delegate IntPtr ZSTD_createDStream(); - - public delegate long ZSTD_maxCLevel(); - - public delegate ulong ZSTD_flushStream(IntPtr zcs, [MarshalAs(UnmanagedType.LPStruct)] NativeBufferInfo outputBuffer); - public delegate ulong ZSTD_endStream(IntPtr zcs, [MarshalAs(UnmanagedType.LPStruct)] NativeBufferInfo outputBuffer); - public delegate ulong ZSTD_freeCStream(IntPtr zcs); - public delegate ulong ZSTD_freeDStream(IntPtr zds); - - public delegate ulong ZSTD_initDStream(IntPtr zds); - - public delegate ulong ZSTD_decompressStream(IntPtr zds, [MarshalAs(UnmanagedType.LPStruct)] NativeBufferInfo outputBuffer, [MarshalAs(UnmanagedType.LPStruct)] NativeBufferInfo inputBuffer); - - public delegate ulong ZSTD_initCStream(IntPtr zcs, int compressionLevel); - - public delegate ulong ZSTD_compressStream(IntPtr zcs, [MarshalAs(UnmanagedType.LPStruct)] NativeBufferInfo outputBuffer, [MarshalAs(UnmanagedType.LPStruct)] NativeBufferInfo inputBuffer); - - public delegate bool ZSTD_isError(ulong code); - public delegate IntPtr ZSTD_getErrorName(ulong code); - } - - private class ZstandardLocator : RelativeLibraryLocatorBase - { - public override string LibraryName => "Zstandard"; - - public override string GetLibraryFileName(OperatingSystemPlatform currentPlatform) => - currentPlatform switch - { - OperatingSystemPlatform.Windows => "libzstd.dll", - OperatingSystemPlatform.Linux => "libzstd.so", - OperatingSystemPlatform.MacOS => "libzstd.dylib", - _ => throw new InvalidOperationException($"{LibraryName} is not supported on the current platform: {currentPlatform}."), - }; - } - } -} diff --git a/src/MongoDB.Driver.Core/Core/Compression/Zstandard/ZstandardNativeWrapper.cs b/src/MongoDB.Driver.Core/Core/Compression/Zstandard/ZstandardNativeWrapper.cs deleted file mode 100644 index 092780a8313..00000000000 --- a/src/MongoDB.Driver.Core/Core/Compression/Zstandard/ZstandardNativeWrapper.cs +++ /dev/null @@ -1,290 +0,0 @@ -/* Original work: -* Copyright(c) 2016-present, Facebook, Inc. All rights reserved. -* -* Redistribution and use in source and binary forms, with or without modification, -* are permitted provided that the following conditions are met: -* -* * Redistributions of source code must retain the above copyright notice, this -* list of conditions and the following disclaimer. -* -* * Redistributions in binary form must reproduce the above copyright notice, -* this list of conditions and the following disclaimer in the documentation -* and/or other materials provided with the distribution. -* -* * Neither the name Facebook nor the names of its contributors may be used to -* endorse or promote products derived from this software without specific -* prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -* -* Modified work: -* Copyright 2020–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.IO; -using System.IO.Compression; -using MongoDB.Driver.Core.Misc; - -namespace MongoDB.Driver.Core.Compression.Zstandard -{ - internal class ZstandardNativeWrapper : IDisposable - { - #region static - public static int MaxCompressionLevel => (int)Zstandard64NativeMethods.ZSTD_maxCLevel(); - #endregion - - private readonly int _compressionLevel; - private readonly CompressionMode _compressionMode; - private bool _operationInitialized; - private readonly ulong _recommendedZstreamInputSize; - private readonly ulong _recommendedZstreamOutputSize; - private IntPtr _zstreamPointer; - - public ZstandardNativeWrapper(CompressionMode compressionMode, int compressionLevel) - { - _compressionLevel = compressionLevel; - _compressionMode = compressionMode; - - switch (_compressionMode) - { - case CompressionMode.Compress: - // calculate recommended size for input buffer - _recommendedZstreamInputSize = Zstandard64NativeMethods.ZSTD_CStreamInSize(); - // calculate recommended size for output buffer. Guarantee to successfully flush at least one complete compressed block - _recommendedZstreamOutputSize = Zstandard64NativeMethods.ZSTD_CStreamOutSize(); - _zstreamPointer = Zstandard64NativeMethods.ZSTD_createCStream(); // create resource - break; - case CompressionMode.Decompress: - // calculate recommended size for input buffer - _recommendedZstreamInputSize = Zstandard64NativeMethods.ZSTD_DStreamInSize(); - // calculate recommended size for output buffer. Guarantee to successfully flush at least one complete block in all circumstances - _recommendedZstreamOutputSize = Zstandard64NativeMethods.ZSTD_DStreamOutSize(); - _zstreamPointer = Zstandard64NativeMethods.ZSTD_createDStream(); // create resource - break; - } - } - - public int RecommendedInputSize => (int)_recommendedZstreamInputSize; - public int RecommendedOutputSize => (int)_recommendedZstreamOutputSize; - - // public methods - public void Compress( - OperationContext operationContext, - int inputCompressedSize, - int inputUncompressedSize, - out int compressedBytesProcessed, - out int uncompressedBytesProcessed) - { - Ensure.IsNotNull(operationContext, nameof(operationContext)); - Ensure.That(_compressionMode == CompressionMode.Compress, nameof(_compressionMode)); - Ensure.IsGreaterThanOrEqualToZero(inputCompressedSize, nameof(inputCompressedSize)); - Ensure.IsGreaterThanOrEqualToZero(inputUncompressedSize, nameof(inputUncompressedSize)); - - InitializeIfNotAlreadyInitialized(); - - // compressed data - var outputNativeBuffer = CreateNativeBuffer( - operationContext.CompressedPinnedBufferWalker, // operation result - (ulong)inputCompressedSize); - - // uncompressed data - var inputNativeBuffer = CreateNativeBuffer( - operationContext.UncompressedPinnedBufferWalker, - (ulong)inputUncompressedSize); - - // compress inputNativeBuffer to outputNativeBuffer - _ = Zstandard64NativeMethods.ZSTD_compressStream( - _zstreamPointer, - outputNativeBuffer, - inputNativeBuffer); - - compressedBytesProcessed = (int)outputNativeBuffer.Position; // because start Position is always 0 - uncompressedBytesProcessed = (int)inputNativeBuffer.Position; // because start Position is always 0 - - operationContext.UncompressedPinnedBufferWalker.Offset += uncompressedBytesProcessed; - // CompressedPinnedBufferWalker.Offset is always 0 - } - - public void Decompress( - OperationContext operationContext, - int compressedOffset, - int inputCompressedSize, - int inputUncompressedSize, - out int compressedBytesProcessed, - out int uncompressedBytesProcessed) - { - Ensure.IsNotNull(operationContext, nameof(operationContext)); - Ensure.That(_compressionMode == CompressionMode.Decompress, nameof(_compressionMode)); - Ensure.IsGreaterThanOrEqualToZero(inputCompressedSize, nameof(inputCompressedSize)); - Ensure.IsGreaterThanOrEqualToZero(inputUncompressedSize, nameof(inputUncompressedSize)); - - InitializeIfNotAlreadyInitialized(); - - // apply reading progress on CompressedPinnedBufferWalker - operationContext.CompressedPinnedBufferWalker.Offset = compressedOffset; - // compressed data - var inputNativeBuffer = CreateNativeBuffer( - inputCompressedSize <= 0 ? null : operationContext.CompressedPinnedBufferWalker, - inputCompressedSize <= 0 ? 0 : (ulong)inputCompressedSize); - - // uncompressed data - var outputNativeBuffer = CreateNativeBuffer( - operationContext.UncompressedPinnedBufferWalker, // operation result - (ulong)inputUncompressedSize); - - // decompress inputNativeBuffer to outputNativeBuffer - _ = Zstandard64NativeMethods.ZSTD_decompressStream( - _zstreamPointer, - outputNativeBuffer, - inputNativeBuffer); - - uncompressedBytesProcessed = (int)outputNativeBuffer.Position; // because start Position is always 0 - compressedBytesProcessed = (int)inputNativeBuffer.Position; // because start Position is always 0 - - operationContext.UncompressedPinnedBufferWalker.Offset += uncompressedBytesProcessed; - // CompressedPinnedBufferWalker.Offset will be calculated on stream side - } - - public void Dispose() - { - switch (_compressionMode) - { - case CompressionMode.Compress: - Zstandard64NativeMethods.ZSTD_freeCStream(_zstreamPointer); - break; - case CompressionMode.Decompress: - Zstandard64NativeMethods.ZSTD_freeDStream(_zstreamPointer); - break; - } - } - - public OperationContext InitializeOperationContext( - BufferInfo compressedBufferInfo, - BufferInfo uncompressedBufferInfo = null) - { - var compressedPinnedBufferWalker = new PinnedBufferWalker(compressedBufferInfo.Bytes, compressedBufferInfo.Offset); - PinnedBufferWalker uncompressedPinnedBufferWalker = null; - if (uncompressedBufferInfo != null) - { - uncompressedPinnedBufferWalker = new PinnedBufferWalker(uncompressedBufferInfo.Bytes, uncompressedBufferInfo.Offset); - } - - return new OperationContext(uncompressedPinnedBufferWalker, compressedPinnedBufferWalker); - } - - public IEnumerable StepwiseFlush(BufferInfo compressedBufferInfo) - { - if (_compressionMode != CompressionMode.Compress) - { - throw new InvalidDataException($"{nameof(StepwiseFlush)} must be called only from Compress mode."); - } - - using (var operationContext = InitializeOperationContext(compressedBufferInfo)) - { - yield return ProcessCompressedOutput(operationContext, (zcs, buffer) => Zstandard64NativeMethods.ZSTD_flushStream(zcs, buffer)); - } - - using (var operationContext = InitializeOperationContext(compressedBufferInfo)) - { - yield return ProcessCompressedOutput(operationContext, (zcs, buffer) => Zstandard64NativeMethods.ZSTD_endStream(zcs, buffer)); - } - - int ProcessCompressedOutput(OperationContext context, Action outputAction) - { - var outputNativeBuffer = CreateNativeBuffer( - context.CompressedPinnedBufferWalker, - _recommendedZstreamOutputSize); - - outputAction(_zstreamPointer, outputNativeBuffer); - - return (int)outputNativeBuffer.Position; - } - } - - // private methods - private NativeBufferInfo CreateNativeBuffer(PinnedBufferWalker pinnedBufferWalker, ulong size) - { - var nativeBuffer = new NativeBufferInfo(); - nativeBuffer.DataPointer = pinnedBufferWalker?.IntPtr ?? IntPtr.Zero; - nativeBuffer.Size = size; - nativeBuffer.Position = 0; - return nativeBuffer; - } - - private void InitializeIfNotAlreadyInitialized() - { - if (!_operationInitialized) - { - _operationInitialized = true; - - switch (_compressionMode) - { - case CompressionMode.Compress: - Zstandard64NativeMethods.ZSTD_initCStream(_zstreamPointer, _compressionLevel); // start a new compression operation - break; - case CompressionMode.Decompress: - Zstandard64NativeMethods.ZSTD_initDStream(_zstreamPointer); // start a new decompression operation - break; - } - } - } - - // nested types - internal class OperationContext : IDisposable - { - private readonly PinnedBufferWalker _compressedPinnedBufferWalker; - private readonly PinnedBufferWalker _uncompressedPinnedBufferWalker; - - public OperationContext(PinnedBufferWalker uncompressedPinnedBufferWalker, PinnedBufferWalker compressedPinnedBufferWalker) - { - _compressedPinnedBufferWalker = Ensure.IsNotNull(compressedPinnedBufferWalker, nameof(compressedPinnedBufferWalker)); - _uncompressedPinnedBufferWalker = uncompressedPinnedBufferWalker; // can be null - } - - public PinnedBufferWalker CompressedPinnedBufferWalker => _compressedPinnedBufferWalker; // internal data - public PinnedBufferWalker UncompressedPinnedBufferWalker => _uncompressedPinnedBufferWalker; // external data - - // public methods - public void Dispose() - { - _compressedPinnedBufferWalker.Dispose(); // PinnedBufferWalker.Dispose suppresses all errors - _uncompressedPinnedBufferWalker?.Dispose(); - } - } - } - - internal class BufferInfo - { - public BufferInfo(byte[] bytes, int offset) - { - Bytes = bytes; - Offset = offset; - } - - public byte[] Bytes { get; } - public int Offset { get; } - } -} diff --git a/src/MongoDB.Driver.Core/Core/Compression/Zstandard/ZstandardStream.cs b/src/MongoDB.Driver.Core/Core/Compression/Zstandard/ZstandardStream.cs deleted file mode 100644 index f025ad81905..00000000000 --- a/src/MongoDB.Driver.Core/Core/Compression/Zstandard/ZstandardStream.cs +++ /dev/null @@ -1,369 +0,0 @@ -/* Original work: -* Copyright(c) 2016-present, Facebook, Inc. All rights reserved. -* -* Redistribution and use in source and binary forms, with or without modification, -* are permitted provided that the following conditions are met: -* -* * Redistributions of source code must retain the above copyright notice, this -* list of conditions and the following disclaimer. -* -* * Redistributions in binary form must reproduce the above copyright notice, -* this list of conditions and the following disclaimer in the documentation -* and/or other materials provided with the distribution. -* -* * Neither the name Facebook nor the names of its contributors may be used to -* endorse or promote products derived from this software without specific -* prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -* -* Modified work: -* Copyright 2020–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.Buffers; -using System.IO; -using System.IO.Compression; -using MongoDB.Driver.Core.Misc; - -namespace MongoDB.Driver.Core.Compression.Zstandard -{ - internal class ZstandardStream : Stream - { - #region static - public static int MaxCompressionLevel => ZstandardNativeWrapper.MaxCompressionLevel; // maximum compression level available - #endregion - - private const int _defaultCompressionLevel = 6; - - private readonly ArrayPool _arrayPool = ArrayPool.Shared; - private readonly Stream _compressedStream; // input for decompress and output for compress - - private readonly StreamReadHelper _streamReadHelper; - private readonly StreamWriteHelper _streamWriteHelper; - private readonly CompressionMode _compressionMode; - - private bool _disposed; - private bool _flushed; - private readonly ZstandardNativeWrapper _nativeWrapper; - - public ZstandardStream( - Stream compressedStream, - CompressionMode compressionMode, - Optional compressionLevel = default) - { - _compressedStream = Ensure.IsNotNull(compressedStream, nameof(compressedStream)); - _compressionMode = EnsureCompressionModeIsValid(compressionMode); - - _nativeWrapper = new ZstandardNativeWrapper(_compressionMode, EnsureCompressionLevelIsValid(compressionLevel)); - switch (_compressionMode) - { - case CompressionMode.Compress: - _streamWriteHelper = new StreamWriteHelper( - compressedStream: compressedStream, - compressedBuffer: _arrayPool.Rent(_nativeWrapper.RecommendedOutputSize)); - break; - case CompressionMode.Decompress: - _streamReadHelper = new StreamReadHelper( - compressedStream: compressedStream, - compressedBuffer: _arrayPool.Rent(_nativeWrapper.RecommendedInputSize)); - break; - } - } - - public override bool CanRead => _compressedStream.CanRead && _compressionMode == CompressionMode.Decompress; - - public override bool CanWrite => _compressedStream.CanWrite && _compressionMode == CompressionMode.Compress; - - public override bool CanSeek => false; - - public override long Length => throw new NotSupportedException(); - - public override long Position - { - get => throw new NotSupportedException(); - set => throw new NotSupportedException(); - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - if (!_disposed) - { - switch (_compressionMode) - { - case CompressionMode.Compress: - if (!_flushed) - { - try { Flush(); } catch { } - } - _nativeWrapper.Dispose(); - _arrayPool.Return(_streamWriteHelper.CompressedBufferInfo.Bytes); - break; - case CompressionMode.Decompress: - _nativeWrapper.Dispose(); - _arrayPool.Return(_streamReadHelper.CompressedBufferInfo.Bytes); - break; - } - - _disposed = true; - } - } - - public override void Flush() - { - foreach (var outputBufferPosition in _nativeWrapper.StepwiseFlush(_streamWriteHelper.CompressedBufferInfo)) - { - _streamWriteHelper.WriteBufferToCompressedStream(count: outputBufferPosition); - } - _compressedStream.Flush(); - _flushed = true; - } - - public override int Read(byte[] outputBytes, int outputOffset, int count) // Decompress - { - if (!CanRead) throw new InvalidDataException("Read is not accessible."); - - var uncompressedOutputBuffer = new BufferInfo(outputBytes, outputOffset); - - using (var operationContext = _nativeWrapper.InitializeOperationContext( - _streamReadHelper.CompressedBufferInfo, - uncompressedOutputBuffer)) // operation result - { - var totalUncompressedBytesProcessed = 0; // the result size - var remainingCount = count; - - while (remainingCount > 0) - { - var currentDataPosition = _streamReadHelper.ReadCompressedStreamToBufferIfAvailable( - recommendedCompressedSize: _nativeWrapper.RecommendedInputSize, - out int remainingCompressedBufferSize); - - // decompress input to output - _nativeWrapper.Decompress( - operationContext, - compressedOffset: currentDataPosition, - inputCompressedSize: remainingCompressedBufferSize, - inputUncompressedSize: remainingCount, - out int compressedBytesProcessed, - out int uncompressedBytesProcessed); - - if (!_streamReadHelper.TryPrepareDataForNextAttempt( - compressedBytesProcessed, - uncompressedBytesProcessed)) - { - break; - } - - totalUncompressedBytesProcessed += uncompressedBytesProcessed; - remainingCount -= uncompressedBytesProcessed; - } - - return totalUncompressedBytesProcessed; - } - } - - public override void Write(byte[] inputBytes, int inputOffset, int count) // Compress - { - if (!CanWrite) throw new InvalidDataException("Write is not accessible."); - - var uncompressedInputInfo = new BufferInfo(inputBytes, inputOffset); - - using (var operationContext = _nativeWrapper.InitializeOperationContext( - _streamWriteHelper.CompressedBufferInfo, // operation result - uncompressedInputInfo)) - { - var remainingCount = count; - - while (remainingCount > 0) - { - var currentAttemptSize = Math.Min(remainingCount, _nativeWrapper.RecommendedInputSize); - - // compress input to output - _nativeWrapper.Compress( - operationContext, - inputCompressedSize: _nativeWrapper.RecommendedOutputSize, - inputUncompressedSize: currentAttemptSize, - out var compressedBytesProcessed, - out var uncompressedBytesProcessed); - - _streamWriteHelper.WriteBufferToCompressedStream(count: compressedBytesProcessed); - - // calculate progress in input buffer - remainingCount -= uncompressedBytesProcessed; - } - } - } - - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotImplementedException(); - } - - public override void SetLength(long value) - { - throw new NotImplementedException(); - } - - // private methods - private int EnsureCompressionLevelIsValid(Optional compressionLevel) - { - if (compressionLevel.HasValue && _compressionMode != CompressionMode.Compress) - { - throw new ArgumentException("Compression level can be specified only in Compress mode", nameof(compressionLevel)); - } - - if (compressionLevel.HasValue && (compressionLevel.Value <= 0 || compressionLevel.Value > ZstandardNativeWrapper.MaxCompressionLevel)) - { - throw new ArgumentOutOfRangeException(nameof(compressionLevel)); - } - - return compressionLevel.WithDefault(_defaultCompressionLevel); - } - - private CompressionMode EnsureCompressionModeIsValid(CompressionMode compressionMode) - { - if (compressionMode != CompressionMode.Compress && compressionMode != CompressionMode.Decompress) - { - throw new ArgumentException($"Invalid compression mode {compressionMode}.", nameof(compressionMode)); - } - return compressionMode; - } - - // nested types - private class StreamReadHelper - { - private readonly byte[] _compressedDataBuffer; - private readonly Stream _compressedStream; - private readonly ReadingState _readingState; - - public StreamReadHelper( - Stream compressedStream, - byte[] compressedBuffer) - { - _compressedDataBuffer = Ensure.IsNotNull(compressedBuffer, nameof(compressedBuffer)); - _compressedStream = Ensure.IsNotNull(compressedStream, nameof(compressedStream)); - _readingState = new ReadingState(); - } - - public BufferInfo CompressedBufferInfo => new BufferInfo(_compressedDataBuffer, _readingState.DataPosition); - - // public methods - public int ReadCompressedStreamToBufferIfAvailable( - int recommendedCompressedSize, - out int remainingSize) - { - remainingSize = _readingState.LastReadDataSize - _readingState.DataPosition; - - if (remainingSize <= 0 && !_readingState.IsDataDepleted && !_readingState.SkipDataReading) - { - var readSize = _compressedStream.Read(_compressedDataBuffer, 0, recommendedCompressedSize); - UpdateStateAfterReading(readSize, out remainingSize); - } - - return _readingState.DataPosition; - } - - public bool TryPrepareDataForNextAttempt(int compressedBytesProcessed, int uncompressedBytesProcessed) - { - if (uncompressedBytesProcessed == 0) // 0 - when a frame is completely decoded and fully flushed - { - // the internal buffer is depleted, we're either done - if (_readingState.IsDataDepleted) - { - return false; - } - - // or we need more bytes - _readingState.SkipDataReading = false; - } - - // 1. calculate progress in compressed(input) buffer - // 2. save the data position for next Read calls - _readingState.DataPosition += compressedBytesProcessed; - return true; - } - - // private methods - private void UpdateStateAfterReading(int readSize, out int remainingSize) - { - _readingState.LastReadDataSize = readSize; - _readingState.IsDataDepleted = readSize <= 0; - _readingState.DataPosition = 0; - - // skip _compressedStream.Read until the internal buffer is depleted - // avoids a Read timeout for applications that know the exact number of bytes in the _compressedStream - _readingState.SkipDataReading = true; - remainingSize = _readingState.IsDataDepleted ? 0 : _readingState.LastReadDataSize; - } - - // nested types - private class ReadingState - { - /// - /// Saves a position between different Read calls. - /// - public int DataPosition { get; set; } - /// - /// Shows whether the last attempt to read data from the stream contained records or no. false if no. - /// - public bool IsDataDepleted { get; set; } - /// - /// The size of the last fetched data from the stream. - /// - public int LastReadDataSize { get; set; } - /// - /// Determines whether stream.Read should be skipped until the internal buffer is depleted. - /// - public bool SkipDataReading { get; set; } - } - } - - private class StreamWriteHelper - { - private readonly BufferInfo _compressedBufferInfo; - private readonly Stream _compressedStream; - - public StreamWriteHelper(Stream compressedStream, byte[] compressedBuffer) - { - _compressedStream = Ensure.IsNotNull(compressedStream, nameof(compressedStream)); - _compressedBufferInfo = new BufferInfo( - Ensure.IsNotNull(compressedBuffer, nameof(compressedBuffer)), - offset: 0); - } - - public BufferInfo CompressedBufferInfo => _compressedBufferInfo; // will be implicitly updated via calls of native methods - - public void WriteBufferToCompressedStream(int count) - { - _compressedStream.Write( - _compressedBufferInfo.Bytes, - _compressedBufferInfo.Offset, // 0 - count); - } - } - } -} diff --git a/src/MongoDB.Driver.Core/Core/Compression/Zstandard/lib/linux/libzstd.so b/src/MongoDB.Driver.Core/Core/Compression/Zstandard/lib/linux/libzstd.so deleted file mode 100644 index 8309c49b768..00000000000 Binary files a/src/MongoDB.Driver.Core/Core/Compression/Zstandard/lib/linux/libzstd.so and /dev/null differ diff --git a/src/MongoDB.Driver.Core/Core/Compression/Zstandard/lib/osx/libzstd.dylib b/src/MongoDB.Driver.Core/Core/Compression/Zstandard/lib/osx/libzstd.dylib deleted file mode 100644 index 5400d7605ce..00000000000 Binary files a/src/MongoDB.Driver.Core/Core/Compression/Zstandard/lib/osx/libzstd.dylib and /dev/null differ diff --git a/src/MongoDB.Driver.Core/Core/Compression/Zstandard/lib/win/libzstd.dll b/src/MongoDB.Driver.Core/Core/Compression/Zstandard/lib/win/libzstd.dll deleted file mode 100644 index e669123c1cd..00000000000 Binary files a/src/MongoDB.Driver.Core/Core/Compression/Zstandard/lib/win/libzstd.dll and /dev/null differ diff --git a/src/MongoDB.Driver.Core/Core/Compression/ZstandardCompressor.cs b/src/MongoDB.Driver.Core/Core/Compression/ZstandardCompressor.cs index 6fd7c0865ff..363213e401c 100644 --- a/src/MongoDB.Driver.Core/Core/Compression/ZstandardCompressor.cs +++ b/src/MongoDB.Driver.Core/Core/Compression/ZstandardCompressor.cs @@ -13,9 +13,8 @@ * limitations under the License. */ +using ZstdSharp; using System.IO; -using System.IO.Compression; -using MongoDB.Driver.Core.Compression.Zstandard; using MongoDB.Driver.Core.Misc; namespace MongoDB.Driver.Core.Compression @@ -37,7 +36,7 @@ public ZstandardCompressor(Optional compressionLevel = default) public void Compress(Stream input, Stream output) { - using (var zstandardStream = new ZstandardStream(output, CompressionMode.Compress, _compressionLevel)) + using (var zstandardStream = new CompressionStream(output, _compressionLevel)) { input.EfficientCopyTo(zstandardStream); zstandardStream.Flush(); @@ -46,7 +45,7 @@ public void Compress(Stream input, Stream output) public void Decompress(Stream input, Stream output) { - using (var zstandardStream = new ZstandardStream(input, CompressionMode.Decompress)) + using (var zstandardStream = new DecompressionStream(input)) { zstandardStream.CopyTo(output); } diff --git a/src/MongoDB.Driver.Core/Core/Configuration/ClusterBuilder.cs b/src/MongoDB.Driver.Core/Core/Configuration/ClusterBuilder.cs index 106540e1d2f..ba5af005544 100644 --- a/src/MongoDB.Driver.Core/Core/Configuration/ClusterBuilder.cs +++ b/src/MongoDB.Driver.Core/Core/Configuration/ClusterBuilder.cs @@ -23,6 +23,7 @@ using MongoDB.Driver.Core.Connections; using MongoDB.Driver.Core.Events; using MongoDB.Driver.Core.Events.Diagnostics; +using MongoDB.Driver.Core.Logging; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; @@ -41,7 +42,10 @@ public class ClusterBuilder private ClusterSettings _clusterSettings; private ConnectionPoolSettings _connectionPoolSettings; private ConnectionSettings _connectionSettings; + private LoggingSettings _loggingSettings; +#pragma warning disable CS0618 // Type or member is obsolete private SdamLoggingSettings _sdamLoggingSettings; +#pragma warning restore CS0618 // Type or member is obsolete private ServerSettings _serverSettings; private SslStreamSettings _sslStreamSettings; private Func _streamFactoryWrapper; @@ -54,7 +58,9 @@ public class ClusterBuilder public ClusterBuilder() { _clusterSettings = new ClusterSettings(); +#pragma warning disable CS0618 // Type or member is obsolete _sdamLoggingSettings = new SdamLoggingSettings(null); +#pragma warning restore CS0618 // Type or member is obsolete _serverSettings = new ServerSettings(); _connectionPoolSettings = new ConnectionPoolSettings(); _connectionSettings = new ConnectionSettings(); @@ -113,11 +119,26 @@ public ClusterBuilder ConfigureConnectionPool(Func + /// Configures the logging settings. + /// + /// The logging settings configurator delegate. + /// A reconfigured cluster builder. + [CLSCompliant(false)] + public ClusterBuilder ConfigureLoggingSettings(Func configurator) + { + Ensure.IsNotNull(configurator, nameof(configurator)); + + _loggingSettings = configurator(_loggingSettings); + return this; + } + /// /// Configures the SDAM logging settings. /// /// The SDAM logging settings configurator delegate. /// A reconfigured cluster builder. + [Obsolete("Use ConfigureLoggingSettings instead.")] public ClusterBuilder ConfigureSdamLogging(Func configurator) { _sdamLoggingSettings = configurator(_sdamLoggingSettings); @@ -218,7 +239,8 @@ private IClusterFactory CreateClusterFactory() return new ClusterFactory( _clusterSettings, serverFactory, - _eventAggregator); + _eventAggregator, + _loggingSettings?.ToInternalLoggerFactory()); } private IConnectionPoolFactory CreateConnectionPoolFactory() @@ -229,14 +251,16 @@ private IConnectionPoolFactory CreateConnectionPoolFactory() _connectionSettings, streamFactory, _eventAggregator, - _clusterSettings.ServerApi); + _clusterSettings.ServerApi, + _loggingSettings.ToInternalLoggerFactory()); var connectionPoolSettings = _connectionPoolSettings.WithInternal(isPausable: !_connectionSettings.LoadBalanced); return new ExclusiveConnectionPoolFactory( connectionPoolSettings, connectionFactory, - _eventAggregator); + _eventAggregator, + _loggingSettings.ToInternalLoggerFactory()); } private ServerFactory CreateServerFactory() @@ -258,7 +282,8 @@ private ServerFactory CreateServerFactory() connectionPoolFactory, serverMonitorFactory, _eventAggregator, - _clusterSettings.ServerApi); + _clusterSettings.ServerApi, + _loggingSettings.ToInternalLoggerFactory()); } private IServerMonitorFactory CreateServerMonitorFactory() @@ -292,13 +317,15 @@ private IServerMonitorFactory CreateServerMonitorFactory() serverMonitorConnectionSettings, serverMonitorStreamFactory, new EventAggregator(), - _clusterSettings.ServerApi); + _clusterSettings.ServerApi, + loggerFactory: null); return new ServerMonitorFactory( serverMonitorSettings, serverMonitorConnectionFactory, _eventAggregator, - _clusterSettings.ServerApi); + _clusterSettings.ServerApi, + _loggingSettings.ToInternalLoggerFactory()); } private IStreamFactory CreateTcpStreamFactory(TcpStreamSettings tcpStreamSettings) diff --git a/src/MongoDB.Driver.Core/Core/Configuration/LoggingSettings.cs b/src/MongoDB.Driver.Core/Core/Configuration/LoggingSettings.cs new file mode 100644 index 00000000000..791f9dfd524 --- /dev/null +++ b/src/MongoDB.Driver.Core/Core/Configuration/LoggingSettings.cs @@ -0,0 +1,101 @@ +/* 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 Microsoft.Extensions.Logging; + +namespace MongoDB.Driver.Core.Configuration +{ + /// + /// Represents the settings for logging. + /// + public sealed class LoggingSettings : IEquatable + { + /// + /// Gets the logger factory. + /// + [CLSCompliant(false)] + public ILoggerFactory LoggerFactory { get; } + + /// + /// Gets the maximum document size in chars + /// + public int MaxDocumentSize { get; } + + // constructors + /// + /// Initializes a new instance of the class. + /// + /// The logger factory. + /// The maximum document size in chars. + [CLSCompliant(false)] + public LoggingSettings( + ILoggerFactory loggerFactory = default, + Optional maxDocumentSize = default) + { + LoggerFactory = loggerFactory; + MaxDocumentSize = maxDocumentSize.WithDefault(MongoInternalDefaults.Logging.MaxDocumentSize); + } + + // public operators + /// + /// Determines whether two instances are equal. + /// + /// The LHS. + /// The RHS. + /// + /// true if the left hand side is equal to the right hand side; otherwise, false. + /// + public static bool operator ==(LoggingSettings lhs, LoggingSettings rhs) + { + return object.Equals(lhs, rhs); // handles lhs == null correctly + } + + /// + /// Determines whether two instances are not equal. + /// + /// The LHS. + /// The RHS. + /// + /// true if the left hand side is not equal to the right hand side; otherwise, false. + /// + public static bool operator !=(LoggingSettings lhs, LoggingSettings rhs) + { + return !(lhs == rhs); + } + + // public methods + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public bool Equals(LoggingSettings rhs) + { + return + rhs != null && + LoggerFactory == rhs.LoggerFactory && + MaxDocumentSize == rhs.MaxDocumentSize; + } + + /// + public override bool Equals(object obj) => Equals(obj as LoggingSettings); + + /// + public override int GetHashCode() => base.GetHashCode(); + } +} diff --git a/src/MongoDB.Driver.Core/Core/Configuration/SdamLoggingSettings.cs b/src/MongoDB.Driver.Core/Core/Configuration/SdamLoggingSettings.cs index 5025b8ce604..630a8fe1d27 100644 --- a/src/MongoDB.Driver.Core/Core/Configuration/SdamLoggingSettings.cs +++ b/src/MongoDB.Driver.Core/Core/Configuration/SdamLoggingSettings.cs @@ -13,11 +13,14 @@ * limitations under the License. */ +using System; + namespace MongoDB.Driver.Core.Configuration { /// /// Represents settings for SDAM logging. /// + [Obsolete("Use MongoClientSettings.LoggerFactory instead.")] public class SdamLoggingSettings { private readonly string _logFilename; diff --git a/src/MongoDB.Driver.Core/Core/ConnectionPools/ExclusiveConnectionPool.Helpers.cs b/src/MongoDB.Driver.Core/Core/ConnectionPools/ExclusiveConnectionPool.Helpers.cs index e4e65a9bf31..252b4a15583 100644 --- a/src/MongoDB.Driver.Core/Core/ConnectionPools/ExclusiveConnectionPool.Helpers.cs +++ b/src/MongoDB.Driver.Core/Core/ConnectionPools/ExclusiveConnectionPool.Helpers.cs @@ -23,6 +23,7 @@ using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.Connections; using MongoDB.Driver.Core.Events; +using MongoDB.Driver.Core.Logging; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.WireProtocol.Messages; using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; @@ -282,8 +283,7 @@ private void AcquireWaitQueueSlot() private void StartCheckingOut() { - _pool._checkingOutConnectionEventHandler? - .Invoke(new ConnectionPoolCheckingOutConnectionEvent(_pool._serverId, EventContext.OperationId)); + _pool._eventLogger.LogAndPublish(new ConnectionPoolCheckingOutConnectionEvent(_pool._serverId, EventContext.OperationId)); _pool._poolState.ThrowIfNotReady(); @@ -296,8 +296,7 @@ private IConnectionHandle EndCheckingOut(PooledConnection pooledConnection, Stop var connectionHandle = new AcquiredConnection(_pool, reference); stopwatch.Stop(); - var checkedOutConnectionEvent = new ConnectionPoolCheckedOutConnectionEvent(connectionHandle.ConnectionId, stopwatch.Elapsed, EventContext.OperationId); - _pool._checkedOutConnectionEventHandler?.Invoke(checkedOutConnectionEvent); + _pool._eventLogger.LogAndPublish(new ConnectionPoolCheckedOutConnectionEvent(connectionHandle.ConnectionId, stopwatch.Elapsed, EventContext.OperationId)); // no need to release the semaphore _poolQueueWaitResult = SemaphoreSlimSignalable.SemaphoreWaitResult.None; @@ -331,18 +330,14 @@ private Exception CreateException(Stopwatch stopwatch) => private void HandleException(Exception ex) { - var handler = _pool._checkingOutConnectionFailedEventHandler; - if (handler != null) + var reason = ex switch { - ConnectionCheckOutFailedReason reason; - switch (ex) - { - case ObjectDisposedException _: reason = ConnectionCheckOutFailedReason.PoolClosed; break; - case TimeoutException _: reason = ConnectionCheckOutFailedReason.Timeout; break; - default: reason = ConnectionCheckOutFailedReason.ConnectionError; break; - } - handler(new ConnectionPoolCheckingOutConnectionFailedEvent(_pool._serverId, ex, EventContext.OperationId, reason)); - } + ObjectDisposedException => ConnectionCheckOutFailedReason.PoolClosed, + TimeoutException => ConnectionCheckOutFailedReason.Timeout, + _ => ConnectionCheckOutFailedReason.ConnectionError + }; + + _pool._eventLogger.LogAndPublish(new ConnectionPoolCheckingOutConnectionFailedEvent(_pool._serverId, ex, EventContext.OperationId, reason)); } } @@ -659,18 +654,14 @@ internal sealed class ListConnectionHolder private readonly object _lockInUse = new object(); private readonly List _connections; private readonly List _connectionsInUse; + private readonly EventLogger _eventLogger; - private readonly Action _removingConnectionEventHandler; - private readonly Action _removedConnectionEventHandler; - - public ListConnectionHolder(IEventSubscriber eventSubscriber, SemaphoreSlimSignalable semaphoreSlimSignalable) + public ListConnectionHolder(EventLogger eventLogger, SemaphoreSlimSignalable semaphoreSlimSignalable) { _semaphoreSlimSignalable = semaphoreSlimSignalable; _connections = new List(); _connectionsInUse = new List(); - - eventSubscriber.TryGetEventHandler(out _removingConnectionEventHandler); - eventSubscriber.TryGetEventHandler(out _removedConnectionEventHandler); + _eventLogger = eventLogger; } public int Count @@ -793,20 +784,14 @@ public void Return(PooledConnection connection) public void RemoveConnection(PooledConnection connection) { - if (_removingConnectionEventHandler != null) - { - _removingConnectionEventHandler(new ConnectionPoolRemovingConnectionEvent(connection.ConnectionId, EventContext.OperationId)); - } + _eventLogger.LogAndPublish(new ConnectionPoolRemovingConnectionEvent(connection.ConnectionId, EventContext.OperationId)); var stopwatch = Stopwatch.StartNew(); UntrackInUseConnection(connection); // no op if connection is not in use connection.Dispose(); stopwatch.Stop(); - if (_removedConnectionEventHandler != null) - { - _removedConnectionEventHandler(new ConnectionPoolRemovedConnectionEvent(connection.ConnectionId, stopwatch.Elapsed, EventContext.OperationId)); - } + _eventLogger.LogAndPublish(new ConnectionPoolRemovedConnectionEvent(connection.ConnectionId, stopwatch.Elapsed, EventContext.OperationId)); } private void SignalOrReset() @@ -1013,8 +998,7 @@ private async Task CreateOpenedInternalAsync(CancellationToken private void StartCreating(CancellationToken cancellationToken) { - var addingConnectionEvent = new ConnectionPoolAddingConnectionEvent(_pool._serverId, EventContext.OperationId); - _pool._addingConnectionEventHandler?.Invoke(addingConnectionEvent); + _pool._eventLogger.LogAndPublish(new ConnectionPoolAddingConnectionEvent(_pool._serverId, EventContext.OperationId)); cancellationToken.ThrowIfCancellationRequested(); @@ -1029,8 +1013,7 @@ private void FinishCreating(ConnectionDescription description) { _stopwatch.Stop(); - var connectionAddedEvent = new ConnectionPoolAddedConnectionEvent(_connection.ConnectionId, _stopwatch.Elapsed, EventContext.OperationId); - _pool._addedConnectionEventHandler?.Invoke(connectionAddedEvent); + _pool._eventLogger.LogAndPublish(new ConnectionPoolAddedConnectionEvent(_connection.ConnectionId, _stopwatch.Elapsed, EventContext.OperationId)); // Only if reached this stage, connection should not be disposed _disposeConnection = false; diff --git a/src/MongoDB.Driver.Core/Core/ConnectionPools/ExclusiveConnectionPool.cs b/src/MongoDB.Driver.Core/Core/ConnectionPools/ExclusiveConnectionPool.cs index 22286867f85..2e947539f4e 100644 --- a/src/MongoDB.Driver.Core/Core/ConnectionPools/ExclusiveConnectionPool.cs +++ b/src/MongoDB.Driver.Core/Core/ConnectionPools/ExclusiveConnectionPool.cs @@ -21,6 +21,7 @@ using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.Connections; using MongoDB.Driver.Core.Events; +using MongoDB.Driver.Core.Logging; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; @@ -44,21 +45,7 @@ internal sealed partial class ExclusiveConnectionPool : IConnectionPool private readonly SemaphoreSlimSignalable _maxConnectingQueue; private readonly IConnectionExceptionHandler _connectionExceptionHandler; - private readonly Action _checkingOutConnectionEventHandler; - private readonly Action _checkedOutConnectionEventHandler; - private readonly Action _checkingOutConnectionFailedEventHandler; - private readonly Action _checkingInConnectionEventHandler; - private readonly Action _checkedInConnectionEventHandler; - private readonly Action _addingConnectionEventHandler; - private readonly Action _addedConnectionEventHandler; - private readonly Action _openingEventHandler; - private readonly Action _openedEventHandler; - private readonly Action _readyEventHandler; - private readonly Action _closingEventHandler; - private readonly Action _closedEventHandler; - private readonly Action _clearingEventHandler; - private readonly Action _clearedEventHandler; - private readonly Action _connectionCreatedEventHandler; + private readonly EventLogger _eventLogger; // constructors public ExclusiveConnectionPool( @@ -66,46 +53,29 @@ public ExclusiveConnectionPool( EndPoint endPoint, ConnectionPoolSettings settings, IConnectionFactory connectionFactory, - IEventSubscriber eventSubscriber, - IConnectionExceptionHandler connectionExceptionHandler) + IConnectionExceptionHandler connectionExceptionHandler, + EventLogger eventLogger) { _serverId = Ensure.IsNotNull(serverId, nameof(serverId)); _endPoint = Ensure.IsNotNull(endPoint, nameof(endPoint)); _settings = Ensure.IsNotNull(settings, nameof(settings)); _connectionFactory = Ensure.IsNotNull(connectionFactory, nameof(connectionFactory)); _connectionExceptionHandler = Ensure.IsNotNull(connectionExceptionHandler, nameof(connectionExceptionHandler)); - Ensure.IsNotNull(eventSubscriber, nameof(eventSubscriber)); + + _eventLogger = Ensure.IsNotNull(eventLogger, nameof(eventLogger)); _maintenanceHelper = new MaintenanceHelper(this, _settings.MaintenanceInterval); _poolState = new PoolState(EndPointHelper.ToString(_endPoint)); _checkOutReasonCounter = new CheckOutReasonCounter(); _maxConnectingQueue = new SemaphoreSlimSignalable(settings.MaxConnecting); - _connectionHolder = new ListConnectionHolder(eventSubscriber, _maxConnectingQueue); + _connectionHolder = new ListConnectionHolder(_eventLogger, _maxConnectingQueue); _maxConnectionsQueue = new SemaphoreSlimSignalable(settings.MaxConnections); _serviceStates = new ServiceStates(); #pragma warning disable 618 _waitQueueFreeSlots = settings.WaitQueueSize; #pragma warning restore 618 - - eventSubscriber.TryGetEventHandler(out _checkingOutConnectionEventHandler); - eventSubscriber.TryGetEventHandler(out _checkedOutConnectionEventHandler); - eventSubscriber.TryGetEventHandler(out _checkingOutConnectionFailedEventHandler); - eventSubscriber.TryGetEventHandler(out _checkingInConnectionEventHandler); - eventSubscriber.TryGetEventHandler(out _checkedInConnectionEventHandler); - eventSubscriber.TryGetEventHandler(out _addingConnectionEventHandler); - eventSubscriber.TryGetEventHandler(out _addedConnectionEventHandler); - eventSubscriber.TryGetEventHandler(out _openingEventHandler); - eventSubscriber.TryGetEventHandler(out _openedEventHandler); - eventSubscriber.TryGetEventHandler(out _closingEventHandler); - eventSubscriber.TryGetEventHandler(out _closedEventHandler); - eventSubscriber.TryGetEventHandler(out _readyEventHandler); - eventSubscriber.TryGetEventHandler(out _addingConnectionEventHandler); - eventSubscriber.TryGetEventHandler(out _addedConnectionEventHandler); - eventSubscriber.TryGetEventHandler(out _clearingEventHandler); - eventSubscriber.TryGetEventHandler(out _clearedEventHandler); - eventSubscriber.TryGetEventHandler(out _connectionCreatedEventHandler); } // properties @@ -190,7 +160,7 @@ public void Clear(bool closeInUseConnections = false) if (_poolState.TransitionState(State.Paused)) { - _clearingEventHandler?.Invoke(new ConnectionPoolClearingEvent(_serverId, _settings)); + _eventLogger.LogAndPublish(new ConnectionPoolClearingEvent(_serverId, _settings)); int? maxGenerationToReap = closeInUseConnections ? _generation : null; _generation++; @@ -199,7 +169,7 @@ public void Clear(bool closeInUseConnections = false) _maxConnectionsQueue.Signal(); _maxConnectingQueue.Signal(); - _clearedEventHandler?.Invoke(new ConnectionPoolClearedEvent(_serverId, _settings)); + _eventLogger.LogAndPublish(new ConnectionPoolClearedEvent(_serverId, _settings)); } } } @@ -214,18 +184,20 @@ public void Clear(ObjectId serviceId) // generation increment can happen outside lock, as _serviceStates is threadsafe // and currently we allow dispose to start during generation increment. - _clearingEventHandler?.Invoke(new ConnectionPoolClearingEvent(_serverId, _settings, serviceId)); + _eventLogger.LogAndPublish(new ConnectionPoolClearingEvent(_serverId, _settings, serviceId)); _serviceStates.IncrementGeneration(serviceId); - _clearedEventHandler?.Invoke(new ConnectionPoolClearedEvent(_serverId, _settings, serviceId)); + _eventLogger.LogAndPublish(new ConnectionPoolClearedEvent(_serverId, _settings, serviceId)); } private PooledConnection CreateNewConnection() { var connection = _connectionFactory.CreateConnection(_serverId, _endPoint); var pooledConnection = new PooledConnection(this, connection); - _connectionCreatedEventHandler?.Invoke(new ConnectionCreatedEvent(connection.ConnectionId, connection.Settings, EventContext.OperationId)); + + _eventLogger.LogAndPublish(new ConnectionCreatedEvent(connection.ConnectionId, connection.Settings, EventContext.OperationId)); + return pooledConnection; } @@ -235,8 +207,8 @@ public void Initialize() { if (_poolState.TransitionState(State.Paused)) { - _openingEventHandler?.Invoke(new ConnectionPoolOpeningEvent(_serverId, _settings)); - _openedEventHandler?.Invoke(new ConnectionPoolOpenedEvent(_serverId, _settings)); + _eventLogger.LogAndPublish(new ConnectionPoolOpeningEvent(_serverId, _settings), _connectionFactory.ConnectionSettings); + _eventLogger.LogAndPublish(new ConnectionPoolOpenedEvent(_serverId, _settings), _connectionFactory.ConnectionSettings); } } } @@ -250,7 +222,8 @@ public void SetReady() if (_poolState.TransitionState(targetState)) { _maxConnectionsQueue.Reset(); - _readyEventHandler?.Invoke(new ConnectionPoolReadyEvent(_serverId, _settings)); + + _eventLogger.LogAndPublish(new ConnectionPoolReadyEvent(_serverId, _settings)); _maintenanceHelper.Start(); } @@ -268,19 +241,13 @@ public void Dispose() if (dispose) { - if (_closingEventHandler != null) - { - _closingEventHandler(new ConnectionPoolClosingEvent(_serverId)); - } + _eventLogger.LogAndPublish(new ConnectionPoolClosingEvent(_serverId)); _maintenanceHelper.Dispose(); _connectionHolder.Clear(); _maxConnectionsQueue.Dispose(); _maxConnectingQueue.Dispose(); - if (_closedEventHandler != null) - { - _closedEventHandler(new ConnectionPoolClosedEvent(_serverId)); - } + _eventLogger.LogAndPublish(new ConnectionPoolClosedEvent(_serverId)); } } @@ -305,15 +272,8 @@ internal SemaphoreSlimSignalable.SemaphoreSlimSignalableAwaiter CreateMaxConnect // private methods private void ReleaseConnection(PooledConnection connection) { - if (_checkingInConnectionEventHandler != null) - { - _checkingInConnectionEventHandler(new ConnectionPoolCheckingInConnectionEvent(connection.ConnectionId, EventContext.OperationId)); - } - - if (_checkedInConnectionEventHandler != null) - { - _checkedInConnectionEventHandler(new ConnectionPoolCheckedInConnectionEvent(connection.ConnectionId, TimeSpan.Zero, EventContext.OperationId)); - } + _eventLogger.LogAndPublish(new ConnectionPoolCheckingInConnectionEvent(connection.ConnectionId, EventContext.OperationId)); + _eventLogger.LogAndPublish(new ConnectionPoolCheckedInConnectionEvent(connection.ConnectionId, TimeSpan.Zero, EventContext.OperationId)); _checkOutReasonCounter.Decrement(connection.CheckOutReason); diff --git a/src/MongoDB.Driver.Core/Core/ConnectionPools/ExclusiveConnectionPoolFactory.cs b/src/MongoDB.Driver.Core/Core/ConnectionPools/ExclusiveConnectionPoolFactory.cs index aa6641e9f4d..cd3ffdd733d 100644 --- a/src/MongoDB.Driver.Core/Core/ConnectionPools/ExclusiveConnectionPoolFactory.cs +++ b/src/MongoDB.Driver.Core/Core/ConnectionPools/ExclusiveConnectionPoolFactory.cs @@ -14,9 +14,11 @@ */ using System.Net; +using Microsoft.Extensions.Logging; using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.Connections; using MongoDB.Driver.Core.Events; +using MongoDB.Driver.Core.Logging; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; @@ -28,12 +30,14 @@ internal sealed class ExclusiveConnectionPoolFactory : IConnectionPoolFactory private readonly IConnectionFactory _connectionFactory; private readonly IEventSubscriber _eventSubscriber; private readonly ConnectionPoolSettings _settings; + private readonly ILoggerFactory _loggerFactory; - public ExclusiveConnectionPoolFactory(ConnectionPoolSettings settings, IConnectionFactory connectionFactory, IEventSubscriber eventSubscriber) + public ExclusiveConnectionPoolFactory(ConnectionPoolSettings settings, IConnectionFactory connectionFactory, IEventSubscriber eventSubscriber, ILoggerFactory loggerFactory) { _settings = Ensure.IsNotNull(settings, nameof(settings)); _connectionFactory = Ensure.IsNotNull(connectionFactory, nameof(connectionFactory)); _eventSubscriber = Ensure.IsNotNull(eventSubscriber, nameof(eventSubscriber)); + _loggerFactory = loggerFactory; } public IConnectionPool CreateConnectionPool(ServerId serverId, EndPoint endPoint, IConnectionExceptionHandler connectionExceptionHandler) @@ -41,7 +45,7 @@ public IConnectionPool CreateConnectionPool(ServerId serverId, EndPoint endPoint Ensure.IsNotNull(serverId, nameof(serverId)); Ensure.IsNotNull(endPoint, nameof(endPoint)); - return new ExclusiveConnectionPool(serverId, endPoint, _settings, _connectionFactory, _eventSubscriber, connectionExceptionHandler); + return new ExclusiveConnectionPool(serverId, endPoint, _settings, _connectionFactory, connectionExceptionHandler, _loggerFactory.CreateEventLogger(_eventSubscriber)); } } } diff --git a/src/MongoDB.Driver.Core/Core/Connections/BinaryConnection.cs b/src/MongoDB.Driver.Core/Core/Connections/BinaryConnection.cs index edc6bbc166a..57e8277272e 100644 --- a/src/MongoDB.Driver.Core/Core/Connections/BinaryConnection.cs +++ b/src/MongoDB.Driver.Core/Core/Connections/BinaryConnection.cs @@ -22,10 +22,12 @@ using System.Net; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using MongoDB.Bson.IO; using MongoDB.Driver.Core.Compression; using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.Events; +using MongoDB.Driver.Core.Logging; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; using MongoDB.Driver.Core.WireProtocol.Messages; @@ -59,22 +61,16 @@ internal class BinaryConnection : IConnection private readonly InterlockedInt32 _state; private Stream _stream; private readonly IStreamFactory _streamFactory; - - private readonly Action _failedEventHandler; - private readonly Action _closingEventHandler; - private readonly Action _closedEventHandler; - private readonly Action _openingEventHandler; - private readonly Action _openedEventHandler; - private readonly Action _failedOpeningEventHandler; - private readonly Action _receivingMessageEventHandler; - private readonly Action _receivedMessageEventHandler; - private readonly Action _failedReceivingMessageEventHandler; - private readonly Action _sendingMessagesEventHandler; - private readonly Action _sentMessagesEventHandler; - private readonly Action _failedSendingMessagesEvent; + private readonly EventLogger _eventLogger; // constructors - public BinaryConnection(ServerId serverId, EndPoint endPoint, ConnectionSettings settings, IStreamFactory streamFactory, IConnectionInitializer connectionInitializer, IEventSubscriber eventSubscriber) + public BinaryConnection(ServerId serverId, + EndPoint endPoint, + ConnectionSettings settings, + IStreamFactory streamFactory, + IConnectionInitializer connectionInitializer, + IEventSubscriber eventSubscriber, + ILoggerFactory loggerFactory) { Ensure.IsNotNull(serverId, nameof(serverId)); _endPoint = Ensure.IsNotNull(endPoint, nameof(endPoint)); @@ -88,21 +84,9 @@ public BinaryConnection(ServerId serverId, EndPoint endPoint, ConnectionSettings _sendLock = new SemaphoreSlim(1); _state = new InterlockedInt32(State.Initial); - _commandEventHelper = new CommandEventHelper(eventSubscriber); - eventSubscriber.TryGetEventHandler(out _failedEventHandler); - eventSubscriber.TryGetEventHandler(out _closingEventHandler); - eventSubscriber.TryGetEventHandler(out _closedEventHandler); - eventSubscriber.TryGetEventHandler(out _openingEventHandler); - eventSubscriber.TryGetEventHandler(out _openedEventHandler); - eventSubscriber.TryGetEventHandler(out _failedOpeningEventHandler); - eventSubscriber.TryGetEventHandler(out _receivingMessageEventHandler); - eventSubscriber.TryGetEventHandler(out _receivedMessageEventHandler); - eventSubscriber.TryGetEventHandler(out _failedReceivingMessageEventHandler); - eventSubscriber.TryGetEventHandler(out _sendingMessagesEventHandler); - eventSubscriber.TryGetEventHandler(out _sentMessagesEventHandler); - eventSubscriber.TryGetEventHandler(out _failedSendingMessagesEvent); - _compressorSource = new CompressorSource(settings.Compressors); + _eventLogger = loggerFactory.CreateEventLogger(eventSubscriber); + _commandEventHelper = new CommandEventHelper(loggerFactory.CreateEventLogger(eventSubscriber)); } // properties @@ -153,6 +137,8 @@ public ConnectionSettings Settings get { return _settings; } } + private bool IsInitializing => _state.Value == State.Initializing; + // methods private void ConnectionFailed(Exception exception) { @@ -168,11 +154,8 @@ private void ConnectionFailed(Exception exception) if (!_failedEventHasBeenRaised) { _failedEventHasBeenRaised = true; - if (_failedEventHandler != null) - { - _failedEventHandler(new ConnectionFailedEvent(_connectionId, exception)); - } - _commandEventHelper.ConnectionFailed(_connectionId, _description?.ServiceId, exception); + _eventLogger.LogAndPublish(new ConnectionFailedEvent(_connectionId, exception)); + _commandEventHelper.ConnectionFailed(_connectionId, _description?.ServiceId, exception, IsInitializing); } } @@ -188,10 +171,7 @@ private void Dispose(bool disposing) { if (disposing) { - if (_closingEventHandler != null) - { - _closingEventHandler(new ConnectionClosingEvent(_connectionId, EventContext.OperationId)); - } + _eventLogger.LogAndPublish(new ConnectionClosingEvent(_connectionId, EventContext.OperationId)); var stopwatch = Stopwatch.StartNew(); _receiveLock.Dispose(); @@ -210,10 +190,7 @@ private void Dispose(bool disposing) } stopwatch.Stop(); - if (_closedEventHandler != null) - { - _closedEventHandler(new ConnectionClosedEvent(_connectionId, stopwatch.Elapsed, EventContext.OperationId)); - } + _eventLogger.LogAndPublish(new ConnectionClosedEvent(_connectionId, stopwatch.Elapsed, EventContext.OperationId)); } } } @@ -843,11 +820,7 @@ public void FailedOpeningConnection(Exception wrappedException) } } - var handler = _connection._failedOpeningEventHandler; - if (handler != null) - { - handler(new ConnectionOpeningFailedEvent(_connection.ConnectionId, _connection._settings, wrappedException, EventContext.OperationId)); - } + _connection._eventLogger.LogAndPublish(new ConnectionOpeningFailedEvent(_connection.ConnectionId, _connection._settings, wrappedException, EventContext.OperationId)); } public void InitializingConnection() @@ -884,20 +857,12 @@ public void OpenedConnection() } } - var handler = _connection._openedEventHandler; - if (handler != null) - { - handler(new ConnectionOpenedEvent(_connection.ConnectionId, _connection._settings, _stopwatch.Elapsed, EventContext.OperationId)); - } + _connection._eventLogger.LogAndPublish(new ConnectionOpenedEvent(_connection.ConnectionId, _connection._settings, _stopwatch.Elapsed, EventContext.OperationId)); } public void OpeningConnection() { - var handler = _connection._openingEventHandler; - if (handler != null) - { - handler(new ConnectionOpeningEvent(_connection.ConnectionId, _connection._settings, EventContext.OperationId)); - } + _connection._eventLogger.LogAndPublish(new ConnectionOpeningEvent(_connection.ConnectionId, _connection._settings, EventContext.OperationId)); _stopwatch = Stopwatch.StartNew(); } @@ -957,37 +922,25 @@ public void FailedReceivingMessage(Exception exception) { if (_connection._commandEventHelper.ShouldCallErrorReceiving) { - _connection._commandEventHelper.ErrorReceiving(_responseTo, _connection._connectionId, _connection.Description?.ServiceId, exception); + _connection._commandEventHelper.ErrorReceiving(_responseTo, _connection._connectionId, _connection.Description?.ServiceId, exception, _connection.IsInitializing); } - var handler = _connection._failedReceivingMessageEventHandler; - if (handler != null) - { - handler(new ConnectionReceivingMessageFailedEvent(_connection.ConnectionId, _responseTo, exception, EventContext.OperationId)); - } + _connection._eventLogger.LogAndPublish(new ConnectionReceivingMessageFailedEvent(_connection.ConnectionId, _responseTo, exception, EventContext.OperationId)); } public void ReceivedMessage(IByteBuffer buffer, ResponseMessage message) { if (_connection._commandEventHelper.ShouldCallAfterReceiving) { - _connection._commandEventHelper.AfterReceiving(message, buffer, _connection._connectionId, _connection.Description?.ServiceId, _messageEncoderSettings); + _connection._commandEventHelper.AfterReceiving(message, buffer, _connection._connectionId, _connection.Description?.ServiceId, _messageEncoderSettings, _connection.IsInitializing); } - var handler = _connection._receivedMessageEventHandler; - if (handler != null) - { - handler(new ConnectionReceivedMessageEvent(_connection.ConnectionId, _responseTo, buffer.Length, _networkDuration, _deserializationDuration, EventContext.OperationId)); - } + _connection._eventLogger.LogAndPublish(new ConnectionReceivedMessageEvent(_connection.ConnectionId, _responseTo, buffer.Length, _networkDuration, _deserializationDuration, EventContext.OperationId)); } public void ReceivingMessage() { - var handler = _connection._receivingMessageEventHandler; - if (handler != null) - { - handler(new ConnectionReceivingMessageEvent(_connection.ConnectionId, _responseTo, EventContext.OperationId)); - } + _connection._eventLogger.LogAndPublish(new ConnectionReceivingMessageEvent(_connection.ConnectionId, _responseTo, EventContext.OperationId)); _stopwatch = Stopwatch.StartNew(); } @@ -1059,32 +1012,24 @@ public IByteBuffer EncodeMessages(CancellationToken cancellationToken, out List< public void EncodingMessages() { - var handler = _connection._sendingMessagesEventHandler; - if (handler != null) - { - handler(new ConnectionSendingMessagesEvent(_connection.ConnectionId, _requestIds.Value, EventContext.OperationId)); - } + _connection._eventLogger.LogAndPublish(new ConnectionSendingMessagesEvent(_connection.ConnectionId, _requestIds.Value, EventContext.OperationId)); } public void FailedSendingMessages(Exception ex) { if (_connection._commandEventHelper.ShouldCallErrorSending) { - _connection._commandEventHelper.ErrorSending(_messages, _connection._connectionId, _connection._description?.ServiceId, ex); + _connection._commandEventHelper.ErrorSending(_messages, _connection._connectionId, _connection._description?.ServiceId, ex, _connection.IsInitializing); } - var handler = _connection._failedSendingMessagesEvent; - if (handler != null) - { - handler(new ConnectionSendingMessagesFailedEvent(_connection.ConnectionId, _requestIds.Value, ex, EventContext.OperationId)); - } + _connection._eventLogger.LogAndPublish(new ConnectionSendingMessagesFailedEvent(_connection.ConnectionId, _requestIds.Value, ex, EventContext.OperationId)); } public void SendingMessages(IByteBuffer buffer) { if (_connection._commandEventHelper.ShouldCallBeforeSending) { - _connection._commandEventHelper.BeforeSending(_messages, _connection.ConnectionId, _connection.Description?.ServiceId, buffer, _messageEncoderSettings, _commandStopwatch); + _connection._commandEventHelper.BeforeSending(_messages, _connection.ConnectionId, _connection.Description?.ServiceId, buffer, _messageEncoderSettings, _commandStopwatch, _connection.IsInitializing); } _networkStopwatch = Stopwatch.StartNew(); @@ -1097,14 +1042,10 @@ public void SentMessages(int bufferLength) if (_connection._commandEventHelper.ShouldCallAfterSending) { - _connection._commandEventHelper.AfterSending(_messages, _connection._connectionId, _connection.Description?.ServiceId); + _connection._commandEventHelper.AfterSending(_messages, _connection._connectionId, _connection.Description?.ServiceId, _connection.IsInitializing); } - var handler = _connection._sentMessagesEventHandler; - if (handler != null) - { - handler(new ConnectionSentMessagesEvent(_connection.ConnectionId, _requestIds.Value, bufferLength, networkDuration, _serializationDuration, EventContext.OperationId)); - } + _connection._eventLogger.LogAndPublish(new ConnectionSentMessagesEvent(_connection.ConnectionId, _requestIds.Value, bufferLength, networkDuration, _serializationDuration, EventContext.OperationId)); } } diff --git a/src/MongoDB.Driver.Core/Core/Connections/BinaryConnectionFactory.cs b/src/MongoDB.Driver.Core/Core/Connections/BinaryConnectionFactory.cs index 81483ad29fa..1589820d92e 100644 --- a/src/MongoDB.Driver.Core/Core/Connections/BinaryConnectionFactory.cs +++ b/src/MongoDB.Driver.Core/Core/Connections/BinaryConnectionFactory.cs @@ -14,6 +14,7 @@ */ using System.Net; +using Microsoft.Extensions.Logging; using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.Events; using MongoDB.Driver.Core.Misc; @@ -29,6 +30,7 @@ internal class BinaryConnectionFactory : IConnectionFactory // fields private readonly IConnectionInitializer _connectionInitializer; private readonly IEventSubscriber _eventSubscriber; + private readonly ILoggerFactory _loggerFactory; private readonly ConnectionSettings _settings; private readonly IStreamFactory _streamFactory; @@ -37,20 +39,25 @@ public BinaryConnectionFactory( ConnectionSettings settings, IStreamFactory streamFactory, IEventSubscriber eventSubscriber, - ServerApi serverApi) + ServerApi serverApi, + ILoggerFactory loggerFactory) { _settings = Ensure.IsNotNull(settings, nameof(settings)); _streamFactory = Ensure.IsNotNull(streamFactory, nameof(streamFactory)); _eventSubscriber = Ensure.IsNotNull(eventSubscriber, nameof(eventSubscriber)); _connectionInitializer = new ConnectionInitializer(settings.ApplicationName, settings.Compressors, serverApi); + _loggerFactory = loggerFactory; } + // properties + public ConnectionSettings ConnectionSettings => _settings; + // methods public IConnection CreateConnection(ServerId serverId, EndPoint endPoint) { Ensure.IsNotNull(serverId, nameof(serverId)); Ensure.IsNotNull(endPoint, nameof(endPoint)); - return new BinaryConnection(serverId, endPoint, _settings, _streamFactory, _connectionInitializer, _eventSubscriber); + return new BinaryConnection(serverId, endPoint, _settings, _streamFactory, _connectionInitializer, _eventSubscriber, _loggerFactory); } } } diff --git a/src/MongoDB.Driver.Core/Core/Connections/CommandEventHelper.cs b/src/MongoDB.Driver.Core/Core/Connections/CommandEventHelper.cs index 3b3664048a7..806fb192f45 100644 --- a/src/MongoDB.Driver.Core/Core/Connections/CommandEventHelper.cs +++ b/src/MongoDB.Driver.Core/Core/Connections/CommandEventHelper.cs @@ -23,6 +23,7 @@ using MongoDB.Bson.IO; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Core.Events; +using MongoDB.Driver.Core.Logging; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.WireProtocol.Messages; using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; @@ -32,24 +33,21 @@ namespace MongoDB.Driver.Core.Connections { internal class CommandEventHelper { - private static readonly string[] __writeConcernIndicators = new[] { "wtimeout", "jnote", "wnote" }; - + private readonly EventLogger _eventLogger; private readonly ConcurrentDictionary _state; - private readonly Action _startedEvent; - private readonly Action _succeededEvent; - private readonly Action _failedEvent; private readonly bool _shouldProcessRequestMessages; private readonly bool _shouldTrackState; + private readonly bool _shouldTrackFailed; + private readonly bool _shouldTrackSucceeded; - public CommandEventHelper(IEventSubscriber eventSubscriber) + public CommandEventHelper(EventLogger eventLogger) { - eventSubscriber.TryGetEventHandler(out _startedEvent); - eventSubscriber.TryGetEventHandler(out _succeededEvent); - eventSubscriber.TryGetEventHandler(out _failedEvent); - - _shouldTrackState = _succeededEvent != null || _failedEvent != null; - _shouldProcessRequestMessages = _startedEvent != null || _shouldTrackState; + _eventLogger = eventLogger; + _shouldTrackSucceeded = _eventLogger.IsEventTracked(); + _shouldTrackFailed = _eventLogger.IsEventTracked(); + _shouldTrackState = _shouldTrackSucceeded || _shouldTrackFailed; + _shouldProcessRequestMessages = _eventLogger.IsEventTracked() || _shouldTrackState; if (_shouldTrackState) { @@ -90,7 +88,8 @@ public void BeforeSending( ObjectId? serviceId, IByteBuffer buffer, MessageEncoderSettings encoderSettings, - Stopwatch stopwatch) + Stopwatch stopwatch, + bool skipLogging) { using (var stream = new ByteBufferStream(buffer, ownsBuffer: false)) { @@ -98,12 +97,12 @@ public void BeforeSending( while (messageQueue.Count > 0) { - ProcessRequestMessages(messageQueue, connectionId, serviceId, stream, encoderSettings, stopwatch); + ProcessRequestMessages(messageQueue, connectionId, serviceId, stream, encoderSettings, stopwatch, skipLogging); } } } - public void AfterSending(IEnumerable messages, ConnectionId connectionId, ObjectId? serviceId) + public void AfterSending(IEnumerable messages, ConnectionId connectionId, ObjectId? serviceId, bool skipLogging) { foreach (var message in messages) { @@ -112,26 +111,25 @@ public void AfterSending(IEnumerable messages, ConnectionId conn state.ExpectedResponseType == ExpectedResponseType.None) { state.Stopwatch.Stop(); - if (_succeededEvent != null) + + if (_shouldTrackSucceeded) { - var @event = new CommandSucceededEvent( + _eventLogger.LogAndPublish(new CommandSucceededEvent( state.CommandName, new BsonDocument("ok", 1), state.OperationId, message.RequestId, connectionId, serviceId, - state.Stopwatch.Elapsed); - - _succeededEvent(@event); + state.Stopwatch.Elapsed), + skipLogging); } - _state.TryRemove(message.RequestId, out state); } } } - public void ErrorSending(IEnumerable messages, ConnectionId connectionId, ObjectId? serviceId, Exception exception) + public void ErrorSending(IEnumerable messages, ConnectionId connectionId, ObjectId? serviceId, Exception exception, bool skipLogging) { foreach (var message in messages) { @@ -139,24 +137,20 @@ public void ErrorSending(IEnumerable messages, ConnectionId conn if (_state.TryRemove(message.RequestId, out state)) { state.Stopwatch.Stop(); - if (_failedEvent != null) - { - var @event = new CommandFailedEvent( - state.CommandName, - exception, - state.OperationId, - message.RequestId, - connectionId, - serviceId, - state.Stopwatch.Elapsed); - - _failedEvent(@event); - } + _eventLogger.LogAndPublish(new CommandFailedEvent( + state.CommandName, + exception, + state.OperationId, + message.RequestId, + connectionId, + serviceId, + state.Stopwatch.Elapsed), + skipLogging); } } } - public void AfterReceiving(ResponseMessage message, IByteBuffer buffer, ConnectionId connectionId, ObjectId? serviceId, MessageEncoderSettings encoderSettings) + public void AfterReceiving(ResponseMessage message, IByteBuffer buffer, ConnectionId connectionId, ObjectId? serviceId, MessageEncoderSettings encoderSettings, bool skipLogging) { CommandState state; if (!_state.TryRemove(message.ResponseTo, out state)) @@ -167,15 +161,15 @@ public void AfterReceiving(ResponseMessage message, IByteBuffer buffer, Connecti if (message is CommandResponseMessage) { - ProcessCommandResponseMessage(state, (CommandResponseMessage)message, buffer, connectionId, serviceId, encoderSettings); + ProcessCommandResponseMessage(state, (CommandResponseMessage)message, buffer, connectionId, serviceId, encoderSettings, skipLogging); } else { - ProcessReplyMessage(state, message, buffer, connectionId, encoderSettings); + ProcessReplyMessage(state, message, buffer, connectionId, encoderSettings, skipLogging); } } - public void ErrorReceiving(int responseTo, ConnectionId connectionId, ObjectId? serviceId, Exception exception) + public void ErrorReceiving(int responseTo, ConnectionId connectionId, ObjectId? serviceId, Exception exception, bool skipLogging) { CommandState state; if (!_state.TryRemove(responseTo, out state)) @@ -185,22 +179,21 @@ public void ErrorReceiving(int responseTo, ConnectionId connectionId, ObjectId? } state.Stopwatch.Stop(); - if (_failedEvent != null) - { - _failedEvent(new CommandFailedEvent( - state.CommandName, - exception, - state.OperationId, - responseTo, - connectionId, - serviceId, - state.Stopwatch.Elapsed)); - } + + _eventLogger.LogAndPublish(new CommandFailedEvent( + state.CommandName, + exception, + state.OperationId, + responseTo, + connectionId, + serviceId, + state.Stopwatch.Elapsed), + skipLogging); } - public void ConnectionFailed(ConnectionId connectionId, ObjectId? serviceId, Exception exception) + public void ConnectionFailed(ConnectionId connectionId, ObjectId? serviceId, Exception exception, bool skipLogging) { - if (_failedEvent == null) + if (!_shouldTrackFailed) { return; } @@ -212,37 +205,36 @@ public void ConnectionFailed(ConnectionId connectionId, ObjectId? serviceId, Exc if (_state.TryRemove(requestId, out state)) { state.Stopwatch.Stop(); - var @event = new CommandFailedEvent( + _eventLogger.LogAndPublish(new CommandFailedEvent( state.CommandName, exception, state.OperationId, requestId, connectionId, serviceId, - state.Stopwatch.Elapsed); - - _failedEvent(@event); + state.Stopwatch.Elapsed), + skipLogging); } } } - private void ProcessRequestMessages(Queue messageQueue, ConnectionId connectionId, ObjectId? serviceId, Stream stream, MessageEncoderSettings encoderSettings, Stopwatch stopwatch) + private void ProcessRequestMessages(Queue messageQueue, ConnectionId connectionId, ObjectId? serviceId, Stream stream, MessageEncoderSettings encoderSettings, Stopwatch stopwatch, bool skipLogging) { var message = messageQueue.Dequeue(); switch (message.MessageType) { case MongoDBMessageType.Command: - ProcessCommandRequestMessage((CommandRequestMessage)message, messageQueue, connectionId, serviceId, new CommandMessageBinaryEncoder(stream, encoderSettings), stopwatch); + ProcessCommandRequestMessage((CommandRequestMessage)message, messageQueue, connectionId, serviceId, new CommandMessageBinaryEncoder(stream, encoderSettings), stopwatch, skipLogging); break; case MongoDBMessageType.Query: - ProcessQueryMessage((QueryMessage)message, connectionId, new QueryMessageBinaryEncoder(stream, encoderSettings), stopwatch); + ProcessQueryMessage((QueryMessage)message, connectionId, new QueryMessageBinaryEncoder(stream, encoderSettings), stopwatch, skipLogging); break; default: throw new MongoInternalException("Invalid message type."); } } - private void ProcessCommandRequestMessage(CommandRequestMessage originalMessage, Queue messageQueue, ConnectionId connectionId, ObjectId? serviceId, CommandMessageBinaryEncoder encoder, Stopwatch stopwatch) + private void ProcessCommandRequestMessage(CommandRequestMessage originalMessage, Queue messageQueue, ConnectionId connectionId, ObjectId? serviceId, CommandMessageBinaryEncoder encoder, Stopwatch stopwatch, bool skipLogging) { var requestId = originalMessage.RequestId; var operationId = EventContext.OperationId; @@ -272,19 +264,15 @@ private void ProcessCommandRequestMessage(CommandRequestMessage originalMessage, command = new BsonDocument(); } - if (_startedEvent != null) - { - var @event = new CommandStartedEvent( - commandName, - command, - databaseNamespace, - operationId, - requestId, - connectionId, - serviceId); - - _startedEvent(@event); - } + _eventLogger.LogAndPublish(new CommandStartedEvent( + commandName, + command, + databaseNamespace, + operationId, + requestId, + connectionId, + serviceId), + skipLogging); if (_shouldTrackState) { @@ -301,7 +289,7 @@ private void ProcessCommandRequestMessage(CommandRequestMessage originalMessage, } } - private void ProcessCommandResponseMessage(CommandState state, CommandResponseMessage message, IByteBuffer buffer, ConnectionId connectionId, ObjectId? serviceId, MessageEncoderSettings encoderSettings) + private void ProcessCommandResponseMessage(CommandState state, CommandResponseMessage message, IByteBuffer buffer, ConnectionId connectionId, ObjectId? serviceId, MessageEncoderSettings encoderSettings, bool skipLogging) { var wrappedMessage = message.WrappedMessage; var type0Section = wrappedMessage.Sections.OfType>().Single(); @@ -322,23 +310,21 @@ private void ProcessCommandResponseMessage(CommandState state, CommandResponseMe if (ok.ToBoolean()) { - if (_succeededEvent != null) - { - _succeededEvent(new CommandSucceededEvent( - state.CommandName, - reply, - state.OperationId, - message.ResponseTo, - connectionId, - serviceId, - state.Stopwatch.Elapsed)); - } + _eventLogger.LogAndPublish(new CommandSucceededEvent( + state.CommandName, + reply, + state.OperationId, + message.ResponseTo, + connectionId, + serviceId, + state.Stopwatch.Elapsed), + skipLogging); } else { - if (_failedEvent != null) + if (_shouldTrackFailed) { - _failedEvent(new CommandFailedEvent( + _eventLogger.LogAndPublish(new CommandFailedEvent( state.CommandName, new MongoCommandException( connectionId, @@ -349,12 +335,13 @@ private void ProcessCommandResponseMessage(CommandState state, CommandResponseMe message.ResponseTo, connectionId, serviceId, - state.Stopwatch.Elapsed)); + state.Stopwatch.Elapsed), + skipLogging); } } } - private void ProcessQueryMessage(QueryMessage originalMessage, ConnectionId connectionId, QueryMessageBinaryEncoder encoder, Stopwatch stopwatch) + private void ProcessQueryMessage(QueryMessage originalMessage, ConnectionId connectionId, QueryMessageBinaryEncoder encoder, Stopwatch stopwatch, bool skipLogging) { var requestId = originalMessage.RequestId; var operationId = EventContext.OperationId; @@ -388,18 +375,14 @@ private void ProcessQueryMessage(QueryMessage originalMessage, ConnectionId conn } } - if (_startedEvent != null) - { - var @event = new CommandStartedEvent( - commandName, - command, - decodedMessage.CollectionNamespace.DatabaseNamespace, - operationId, - requestId, - connectionId); - - _startedEvent(@event); - } + _eventLogger.LogAndPublish(new CommandStartedEvent( + commandName, + command, + decodedMessage.CollectionNamespace.DatabaseNamespace, + operationId, + requestId, + connectionId), + skipLogging); if (_shouldTrackState) { @@ -429,7 +412,7 @@ private void ProcessQueryMessage(QueryMessage originalMessage, ConnectionId conn } } - private void ProcessReplyMessage(CommandState state, ResponseMessage message, IByteBuffer buffer, ConnectionId connectionId, MessageEncoderSettings encoderSettings) + private void ProcessReplyMessage(CommandState state, ResponseMessage message, IByteBuffer buffer, ConnectionId connectionId, MessageEncoderSettings encoderSettings, bool skipLogging) { state.Stopwatch.Stop(); bool disposeOfDocuments = false; @@ -458,9 +441,10 @@ private void ProcessReplyMessage(CommandState state, ResponseMessage message, IB { queryFailureDocument = new BsonDocument(); } - if (_failedEvent != null) + + if (_shouldTrackFailed) { - _failedEvent(new CommandFailedEvent( + _eventLogger.LogAndPublish(new CommandFailedEvent( state.CommandName, new MongoCommandException( connectionId, @@ -470,7 +454,8 @@ private void ProcessReplyMessage(CommandState state, ResponseMessage message, IB state.OperationId, replyMessage.ResponseTo, connectionId, - state.Stopwatch.Elapsed)); + state.Stopwatch.Elapsed), + skipLogging); } } else @@ -478,10 +463,10 @@ private void ProcessReplyMessage(CommandState state, ResponseMessage message, IB switch (state.ExpectedResponseType) { case ExpectedResponseType.Command: - ProcessCommandReplyMessage(state, replyMessage, connectionId); + ProcessCommandReplyMessage(state, replyMessage, connectionId, skipLogging); break; case ExpectedResponseType.Query: - ProcessQueryReplyMessage(state, replyMessage, connectionId); + ProcessQueryReplyMessage(state, replyMessage, connectionId, skipLogging); break; } } @@ -495,7 +480,7 @@ private void ProcessReplyMessage(CommandState state, ResponseMessage message, IB } } - private void ProcessCommandReplyMessage(CommandState state, ReplyMessage replyMessage, ConnectionId connectionId) + private void ProcessCommandReplyMessage(CommandState state, ReplyMessage replyMessage, ConnectionId connectionId, bool skipLogging) { BsonDocument reply = replyMessage.Documents[0]; BsonValue ok; @@ -513,9 +498,9 @@ private void ProcessCommandReplyMessage(CommandState state, ReplyMessage replyMessage, ConnectionId connectionId) + private void ProcessQueryReplyMessage(CommandState state, ReplyMessage replyMessage, ConnectionId connectionId, bool skipLogging) { - if (_succeededEvent != null) + if (_shouldTrackSucceeded) { BsonDocument reply; if (state.CommandName == "explain") @@ -565,13 +552,14 @@ private void ProcessQueryReplyMessage(CommandState state, ReplyMessage { // fields private readonly ServerId _serverId; - private readonly int _localValue; - private readonly int? _serverValue; + private readonly long _localValue; + private readonly long? _serverValue; private readonly int _hashCode; // constructors @@ -38,7 +38,7 @@ public sealed class ConnectionId : IEquatable /// /// The server identifier. public ConnectionId(ServerId serverId) - : this(serverId, IdGenerator.GetNextId()) + : this(serverId, LongIdGenerator.GetNextId()) { } @@ -47,7 +47,7 @@ public ConnectionId(ServerId serverId) /// /// The server identifier. /// The local value. - public ConnectionId(ServerId serverId, int localValue) + public ConnectionId(ServerId serverId, long localValue) { _serverId = Ensure.IsNotNull(serverId, nameof(serverId)); _localValue = Ensure.IsGreaterThanOrEqualToZero(localValue, nameof(localValue)); @@ -57,7 +57,7 @@ public ConnectionId(ServerId serverId, int localValue) .GetHashCode(); } - private ConnectionId(ServerId serverId, int localValue, int serverValue) + private ConnectionId(ServerId serverId, long localValue, long serverValue) : this(serverId, localValue) { _serverValue = Ensure.IsGreaterThanOrEqualToZero(serverValue, nameof(serverValue)); @@ -81,7 +81,19 @@ public ServerId ServerId /// /// The local value. /// + [Obsolete("Use LongLocalValue instead.")] public int LocalValue + { + get { return (int)_localValue; } + } + + /// + /// Gets the local value. + /// + /// + /// The local value. + /// + public long LongLocalValue { get { return _localValue; } } @@ -92,7 +104,19 @@ public int LocalValue /// /// The server value. /// + [Obsolete("Use LongServerValue instead.")] public int? ServerValue + { + get { return (int)_serverValue; } + } + + /// + /// Gets the server value. + /// + /// + /// The server value. + /// + public long? LongServerValue { get { return _serverValue; } } @@ -159,7 +183,7 @@ public override string ToString() /// /// The server value. /// A ConnectionId. - public ConnectionId WithServerValue(int serverValue) + public ConnectionId WithServerValue(long serverValue) { return new ConnectionId(_serverId, _localValue, serverValue); } diff --git a/src/MongoDB.Driver.Core/Core/Connections/IConnectionFactory.cs b/src/MongoDB.Driver.Core/Core/Connections/IConnectionFactory.cs index a26dd5e2527..7e1d7b79f6d 100644 --- a/src/MongoDB.Driver.Core/Core/Connections/IConnectionFactory.cs +++ b/src/MongoDB.Driver.Core/Core/Connections/IConnectionFactory.cs @@ -13,13 +13,8 @@ * limitations under the License. */ -using System; -using System.Collections.Generic; -using System.Linq; using System.Net; -using System.Text; -using System.Threading; -using System.Threading.Tasks; +using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.Servers; namespace MongoDB.Driver.Core.Connections @@ -29,6 +24,12 @@ namespace MongoDB.Driver.Core.Connections /// public interface IConnectionFactory { + // properties + /// + /// Gets the connection settings. + /// + ConnectionSettings ConnectionSettings { get; } + // methods /// /// Creates the connection. diff --git a/src/MongoDB.Driver.Core/Core/Connections/TcpStreamFactory.cs b/src/MongoDB.Driver.Core/Core/Connections/TcpStreamFactory.cs index 129690377be..da707e65e07 100644 --- a/src/MongoDB.Driver.Core/Core/Connections/TcpStreamFactory.cs +++ b/src/MongoDB.Driver.Core/Core/Connections/TcpStreamFactory.cs @@ -259,29 +259,30 @@ private Socket CreateSocket(EndPoint endPoint) var socket = new Socket(addressFamily, SocketType.Stream, ProtocolType.Tcp); - // not all platforms support IOControl try { - var keepAliveValues = new KeepAliveValues + if (OperatingSystemHelper.CurrentOperatingSystem == OperatingSystemPlatform.Windows) { - OnOff = 1, - KeepAliveTime = 120000, // 120 seconds in milliseconds - KeepAliveInterval = 10000 // 10 seconds in milliseconds - }; - socket.IOControl(IOControlCode.KeepAliveValues, keepAliveValues.ToBytes(), null); - } - catch (PlatformNotSupportedException) - { - // most platforms should support this call to SetSocketOption, but just in case call it in a try/catch also - try - { - socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); + // Reviewing the .NET source, Socket.IOControl for IOControlCode.KeepAlivesValue will + // throw PlatformNotSupportedException on all platforms except for Windows. + // https://github.com/dotnet/runtime/blob/main/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Unix.cs#L1346 + var keepAliveValues = new KeepAliveValues + { + OnOff = 1, + KeepAliveTime = 120000, // 120 seconds in milliseconds + KeepAliveInterval = 10000 // 10 seconds in milliseconds + }; + socket.IOControl(IOControlCode.KeepAliveValues, keepAliveValues.ToBytes(), null); } - catch (PlatformNotSupportedException) + else { - // ignore PlatformNotSupportedException + socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); } } + catch (PlatformNotSupportedException) + { + // ignore PlatformNotSupportedException + } return socket; } diff --git a/src/MongoDB.Driver.Core/Core/Encryption/EncryptedCollectionHelper.cs b/src/MongoDB.Driver.Core/Core/Encryption/EncryptedCollectionHelper.cs index 0e66ad36641..7fc13b4a363 100644 --- a/src/MongoDB.Driver.Core/Core/Encryption/EncryptedCollectionHelper.cs +++ b/src/MongoDB.Driver.Core/Core/Encryption/EncryptedCollectionHelper.cs @@ -76,6 +76,30 @@ public static BsonDocument GetEffectiveEncryptedFields(CollectionNamespace colle } } + public static IEnumerable IterateEmptyKeyIds(CollectionNamespace collectionNamespace, BsonDocument encryptedFields) + { + if (!EncryptedCollectionHelper.TryGetEffectiveEncryptedFields(collectionNamespace, encryptedFields, encryptedFieldsMap: null, out var storedEncryptedFields)) + { + throw new InvalidOperationException("There are no encrypted fields defined for the collection."); + } + + if (storedEncryptedFields.TryGetValue("fields", out var fields) && fields is BsonArray fieldsArray) + { + foreach (var field in fieldsArray.OfType()) // If `F` is not a document element, skip it. + { + if (field.TryGetElement("keyId", out var keyId) && keyId.Value == BsonNull.Value) + { + yield return field; + } + } + } + } + + public static void ModifyEncryptedFields(BsonDocument fieldDocument, Guid dataKey) + { + fieldDocument["keyId"] = new BsonBinaryData(dataKey, GuidRepresentation.Standard); + } + public enum HelperCollectionForEncryption { Esc, diff --git a/src/MongoDB.Driver.Core/Core/Events/ClusterAddedServerEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ClusterAddedServerEvent.cs index 6ad625d8c91..8f1f3a55696 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ClusterAddedServerEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ClusterAddedServerEvent.cs @@ -22,7 +22,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs after a server is added to the cluster. /// - public struct ClusterAddedServerEvent + public struct ClusterAddedServerEvent : IEvent { private readonly TimeSpan _duration; private readonly ServerId _serverId; @@ -71,5 +71,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ClusterAddedServer; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ClusterAddingServerEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ClusterAddingServerEvent.cs index 2dee3f53c24..ef745521fc3 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ClusterAddingServerEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ClusterAddingServerEvent.cs @@ -22,7 +22,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs before a server is added to the cluster. /// - public struct ClusterAddingServerEvent + public struct ClusterAddingServerEvent : IEvent { private readonly ClusterId _clusterId; private readonly EndPoint _endPoint; @@ -63,5 +63,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ClusterAddingServer; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ClusterClosedEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ClusterClosedEvent.cs index c53049d0abc..71c31192ca5 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ClusterClosedEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ClusterClosedEvent.cs @@ -21,7 +21,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs after a cluster is closed. /// - public struct ClusterClosedEvent + public struct ClusterClosedEvent : IEvent { private readonly ClusterId _clusterId; private readonly TimeSpan _duration; @@ -62,5 +62,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ClusterClosed; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ClusterClosingEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ClusterClosingEvent.cs index e34e466843d..42d36c46c45 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ClusterClosingEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ClusterClosingEvent.cs @@ -21,7 +21,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs before a cluster is closed. /// - public struct ClusterClosingEvent + public struct ClusterClosingEvent : IEvent { private readonly ClusterId _clusterId; private readonly DateTime _timestamp; @@ -51,5 +51,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ClusterClosing; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ClusterDescriptionChangedEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ClusterDescriptionChangedEvent.cs index 981ea686a27..98089aa8fa1 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ClusterDescriptionChangedEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ClusterDescriptionChangedEvent.cs @@ -21,7 +21,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs when a cluster has changed. /// - public struct ClusterDescriptionChangedEvent + public struct ClusterDescriptionChangedEvent : IEvent { private readonly ClusterDescription _oldDescription; private readonly ClusterDescription _newDescription; @@ -70,5 +70,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ClusterDescriptionChanged; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ClusterOpenedEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ClusterOpenedEvent.cs index fe8ab528f14..8bbd4eedb3f 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ClusterOpenedEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ClusterOpenedEvent.cs @@ -22,7 +22,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs after a cluster is opened. /// - public struct ClusterOpenedEvent + public struct ClusterOpenedEvent : IEvent { private readonly ClusterId _clusterId; private readonly ClusterSettings _clusterSettings; @@ -74,5 +74,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ClusterOpened; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ClusterOpeningEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ClusterOpeningEvent.cs index 0e74a7b69c9..285b6535e6a 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ClusterOpeningEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ClusterOpeningEvent.cs @@ -22,7 +22,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs before a cluster is opened. /// - public struct ClusterOpeningEvent + public struct ClusterOpeningEvent : IEvent { private readonly ClusterId _clusterId; private readonly ClusterSettings _clusterSettings; @@ -63,5 +63,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ClusterOpening; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ClusterRemovedServerEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ClusterRemovedServerEvent.cs index 61a2e2e3beb..cc18155e944 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ClusterRemovedServerEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ClusterRemovedServerEvent.cs @@ -22,7 +22,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs after a server has been removed from the cluster. /// - public struct ClusterRemovedServerEvent + public struct ClusterRemovedServerEvent : IEvent { private readonly TimeSpan _duration; private readonly string _reason; @@ -82,5 +82,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ClusterRemovedServer; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ClusterRemovingServerEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ClusterRemovingServerEvent.cs index 67b1e6150b7..2ae31a0f477 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ClusterRemovingServerEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ClusterRemovingServerEvent.cs @@ -22,7 +22,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs before a server is removed from the cluster. /// - public struct ClusterRemovingServerEvent + public struct ClusterRemovingServerEvent : IEvent { private readonly string _reason; private readonly ServerId _serverId; @@ -71,5 +71,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ClusterRemovingServer; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ClusterSelectedServerEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ClusterSelectedServerEvent.cs index 415a6ad1c2c..e4b36a6687b 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ClusterSelectedServerEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ClusterSelectedServerEvent.cs @@ -23,7 +23,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs after a server is selected. /// - public struct ClusterSelectedServerEvent + public struct ClusterSelectedServerEvent : IEvent { private readonly ClusterDescription _clusterDescription; private readonly TimeSpan _duration; @@ -105,5 +105,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ClusterSelectedServer; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ClusterSelectingServerEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ClusterSelectingServerEvent.cs index e2473c0da29..1505106d6a0 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ClusterSelectingServerEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ClusterSelectingServerEvent.cs @@ -22,7 +22,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs before a server is selected. /// - public struct ClusterSelectingServerEvent + public struct ClusterSelectingServerEvent : IEvent { private readonly ClusterDescription _clusterDescription; private readonly long? _operationId; @@ -82,5 +82,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ClusterSelectingServer; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ClusterSelectingServerFailedEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ClusterSelectingServerFailedEvent.cs index 3dc2b37c7d1..e4d5deb7a5b 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ClusterSelectingServerFailedEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ClusterSelectingServerFailedEvent.cs @@ -22,7 +22,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs when selecting a server fails. /// - public struct ClusterSelectingServerFailedEvent + public struct ClusterSelectingServerFailedEvent : IEvent { private readonly ClusterDescription _clusterDescription; private readonly Exception _exception; @@ -93,5 +93,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ClusterSelectingServerFailed; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/CommandFailedEvent.cs b/src/MongoDB.Driver.Core/Core/Events/CommandFailedEvent.cs index c7c679ecbac..543aabea698 100644 --- a/src/MongoDB.Driver.Core/Core/Events/CommandFailedEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/CommandFailedEvent.cs @@ -23,7 +23,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs when a command has failed. /// - public struct CommandFailedEvent + public struct CommandFailedEvent : IEvent { private readonly string _commandName; private readonly ConnectionId _connectionId; @@ -133,5 +133,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.CommandFailed; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/CommandStartedEvent.cs b/src/MongoDB.Driver.Core/Core/Events/CommandStartedEvent.cs index ac7a5bffa60..1ef3b2db977 100644 --- a/src/MongoDB.Driver.Core/Core/Events/CommandStartedEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/CommandStartedEvent.cs @@ -23,7 +23,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs when a command has started. /// - public struct CommandStartedEvent + public struct CommandStartedEvent : IEvent { private readonly BsonDocument _command; private readonly string _commandName; @@ -133,5 +133,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.CommandStarted; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/CommandSucceededEvent.cs b/src/MongoDB.Driver.Core/Core/Events/CommandSucceededEvent.cs index 60e493435a2..ea967a0ffc3 100644 --- a/src/MongoDB.Driver.Core/Core/Events/CommandSucceededEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/CommandSucceededEvent.cs @@ -23,7 +23,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs when a command has succeeded. /// - public struct CommandSucceededEvent + public struct CommandSucceededEvent : IEvent { private readonly string _commandName; private readonly ConnectionId _connectionId; @@ -133,5 +133,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.CommandSucceeded; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ConnectionClosedEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ConnectionClosedEvent.cs index 02501959b8d..ef8bf3d2b88 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ConnectionClosedEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ConnectionClosedEvent.cs @@ -23,7 +23,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs after a connection is closed. /// - public struct ConnectionClosedEvent + public struct ConnectionClosedEvent : IEvent { private readonly ConnectionId _connectionId; private readonly TimeSpan _duration; @@ -91,5 +91,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ConnectionClosed; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ConnectionClosingEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ConnectionClosingEvent.cs index 9e7b5bb3a5b..90fef002025 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ConnectionClosingEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ConnectionClosingEvent.cs @@ -23,7 +23,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs before a connection is closed. /// - public struct ConnectionClosingEvent + public struct ConnectionClosingEvent : IEvent { private readonly ConnectionId _connectionId; private readonly long? _operationId; @@ -80,5 +80,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ConnectionClosing; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ConnectionCreatedEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ConnectionCreatedEvent.cs index 5d441b39e64..ed87ee91fd8 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ConnectionCreatedEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ConnectionCreatedEvent.cs @@ -22,9 +22,9 @@ namespace MongoDB.Driver.Core.Events { /// - /// Occurs when a connection is created.. + /// Occurs when a connection is created. /// - public struct ConnectionCreatedEvent + public struct ConnectionCreatedEvent : IEvent { private readonly ConnectionId _connectionId; private readonly ConnectionSettings _connectionSettings; @@ -92,5 +92,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ConnectionCreated; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ConnectionFailedEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ConnectionFailedEvent.cs index 762fadfb912..00dfd5a902a 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ConnectionFailedEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ConnectionFailedEvent.cs @@ -23,7 +23,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs when a connection fails. /// - public struct ConnectionFailedEvent + public struct ConnectionFailedEvent : IEvent { private readonly ConnectionId _connectionId; private readonly Exception _exception; @@ -83,5 +83,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ConnectionFailed; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ConnectionOpenedEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ConnectionOpenedEvent.cs index 6ab52aa5985..52c97aabd64 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ConnectionOpenedEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ConnectionOpenedEvent.cs @@ -24,7 +24,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs after a connection is opened. /// - public struct ConnectionOpenedEvent + public struct ConnectionOpenedEvent : IEvent { private readonly ConnectionId _connectionId; private readonly ConnectionSettings _connectionSettings; @@ -103,5 +103,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ConnectionOpened; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ConnectionOpeningEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ConnectionOpeningEvent.cs index d179688f072..6df5adbe674 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ConnectionOpeningEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ConnectionOpeningEvent.cs @@ -24,7 +24,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs before a connection is opened. /// - public struct ConnectionOpeningEvent + public struct ConnectionOpeningEvent : IEvent { private readonly ConnectionId _connectionId; private readonly ConnectionSettings _connectionSettings; @@ -92,5 +92,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ConnectionOpening; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ConnectionOpeningFailedEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ConnectionOpeningFailedEvent.cs index fa657045e36..0bbc921c28e 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ConnectionOpeningFailedEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ConnectionOpeningFailedEvent.cs @@ -24,7 +24,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs when a connection fails to open. /// - public struct ConnectionOpeningFailedEvent + public struct ConnectionOpeningFailedEvent : IEvent { private readonly ConnectionId _connectionId; private readonly ConnectionSettings _connectionSettings; @@ -103,5 +103,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ConnectionOpeningFailed; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolAddedConnectionEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolAddedConnectionEvent.cs index 053618e9be1..7119ef054dd 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolAddedConnectionEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolAddedConnectionEvent.cs @@ -23,7 +23,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs after a connection is added to the pool. /// - public struct ConnectionPoolAddedConnectionEvent + public struct ConnectionPoolAddedConnectionEvent : IEvent { private readonly ConnectionId _connectionId; private readonly TimeSpan _duration; @@ -91,5 +91,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ConnectionPoolAddedConnection; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolAddingConnectionEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolAddingConnectionEvent.cs index ce35f9ebad5..5736be28faa 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolAddingConnectionEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolAddingConnectionEvent.cs @@ -22,7 +22,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs before a connection is added to the pool. /// - public struct ConnectionPoolAddingConnectionEvent + public struct ConnectionPoolAddingConnectionEvent : IEvent { private readonly long? _operationId; private readonly ServerId _serverId; @@ -71,5 +71,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ConnectionPoolAddingConnection; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolCheckedInConnectionEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolCheckedInConnectionEvent.cs index 4fee6a901e8..19994e78fec 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolCheckedInConnectionEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolCheckedInConnectionEvent.cs @@ -23,7 +23,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs after a connection is checked in to the pool. /// - public struct ConnectionPoolCheckedInConnectionEvent + public struct ConnectionPoolCheckedInConnectionEvent : IEvent { private readonly ConnectionId _connectionId; private readonly TimeSpan _duration; @@ -91,5 +91,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ConnectionPoolCheckedInConnection; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolCheckedOutConnectionEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolCheckedOutConnectionEvent.cs index 925dcdc7b9b..e21236ccf72 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolCheckedOutConnectionEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolCheckedOutConnectionEvent.cs @@ -23,7 +23,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs after a connection is checked out of the pool. /// - public struct ConnectionPoolCheckedOutConnectionEvent + public struct ConnectionPoolCheckedOutConnectionEvent : IEvent { private readonly ConnectionId _connectionId; private readonly TimeSpan _duration; @@ -91,5 +91,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ConnectionPoolCheckedOutConnection; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolCheckingInConnectionEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolCheckingInConnectionEvent.cs index 3d88bb54a0f..b9237abbfd4 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolCheckingInConnectionEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolCheckingInConnectionEvent.cs @@ -23,7 +23,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs before a connection is checked in to the pool. /// - public struct ConnectionPoolCheckingInConnectionEvent + public struct ConnectionPoolCheckingInConnectionEvent : IEvent { private readonly ConnectionId _connectionId; private readonly long? _operationId; @@ -80,5 +80,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ConnectionPoolCheckingInConnection; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolCheckingOutConnectionEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolCheckingOutConnectionEvent.cs index 520bf76820e..381f27c2640 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolCheckingOutConnectionEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolCheckingOutConnectionEvent.cs @@ -20,9 +20,9 @@ namespace MongoDB.Driver.Core.Events { /// - /// Occurs before a connection is checking out of the pool. + /// Occurs before a connection is checked out of the pool. /// - public struct ConnectionPoolCheckingOutConnectionEvent + public struct ConnectionPoolCheckingOutConnectionEvent : IEvent { private readonly long? _operationId; private readonly ServerId _serverId; @@ -71,5 +71,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ConnectionPoolCheckingOutConnection; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolCheckingOutConnectionFailedEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolCheckingOutConnectionFailedEvent.cs index 7cedfaf7ce8..9f7cea41b44 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolCheckingOutConnectionFailedEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolCheckingOutConnectionFailedEvent.cs @@ -22,7 +22,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs when a connection could not be checked out of the pool. /// - public struct ConnectionPoolCheckingOutConnectionFailedEvent + public struct ConnectionPoolCheckingOutConnectionFailedEvent : IEvent { private readonly ConnectionCheckOutFailedReason _reason; private readonly ServerId _serverId; @@ -97,5 +97,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ConnectionPoolCheckingOutConnectionFailed; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolClearedEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolClearedEvent.cs index eda4749abd5..79cc2ab4069 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolClearedEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolClearedEvent.cs @@ -25,7 +25,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs after the pool is cleared. /// - public struct ConnectionPoolClearedEvent + public struct ConnectionPoolClearedEvent : IEvent { private readonly ConnectionPoolSettings _connectionPoolSettings; private readonly ServerId _serverId; @@ -95,5 +95,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ConnectionPoolCleared; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolClearingEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolClearingEvent.cs index 026ed59cd8f..e5638d401db 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolClearingEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolClearingEvent.cs @@ -25,7 +25,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs when the pool is about to be cleared. /// - public struct ConnectionPoolClearingEvent + public struct ConnectionPoolClearingEvent : IEvent { private readonly ConnectionPoolSettings _connectionPoolSettings; private readonly ObjectId? _serviceId; @@ -95,5 +95,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ConnectionPoolClearing; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolClosedEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolClosedEvent.cs index 5052fab7b9e..96757acd0b6 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolClosedEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolClosedEvent.cs @@ -22,7 +22,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs after the pool is closed. /// - public struct ConnectionPoolClosedEvent + public struct ConnectionPoolClosedEvent : IEvent { private readonly ServerId _serverId; private readonly DateTime _timestamp; @@ -60,5 +60,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ConnectionPoolClosed; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolClosingEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolClosingEvent.cs index 454ff7a722f..2ca0b5b8b2e 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolClosingEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolClosingEvent.cs @@ -22,7 +22,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs before the pool is closed. /// - public struct ConnectionPoolClosingEvent + public struct ConnectionPoolClosingEvent : IEvent { private readonly ServerId _serverId; private readonly DateTime _timestamp; @@ -60,5 +60,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ConnectionPoolClosing; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolOpenedEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolOpenedEvent.cs index c334e6282f9..5fbe76379f6 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolOpenedEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolOpenedEvent.cs @@ -23,7 +23,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs after the pool is opened. /// - public struct ConnectionPoolOpenedEvent + public struct ConnectionPoolOpenedEvent : IEvent { private readonly ConnectionPoolSettings _connectionPoolSettings; private readonly ServerId _serverId; @@ -72,5 +72,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ConnectionPoolOpened; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolOpeningEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolOpeningEvent.cs index 2b8ea3b8468..13e04ba7e39 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolOpeningEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolOpeningEvent.cs @@ -23,7 +23,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs before the pool is opened. /// - public struct ConnectionPoolOpeningEvent + public struct ConnectionPoolOpeningEvent : IEvent { private readonly ConnectionPoolSettings _connectionPoolSettings; private readonly ServerId _serverId; @@ -72,5 +72,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ConnectionPoolOpening; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolReadyEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolReadyEvent.cs index e51cb5f687d..dad9d2ce650 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolReadyEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolReadyEvent.cs @@ -22,7 +22,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs after the pool is opened. /// - public struct ConnectionPoolReadyEvent + public struct ConnectionPoolReadyEvent : IEvent { private readonly ServerId _serverId; private readonly ConnectionPoolSettings _connectionPoolSettings; @@ -61,5 +61,8 @@ public ServerId ServerId { get { return _serverId; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ConnectionPoolReady; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolRemovedConnectionEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolRemovedConnectionEvent.cs index 7410413d31c..48378bac66f 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolRemovedConnectionEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolRemovedConnectionEvent.cs @@ -23,7 +23,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs after a connection is removed from the pool. /// - public struct ConnectionPoolRemovedConnectionEvent + public struct ConnectionPoolRemovedConnectionEvent : IEvent { private readonly ConnectionId _connectionId; private readonly TimeSpan _duration; @@ -91,5 +91,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ConnectionPoolRemovedConnection; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolRemovingConnectionEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolRemovingConnectionEvent.cs index cdd442f445a..a7dbeece38b 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolRemovingConnectionEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ConnectionPoolRemovingConnectionEvent.cs @@ -23,7 +23,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs before a connection is removed from the pool. /// - public struct ConnectionPoolRemovingConnectionEvent + public struct ConnectionPoolRemovingConnectionEvent : IEvent { private readonly ConnectionId _connectionId; private readonly long? _operationId; @@ -80,5 +80,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ConnectionPoolRemovingConnection; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ConnectionReceivedMessageEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ConnectionReceivedMessageEvent.cs index b5b8689c392..7a342c6a740 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ConnectionReceivedMessageEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ConnectionReceivedMessageEvent.cs @@ -23,7 +23,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs after a message is received. /// - public struct ConnectionReceivedMessageEvent + public struct ConnectionReceivedMessageEvent : IEvent { private readonly ConnectionId _connectionId; private readonly TimeSpan _deserializationDuration; @@ -132,5 +132,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ConnectionReceivedMessage; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ConnectionReceivingMessageEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ConnectionReceivingMessageEvent.cs index e0fd121c5ab..f3f986b17e7 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ConnectionReceivingMessageEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ConnectionReceivingMessageEvent.cs @@ -23,7 +23,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs before a message is received. /// - public struct ConnectionReceivingMessageEvent + public struct ConnectionReceivingMessageEvent : IEvent { private readonly ConnectionId _connectionId; private readonly long? _operationId; @@ -91,5 +91,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ConnectionReceivingMessage; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ConnectionReceivingMessageFailedEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ConnectionReceivingMessageFailedEvent.cs index 97d7e322d8f..aac7061a1a0 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ConnectionReceivingMessageFailedEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ConnectionReceivingMessageFailedEvent.cs @@ -23,7 +23,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs when a message was unable to be received. /// - public struct ConnectionReceivingMessageFailedEvent + public struct ConnectionReceivingMessageFailedEvent : IEvent { private readonly ConnectionId _connectionId; private readonly Exception _exception; @@ -102,5 +102,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ConnectionReceivingMessageFailed; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ConnectionSendingMessagesEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ConnectionSendingMessagesEvent.cs index 4d1d45a3927..1e80c9a8e66 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ConnectionSendingMessagesEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ConnectionSendingMessagesEvent.cs @@ -24,7 +24,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs before a message is sent. /// - public struct ConnectionSendingMessagesEvent + public struct ConnectionSendingMessagesEvent : IEvent { private readonly ConnectionId _connectionId; private readonly long? _operationId; @@ -92,5 +92,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ConnectionSendingMessages; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ConnectionSendingMessagesFailedEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ConnectionSendingMessagesFailedEvent.cs index daa76ab32b2..2fb5aab05df 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ConnectionSendingMessagesFailedEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ConnectionSendingMessagesFailedEvent.cs @@ -24,7 +24,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs when a message could not be sent. /// - public struct ConnectionSendingMessagesFailedEvent + public struct ConnectionSendingMessagesFailedEvent : IEvent { private readonly ConnectionId _connectionId; private readonly Exception _exception; @@ -103,5 +103,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ConnectionSendingMessagesFailed; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ConnectionSentMessagesEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ConnectionSentMessagesEvent.cs index 1cd7603bf60..a5e3608b9c9 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ConnectionSentMessagesEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ConnectionSentMessagesEvent.cs @@ -24,7 +24,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs after a message has been sent. /// - public struct ConnectionSentMessagesEvent + public struct ConnectionSentMessagesEvent : IEvent { private readonly ConnectionId _connectionId; private readonly TimeSpan _networkDuration; @@ -133,5 +133,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ConnectionSentMessages; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/Diagnostics/TraceSourceEventHelper.cs b/src/MongoDB.Driver.Core/Core/Events/Diagnostics/TraceSourceEventHelper.cs index d85338080b2..406738c8b09 100644 --- a/src/MongoDB.Driver.Core/Core/Events/Diagnostics/TraceSourceEventHelper.cs +++ b/src/MongoDB.Driver.Core/Core/Events/Diagnostics/TraceSourceEventHelper.cs @@ -50,11 +50,11 @@ public static string Label(ClusterId clusterId) public static string Format(ConnectionId id) { - if (id.ServerValue.HasValue) + if (id.LongServerValue.HasValue) { - return id.LocalValue.ToString() + "-" + id.ServerValue.Value.ToString(); + return id.LongLocalValue.ToString() + "-" + id.LongServerValue.Value.ToString(); } - return id.LocalValue.ToString(); + return id.LongLocalValue.ToString(); } public static string Format(ServerId serverId) diff --git a/src/MongoDB.Driver.Core/Core/Events/EventPublisher.cs b/src/MongoDB.Driver.Core/Core/Events/EventPublisher.cs new file mode 100644 index 00000000000..042bc15db07 --- /dev/null +++ b/src/MongoDB.Driver.Core/Core/Events/EventPublisher.cs @@ -0,0 +1,60 @@ +/* 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 MongoDB.Driver.Core.Misc; + +namespace MongoDB.Driver.Core.Events +{ + internal sealed class EventPublisher + { + private static readonly Delegate __eventHandlerNull = new Action(() => { }); + private static readonly int __eventTypesCount = Enum.GetValues(typeof(EventType)).Length; + + private readonly Delegate[] _eventHandlers; + private readonly IEventSubscriber _eventSubscriber; + + public EventPublisher(IEventSubscriber eventSubscriber) + { + _eventSubscriber = Ensure.IsNotNull(eventSubscriber, nameof(eventSubscriber)); + _eventHandlers = new Delegate[__eventTypesCount]; + } + + public bool IsEventTracked() where TEvent : IEvent + { + _eventSubscriber.TryGetEventHandler(out var handler); + return handler != null; + } + + public void Publish(TEvent @event) where TEvent : IEvent + { + var eventType = (int)@event.Type; + var eventHandler = _eventHandlers[eventType]; + + if (eventHandler == null) + { + _eventSubscriber.TryGetEventHandler(out var registeredHandler); + eventHandler = registeredHandler ?? __eventHandlerNull; + _eventHandlers[eventType] = eventHandler; + } + + if (eventHandler != __eventHandlerNull) + { + var action = (Action)eventHandler; + action(@event); + } + } + } +} diff --git a/src/MongoDB.Driver.Core/Core/Events/EventType.cs b/src/MongoDB.Driver.Core/Core/Events/EventType.cs new file mode 100644 index 00000000000..065dad8542a --- /dev/null +++ b/src/MongoDB.Driver.Core/Core/Events/EventType.cs @@ -0,0 +1,74 @@ +/* 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. +*/ + +namespace MongoDB.Driver.Core.Events +{ + internal enum EventType + { + ClusterAddedServer = 0, + ClusterAddingServer, + ClusterClosed, + ClusterClosing, + ClusterDescriptionChanged, + ClusterOpened, + ClusterOpening, + ClusterRemovedServer, + ClusterRemovingServer, + ClusterSelectedServer, + ClusterSelectingServer, + ClusterSelectingServerFailed, + CommandFailed, + CommandStarted, + CommandSucceeded, + ConnectionClosed, + ConnectionClosing, + ConnectionCreated, + ConnectionFailed, + ConnectionOpened, + ConnectionOpening, + ConnectionOpeningFailed, + ConnectionPoolAddedConnection, + ConnectionPoolAddingConnection, + ConnectionPoolCheckedInConnection, + ConnectionPoolCheckedOutConnection, + ConnectionPoolCheckingInConnection, + ConnectionPoolCheckingOutConnection, + ConnectionPoolCheckingOutConnectionFailed, + ConnectionPoolCleared, + ConnectionPoolClearing, + ConnectionPoolClosed, + ConnectionPoolClosing, + ConnectionPoolOpened, + ConnectionPoolOpening, + ConnectionPoolReady, + ConnectionPoolRemovedConnection, + ConnectionPoolRemovingConnection, + ConnectionReceivedMessage, + ConnectionReceivingMessage, + ConnectionReceivingMessageFailed, + ConnectionSendingMessages, + ConnectionSendingMessagesFailed, + ConnectionSentMessages, + SdamInformation, + ServerClosed, + ServerClosing, + ServerDescriptionChanged, + ServerHeartbeatFailed, + ServerHeartbeatStarted, + ServerHeartbeatSucceeded, + ServerOpened, + ServerOpening + } +} diff --git a/tests/MongoDB.Driver.Core.TestHelpers/Logging/ILoggerFactory.cs b/src/MongoDB.Driver.Core/Core/Events/IEvent.cs similarity index 74% rename from tests/MongoDB.Driver.Core.TestHelpers/Logging/ILoggerFactory.cs rename to src/MongoDB.Driver.Core/Core/Events/IEvent.cs index 4e457ed4f48..af3167dd312 100644 --- a/tests/MongoDB.Driver.Core.TestHelpers/Logging/ILoggerFactory.cs +++ b/src/MongoDB.Driver.Core/Core/Events/IEvent.cs @@ -1,4 +1,4 @@ -/* Copyright 2021-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. @@ -13,10 +13,10 @@ * limitations under the License. */ -namespace MongoDB.Driver.Core.TestHelpers.Logging +namespace MongoDB.Driver.Core.Events { - public interface ILoggerFactory + internal interface IEvent { - public ILogger CreateLogger(); + public EventType Type { get; } } } diff --git a/src/MongoDB.Driver.Core/Core/Events/SdamInformationEvent.cs b/src/MongoDB.Driver.Core/Core/Events/SdamInformationEvent.cs index 2e54678811c..7fd83fb11e7 100644 --- a/src/MongoDB.Driver.Core/Core/Events/SdamInformationEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/SdamInformationEvent.cs @@ -14,46 +14,83 @@ */ using System; +using MongoDB.Driver.Core.Misc; namespace MongoDB.Driver.Core.Events { /// - /// An informational event used for logging Server Discovery and Monitoring (SDAM) events. + /// An informational event used for logging Server Discovery and Monitoring (SDAM) events. /// - public struct SdamInformationEvent + public struct SdamInformationEvent : IEvent { - private readonly Lazy _message; + private readonly object _arg0; + private readonly object[] _args; + private readonly int _argsCount; + + private readonly string _messageFormat; private readonly DateTime _timestamp; + private string _formattedMessage; + /// /// Initializes a new instance of the struct. /// - /// Function that creates the message to log. - public SdamInformationEvent(Func createMessage) - : this(new Lazy(createMessage)) + /// Message format. + /// Message argument. + public SdamInformationEvent(string messageFormat, object arg0) : + this(messageFormat, 1, arg0, null) { } /// /// Initializes a new instance of the struct. /// - /// The message to log. - public SdamInformationEvent(Lazy message) + /// Message format. + /// Message arguments. + public SdamInformationEvent(string messageFormat, params object[] args) : + this(messageFormat, -1, null, args) + { + } + + private SdamInformationEvent(string messageFormat, int argsCount, object arg0, params object[] args) { - _message = message; + _args = args; + _arg0 = arg0; + _argsCount = argsCount; + _messageFormat = Ensure.IsNotNull(messageFormat, nameof(messageFormat)); _timestamp = DateTime.UtcNow; + _formattedMessage = null; } /// /// Gets the message. /// - public string Message => _message.Value; + public string Message + { + get + { + if (_formattedMessage == null) + { + _formattedMessage = _argsCount switch + { + -1 => string.Format(_messageFormat, _args), + 1 => string.Format(_messageFormat, _arg0), + _ => throw new InvalidOperationException($"Not supported argument count {_argsCount}") + }; + } + + return _formattedMessage; + } + } /// /// Gets the timestamp. /// public DateTime Timestamp => _timestamp; + // explicit interface implementations + EventType IEvent.Type => EventType.SdamInformation; + /// public override string ToString() { diff --git a/src/MongoDB.Driver.Core/Core/Events/ServerClosedEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ServerClosedEvent.cs index 0cf12c29bc5..d5bde2cfcd4 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ServerClosedEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ServerClosedEvent.cs @@ -22,7 +22,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs after a server is closed. /// - public struct ServerClosedEvent + public struct ServerClosedEvent : IEvent { private readonly ServerId _serverId; private readonly TimeSpan _duration; @@ -71,5 +71,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ServerClosed; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ServerClosingEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ServerClosingEvent.cs index fa1a016d8a0..938ce6e12ae 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ServerClosingEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ServerClosingEvent.cs @@ -22,7 +22,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs before a server is closed. /// - public struct ServerClosingEvent + public struct ServerClosingEvent : IEvent { private readonly ServerId _serverId; private readonly DateTime _timestamp; @@ -60,5 +60,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ServerClosing; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ServerDescriptionChangedEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ServerDescriptionChangedEvent.cs index 0be8d342972..3cccc301a1d 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ServerDescriptionChangedEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ServerDescriptionChangedEvent.cs @@ -22,7 +22,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs after a server's description has changed. /// - public struct ServerDescriptionChangedEvent + public struct ServerDescriptionChangedEvent : IEvent { private readonly ServerDescription _oldDescription; private readonly ServerDescription _newDescription; @@ -79,5 +79,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ServerDescriptionChanged; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ServerHeartbeatFailedEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ServerHeartbeatFailedEvent.cs index 729ae402b59..cf468fb6c6f 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ServerHeartbeatFailedEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ServerHeartbeatFailedEvent.cs @@ -23,7 +23,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs when a heartbeat failed. /// - public struct ServerHeartbeatFailedEvent + public struct ServerHeartbeatFailedEvent : IEvent { private readonly bool _awaited; private readonly ConnectionId _connectionId; @@ -88,5 +88,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ServerHeartbeatFailed; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ServerHeartbeatStartedEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ServerHeartbeatStartedEvent.cs index abc115ba76d..c6f08c9bd14 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ServerHeartbeatStartedEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ServerHeartbeatStartedEvent.cs @@ -21,9 +21,9 @@ namespace MongoDB.Driver.Core.Events { /// - /// Occurs when a heartbeat succeeded. + /// Occurs before heartbeat is issued. /// - public struct ServerHeartbeatStartedEvent + public struct ServerHeartbeatStartedEvent : IEvent { private readonly bool _awaited; private readonly ConnectionId _connectionId; @@ -77,5 +77,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ServerHeartbeatStarted; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ServerHeartbeatSucceededEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ServerHeartbeatSucceededEvent.cs index 35e8c9b067b..f614bc86817 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ServerHeartbeatSucceededEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ServerHeartbeatSucceededEvent.cs @@ -21,9 +21,9 @@ namespace MongoDB.Driver.Core.Events { /// - /// Occurs before heartbeat is issued. + /// Occurs when a heartbeat succeeded. /// - public struct ServerHeartbeatSucceededEvent + public struct ServerHeartbeatSucceededEvent : IEvent { private readonly bool _awaited; private readonly ConnectionId _connectionId; @@ -88,5 +88,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ServerHeartbeatSucceeded; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ServerOpenedEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ServerOpenedEvent.cs index f8db67c7252..8c2d9cd258f 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ServerOpenedEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ServerOpenedEvent.cs @@ -23,7 +23,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs after a server is opened. /// - public struct ServerOpenedEvent + public struct ServerOpenedEvent : IEvent { private readonly TimeSpan _duration; private readonly ServerId _serverId; @@ -83,5 +83,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ServerOpened; } } diff --git a/src/MongoDB.Driver.Core/Core/Events/ServerOpeningEvent.cs b/src/MongoDB.Driver.Core/Core/Events/ServerOpeningEvent.cs index 46ae01bb88b..83c62a99a37 100644 --- a/src/MongoDB.Driver.Core/Core/Events/ServerOpeningEvent.cs +++ b/src/MongoDB.Driver.Core/Core/Events/ServerOpeningEvent.cs @@ -23,7 +23,7 @@ namespace MongoDB.Driver.Core.Events /// /// Occurs before a server is opened. /// - public struct ServerOpeningEvent + public struct ServerOpeningEvent : IEvent { private readonly ServerId _serverId; private readonly ServerSettings _serverSettings; @@ -72,5 +72,8 @@ public DateTime Timestamp { get { return _timestamp; } } + + // explicit interface implementations + EventType IEvent.Type => EventType.ServerOpening; } } diff --git a/src/MongoDB.Driver.Core/Core/NativeLibraryLoader/ILibraryLocator.cs b/src/MongoDB.Driver.Core/Core/Logging/EventLogFormattingOptions.cs similarity index 60% rename from src/MongoDB.Driver.Core/Core/NativeLibraryLoader/ILibraryLocator.cs rename to src/MongoDB.Driver.Core/Core/Logging/EventLogFormattingOptions.cs index 0ac8b7645f1..6a3a9a930ca 100644 --- a/src/MongoDB.Driver.Core/Core/NativeLibraryLoader/ILibraryLocator.cs +++ b/src/MongoDB.Driver.Core/Core/Logging/EventLogFormattingOptions.cs @@ -1,4 +1,4 @@ -/* Copyright 2019-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. @@ -15,12 +15,15 @@ using MongoDB.Driver.Core.Misc; -namespace MongoDB.Driver.Core.NativeLibraryLoader +namespace MongoDB.Driver.Core.Logging { - internal interface ILibraryLocator + internal sealed class EventLogFormattingOptions { - bool IsX32ModeSupported { get; } - string LibraryName { get; } - string GetLibraryAbsolutePath(OperatingSystemPlatform currentPlatform); + public int MaxDocumentSize { get; } + + public EventLogFormattingOptions(int maxCommandDocumentSize) + { + MaxDocumentSize = Ensure.IsGreaterThanOrEqualToZero(maxCommandDocumentSize, nameof(maxCommandDocumentSize)); + } } } diff --git a/src/MongoDB.Driver.Core/Core/Logging/EventLogger.cs b/src/MongoDB.Driver.Core/Core/Logging/EventLogger.cs new file mode 100644 index 00000000000..52ae6515259 --- /dev/null +++ b/src/MongoDB.Driver.Core/Core/Logging/EventLogger.cs @@ -0,0 +1,96 @@ +/* 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 Microsoft.Extensions.Logging; +using MongoDB.Driver.Core.Events; + +namespace MongoDB.Driver.Core.Logging +{ + internal sealed class EventLogger where T : LogCategories.EventCategory + { + private readonly EventPublisher _eventPublisher; + private readonly ILogger _logger; + private readonly EventLogFormattingOptions _eventLogFormattingOptions; + + public static EventLogger Empty { get; } = new EventLogger(null, null); + + public EventLogger(IEventSubscriber eventSubscriber, ILogger logger, EventLogFormattingOptions eventLogFormattingOptions = null) + { + _logger = logger; + _eventPublisher = eventSubscriber != null ? new EventPublisher(eventSubscriber) : null; + _eventLogFormattingOptions = eventLogFormattingOptions ?? new EventLogFormattingOptions(0); + } + + public ILogger Logger => _logger; + + public bool IsEventTracked() where TEvent : struct, IEvent => + Logger?.IsEnabled(GetEventVerbosity()) == true || + _eventPublisher?.IsEventTracked() == true; + + private LogLevel GetEventVerbosity() where TEvent : struct, IEvent => + StructuredLogTemplateProviders.GetTemplateProvider(new TEvent().Type).LogLevel; + + public void LogAndPublish(TEvent @event, bool skipLogging = false) where TEvent : struct, IEvent + => LogAndPublish(null, @event, skipLogging); + + public void LogAndPublish(Exception exception, TEvent @event, bool skipLogging = false) where TEvent : struct, IEvent + { + if (!skipLogging) + { + var eventTemplateProvider = StructuredLogTemplateProviders.GetTemplateProvider(@event.Type); + + if (_logger?.IsEnabled(eventTemplateProvider.LogLevel) == true) + { + var @params = eventTemplateProvider.GetParams(@event, _eventLogFormattingOptions); + var template = eventTemplateProvider.GetTemplate(@event); + + Log(eventTemplateProvider.LogLevel, template, exception, @params); + } + } + + _eventPublisher?.Publish(@event); + } + + public void LogAndPublish(TEvent @event, TArg arg) where TEvent : struct, IEvent + { + var eventTemplateProvider = StructuredLogTemplateProviders.GetTemplateProvider(@event.Type); + + if (_logger?.IsEnabled(eventTemplateProvider.LogLevel) == true) + { + var @params = eventTemplateProvider.GetParams(@event, _eventLogFormattingOptions, arg); + var template = eventTemplateProvider.GetTemplate(@event); + + Log(eventTemplateProvider.LogLevel, template, exception: null, @params); + } + + _eventPublisher?.Publish(@event); + } + + private void Log(LogLevel logLevel, string template, Exception exception, object[] @params) + { + switch (logLevel) + { + case LogLevel.Trace: _logger.LogTrace(exception, template, @params); break; + case LogLevel.Debug: _logger.LogDebug(exception, template, @params); break; + case LogLevel.Information: _logger.LogInformation(exception, template, @params); break; + case LogLevel.Warning: _logger.LogWarning(exception, template, @params); break; + case LogLevel.Error: _logger.LogError(exception, template, @params); break; + case LogLevel.Critical: _logger.LogCritical(exception, template, @params); break; + default: throw new ArgumentOutOfRangeException(nameof(logLevel), logLevel, "Unsupported log level."); + } + } + } +} diff --git a/src/MongoDB.Driver.Core/Core/Logging/LogCategories.cs b/src/MongoDB.Driver.Core/Core/Logging/LogCategories.cs new file mode 100644 index 00000000000..2647d6ed5d4 --- /dev/null +++ b/src/MongoDB.Driver.Core/Core/Logging/LogCategories.cs @@ -0,0 +1,30 @@ +/* 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. +*/ + +namespace MongoDB.Driver.Core.Logging +{ + internal static class LogCategories + { + public abstract class EventCategory { } + + public sealed class Command : EventCategory { } + + public sealed class Connection : EventCategory { } + + public sealed class SDAM : EventCategory { } + + public sealed class ServerSelection : EventCategory { } + } +} diff --git a/src/MongoDB.Driver.Core/Core/Logging/LogCategoryHelper.cs b/src/MongoDB.Driver.Core/Core/Logging/LogCategoryHelper.cs new file mode 100644 index 00000000000..e24e960713f --- /dev/null +++ b/src/MongoDB.Driver.Core/Core/Logging/LogCategoryHelper.cs @@ -0,0 +1,86 @@ +/* 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.Concurrent; +using System.Collections.Generic; +using System.Linq; +using MongoDB.Driver.Core.Misc; + +namespace MongoDB.Driver.Core.Logging +{ + internal static class LogCategoryHelper + { + private static readonly IDictionary __catergories = new ConcurrentDictionary(); + + private static readonly string[] __driverNamespaces = new [] + { + "MongoDB.Bson", + "MongoDB.Driver", + "MongoDB.Driver.Core" + }; + + private static readonly string[] __driverTestsNamespaces = new[] + { + "MongoDB.Bson.Tests", + "MongoDB.Bson.TestHelpers", + "MongoDB.Driver.Tests", + "MongoDB.Driver.TestHelpers", + "MongoDB.Driver.Core.Tests", + "MongoDB.Driver.Core.TestHelpers" + }; + + private static readonly string __specCategoryPrefix = typeof(LogCategories).FullName; + + private const string PrefixSpec = "MongoDB"; + private const string PrefixInternal = "MongoDB.Internal"; + private const string PrefixTests = "MongoDB.Tests"; + + public static string DecorateCategoryName(string categoryName) + { + Ensure.IsNotNullOrEmpty(categoryName, nameof(categoryName)); + + var prefixOverride = categoryName switch + { + _ when categoryName.StartsWith(__specCategoryPrefix) => PrefixSpec, + _ when __driverTestsNamespaces.Any(n => categoryName.StartsWith(n)) => PrefixTests, + _ when __driverNamespaces.Any(n => categoryName.StartsWith(n)) => PrefixInternal, + _ => null, + }; + + var result = categoryName; + + if (prefixOverride != null) + { + var pathComponents = categoryName.Split('.'); + result = $"{prefixOverride}.{pathComponents.Last()}"; + } + + return result; + } + + public static string GetCategoryName() where T : LogCategories.EventCategory + { + var type = typeof(T); + if (!__catergories.TryGetValue(type, out var result)) + { + result = DecorateCategoryName(type.FullName.Replace('+', '.')); + __catergories.Add(type, result); + } + + return result; + } + } +} diff --git a/src/MongoDB.Driver.Core/Core/Logging/LoggerExtensions.cs b/src/MongoDB.Driver.Core/Core/Logging/LoggerExtensions.cs new file mode 100644 index 00000000000..53ec7a56644 --- /dev/null +++ b/src/MongoDB.Driver.Core/Core/Logging/LoggerExtensions.cs @@ -0,0 +1,66 @@ +/* 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 Microsoft.Extensions.Logging; +using MongoDB.Driver.Core.Clusters; +using MongoDB.Driver.Core.Events; +using MongoDB.Driver.Core.Servers; +using static MongoDB.Driver.Core.Logging.StructuredLogTemplateProviders; + +namespace MongoDB.Driver.Core.Logging +{ + internal static class LoggerExtensions + { + public static EventLogger ToEventLogger(this ILogger logger, IEventSubscriber eventSubscriber) + where T : LogCategories.EventCategory => + new EventLogger(eventSubscriber, logger); + + public static EventLogger ToEventLogger(this IEventSubscriber eventSubscriber) + where T : LogCategories.EventCategory => + new EventLogger(eventSubscriber, null); + + public static void LogDebug(this ILogger logger, ClusterId clusterId, string message) + { + if (logger.IsEnabled(LogLevel.Debug)) + { + logger.LogDebug(ClusterId_Message, GetParams(clusterId, message)); + } + } + + public static void LogDebug(this ILogger logger, string format, ClusterId clusterId, string message, object arg1) + { + if (logger.IsEnabled(LogLevel.Debug)) + { + logger.LogDebug(format, GetParams(clusterId, message, arg1)); + } + } + + public static void LogDebug(this ILogger logger, ServerId serverId, string message) + { + if (logger.IsEnabled(LogLevel.Debug)) + { + logger.LogDebug(ServerId_Message, GetParams(serverId, message)); + } + } + + public static void LogDebug(this ILogger logger, string format, ServerId serverId, string message, object arg1) + { + if (logger.IsEnabled(LogLevel.Debug)) + { + logger.LogDebug(format, GetParams(serverId, message, arg1)); + } + } + } +} diff --git a/src/MongoDB.Driver.Core/Core/Logging/LoggerFactoryCategoryDecorator.cs b/src/MongoDB.Driver.Core/Core/Logging/LoggerFactoryCategoryDecorator.cs new file mode 100644 index 00000000000..33e88f56732 --- /dev/null +++ b/src/MongoDB.Driver.Core/Core/Logging/LoggerFactoryCategoryDecorator.cs @@ -0,0 +1,42 @@ +/* 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 Microsoft.Extensions.Logging; +using MongoDB.Driver.Core.Configuration; +using MongoDB.Driver.Core.Misc; + +namespace MongoDB.Driver.Core.Logging +{ + internal sealed class LoggerFactoryCategoryDecorator : ILoggerFactory + { + private readonly ILoggerFactory _loggerFactory; + private readonly LoggingSettings _loggingSettings; + + public LoggerFactoryCategoryDecorator(ILoggerFactory loggerFactory, LoggingSettings loggingSettings) + { + _loggerFactory = Ensure.IsNotNull(loggerFactory, nameof(loggerFactory)); + _loggingSettings = Ensure.IsNotNull(loggingSettings, nameof(loggingSettings)); + } + + public LoggingSettings LoggingSettings => _loggingSettings; + + public void AddProvider(ILoggerProvider provider) => _loggerFactory.AddProvider(provider); + + public ILogger CreateLogger(string categoryName) => + _loggerFactory.CreateLogger(LogCategoryHelper.DecorateCategoryName(categoryName)); + + public void Dispose() => _loggerFactory.Dispose(); + } +} diff --git a/src/MongoDB.Driver.Core/Core/Logging/LoggerFactoryExtensions.cs b/src/MongoDB.Driver.Core/Core/Logging/LoggerFactoryExtensions.cs new file mode 100644 index 00000000000..14ac08dfbe4 --- /dev/null +++ b/src/MongoDB.Driver.Core/Core/Logging/LoggerFactoryExtensions.cs @@ -0,0 +1,32 @@ +/* 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 Microsoft.Extensions.Logging; +using MongoDB.Driver.Core.Events; + +namespace MongoDB.Driver.Core.Logging +{ + internal static class LoggerFactoryExtensions + { + public static EventLogger CreateEventLogger(this ILoggerFactory loggerFactory, IEventSubscriber eventSubscriber) + where T : LogCategories.EventCategory + { + var loggingSettings = (loggerFactory as LoggerFactoryCategoryDecorator)?.LoggingSettings; + var eventLogFormattingOptions = loggingSettings != null ? new EventLogFormattingOptions(loggingSettings.MaxDocumentSize) : null; + + return new EventLogger(eventSubscriber, loggerFactory?.CreateLogger(), eventLogFormattingOptions); + } + } +} diff --git a/src/MongoDB.Driver.Core/Core/Logging/LoggingSettingsExtensions.cs b/src/MongoDB.Driver.Core/Core/Logging/LoggingSettingsExtensions.cs new file mode 100644 index 00000000000..43fcf34816c --- /dev/null +++ b/src/MongoDB.Driver.Core/Core/Logging/LoggingSettingsExtensions.cs @@ -0,0 +1,37 @@ +/* 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 Microsoft.Extensions.Logging; +using MongoDB.Driver.Core.Configuration; + +namespace MongoDB.Driver.Core.Logging +{ + internal static class LoggingSettingsExtensions + { + public static ILoggerFactory ToInternalLoggerFactory(this LoggingSettings loggingSettings) => + loggingSettings?.LoggerFactory switch + { + _ when loggingSettings?.LoggerFactory != null => new LoggerFactoryCategoryDecorator(loggingSettings.LoggerFactory, loggingSettings), + _ => null + }; + + public static ILogger CreateLogger(this LoggingSettings loggingSettings) => + loggingSettings?.LoggerFactory switch + { + _ when loggingSettings?.LoggerFactory != null => new LoggerFactoryCategoryDecorator(loggingSettings.LoggerFactory, loggingSettings).CreateLogger(), + _ => null + }; + } +} diff --git a/src/MongoDB.Driver.Core/Core/Logging/StructuredLogTemplateProviders.cs b/src/MongoDB.Driver.Core/Core/Logging/StructuredLogTemplateProviders.cs new file mode 100644 index 00000000000..371e0ee63c2 --- /dev/null +++ b/src/MongoDB.Driver.Core/Core/Logging/StructuredLogTemplateProviders.cs @@ -0,0 +1,229 @@ +/* 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 System.Net; +using Microsoft.Extensions.Logging; +using MongoDB.Driver.Core.Clusters; +using MongoDB.Driver.Core.Connections; +using MongoDB.Driver.Core.Events; +using MongoDB.Driver.Core.Misc; +using MongoDB.Driver.Core.Servers; + +namespace MongoDB.Driver.Core.Logging +{ + internal static partial class StructuredLogTemplateProviders + { + public const string ClusterId = nameof(ClusterId); + public const string Command = nameof(Command); + public const string CommandName = nameof(CommandName); + public const string DatabaseName = nameof(DatabaseName); + public const string Description = nameof(Description); + public const string DriverConnectionId = nameof(DriverConnectionId); + public const string DurationMS = nameof(DurationMS); + public const string Failure = nameof(Failure); + public const string Error = nameof(Error); + public const string MaxConnecting = nameof(MaxConnecting); + public const string MaxIdleTimeMS = nameof(MaxIdleTimeMS); + public const string MaxPoolSize = nameof(MaxPoolSize); + public const string Message = nameof(Message); + public const string MinPoolSize = nameof(MinPoolSize); + public const string OperationId = nameof(OperationId); + public const string RequestId = nameof(RequestId); + public const string Reply = nameof(Reply); + public const string Reason = nameof(Reason); + public const string ServerHost = nameof(ServerHost); + public const string ServerPort = nameof(ServerPort); + public const string ServerConnectionId = nameof(ServerConnectionId); + public const string ServiceId = nameof(ServiceId); + public const string SharedLibraryVersion = nameof(SharedLibraryVersion); + public const string WaitQueueTimeoutMS = nameof(WaitQueueTimeoutMS); + public const string WaitQueueSize = nameof(WaitQueueSize); + + public const string ClusterId_Message = $"{{{ClusterId}}} {{{Message}}}"; + public const string DriverConnectionId_Message = $"{{{DriverConnectionId}}} {{{Message}}}"; + public const string ServerId_Message = $"{{{ClusterId}}} {{{ServerHost}}} {{{ServerPort}}} {{{Message}}}"; + public const string ServerId_Message_Description = $"{{{ClusterId}}} {{{ServerHost}}} {{{ServerPort}}} {{{Message}}} {{{Description}}}"; + public const string ClusterId_Message_SharedLibraryVersion = $"{{{ClusterId}}} {{{Message}}} {{{SharedLibraryVersion}}}"; + + private readonly static LogTemplateProvider[] __eventTemplateProviders; + + static StructuredLogTemplateProviders() + { + var eventTypesCount = Enum.GetValues(typeof(EventType)).Length; + __eventTemplateProviders = new LogTemplateProvider[eventTypesCount]; + + AddClusterTemplates(); + AddCmapTemplates(); + AddCommandTemplates(); + AddConnectionTemplates(); + AddSdamTemplates(); + } + + public static LogTemplateProvider GetTemplateProvider(EventType eventType) => __eventTemplateProviders[(int)eventType]; + + public static object[] GetParams(ClusterId clusterId, object arg1) + { + return new object[] { clusterId.Value, arg1 }; + } + + public static object[] GetParams(ClusterId clusterId, object arg1, object arg2) + { + return new object[] { clusterId.Value, arg1, arg2 }; + } + + public static object[] GetParams(ClusterId clusterId, EndPoint endPoint, object arg1) + { + var (host, port) = endPoint.GetHostAndPort(); + + return new object[] { clusterId.Value, host, port, arg1 }; + } + + public static object[] GetParams(ServerId serverId, object arg1) + { + var (host, port) = serverId.EndPoint.GetHostAndPort(); + + return new object[] { serverId.ClusterId.Value, host, port, arg1 }; + } + + public static object[] GetParams(ServerId serverId, object arg1, object arg2) + { + var (host, port) = serverId.EndPoint.GetHostAndPort(); + + return new object[] { serverId.ClusterId.Value, host, port, arg1, arg2 }; + } + + public static object[] GetParams(ServerId serverId, object arg1, object arg2, object arg3) + { + var (host, port) = serverId.EndPoint.GetHostAndPort(); + + return new object[] { serverId.ClusterId.Value, host, port, arg1, arg2, arg3 }; + } + + public static object[] GetParams(ServerId serverId, object arg1, object arg2, object arg3, object arg4, object arg5, object arg6) + { + var (host, port) = serverId.EndPoint.GetHostAndPort(); + + return new object[] { serverId.ClusterId.Value, host, port, arg1, arg2, arg3, arg4, arg5, arg6 }; + } + + public static object[] GetParams(ServerId serverId, object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7) + { + var (host, port) = serverId.EndPoint.GetHostAndPort(); + + return new object[] { serverId.ClusterId.Value, host, port, arg1, arg2, arg3, arg4, arg5, arg6, arg7 }; + } + + public static object[] GetParams(ConnectionId connectionId, object arg1) + { + var (host, port) = connectionId.ServerId.EndPoint.GetHostAndPort(); + + return new object[] { connectionId.ServerId.ClusterId.Value, connectionId.LongLocalValue, host, port, arg1}; + } + + public static object[] GetParams(ConnectionId connectionId, object arg1, object arg2) + { + var (host, port) = connectionId.ServerId.EndPoint.GetHostAndPort(); + + return new object[] { connectionId.ServerId.ClusterId.Value, connectionId.LongLocalValue, host, port, arg1, arg2 }; + } + + public static object[] GetParamsOmitNull(ConnectionId connectionId, object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object ommitableParam) + { + var (host, port) = connectionId.ServerId.EndPoint.GetHostAndPort(); + + if (ommitableParam == null) + return new object[] { connectionId.ServerId.ClusterId.Value, connectionId.LongLocalValue, host, port, arg1, arg2, arg3, arg4, arg5, arg6, arg7, }; + else + return new object[] { connectionId.ServerId.ClusterId.Value, connectionId.LongLocalValue, host, port, arg1, arg2, arg3, arg4, arg5, arg6, arg7, ommitableParam }; + } + + private static void AddTemplateProvider(LogLevel logLevel, string template, Func extractor) where TEvent : struct, IEvent => + AddTemplateProvider(new LogTemplateProvider( + logLevel, + new[] { template }, + extractor)); + + private static void AddTemplateProvider(LogLevel logLevel, string[] templates, Func extractor, Func templateExtractor) where TEvent : struct, IEvent => + AddTemplateProvider(new LogTemplateProvider( + logLevel, + templates, + extractor, + templateExtractor)); + + private static void AddTemplate(LogLevel logLevel, string template, Func extractor) where TEvent : struct, IEvent => + AddTemplateProvider(new LogTemplateProvider( + logLevel, + new[] { template }, + extractor)); + + private static void AddTemplateProvider(LogTemplateProvider templateProvider) where TEvent : struct, IEvent + { + var index = (int)(new TEvent().Type); + + if (__eventTemplateProviders[index] != null) + { + throw new InvalidOperationException($"Template already registered for {typeof(TEvent)} event."); + } + + __eventTemplateProviders[index] = templateProvider; + } + + private static string Concat(params string[] parameters) => + string.Join(" ", parameters.Select(p => $"{{{p}}}")); + + private static string Concat(string[] parameters, params string[] additionalParameters) => + string.Join(" ", parameters.Concat(additionalParameters).Select(p => $"{{{p}}}")); + + private static string FormatException(Exception exception, EventLogFormattingOptions eventLogFormattingOptions) + { + if (exception == null) + { + return null; + } + + return TruncateIfNeeded(exception.ToString(), eventLogFormattingOptions.MaxDocumentSize); + } + + private static string TruncateIfNeeded(string str, int length) => + str.Length > length ? str.Substring(0, length) + "..." : str; + + internal sealed class LogTemplateProvider + { + public LogLevel LogLevel { get; } + public string[] Templates { get; } + public Delegate ParametersExtractor { get; } + public Delegate TemplateExtractor { get; } + + public LogTemplateProvider(LogLevel logLevel, string[] templates, Delegate parametersExtractor, Delegate templateExtractor = null) + { + LogLevel = logLevel; + Templates = templates; + ParametersExtractor = parametersExtractor; + TemplateExtractor = templateExtractor; + } + + public string GetTemplate(TEvent @event) where TEvent : struct, IEvent => + TemplateExtractor != null ? ((Func)TemplateExtractor)(@event, this) : Templates.First(); + + public object[] GetParams(TEvent @event, EventLogFormattingOptions eventLogFormattingOptions) where TEvent : struct, IEvent => + (ParametersExtractor as Func)(@event, eventLogFormattingOptions); + + public object[] GetParams(TEvent @event, EventLogFormattingOptions eventLogFormattingOptions, TArg arg) where TEvent : struct, IEvent => + (ParametersExtractor as Func)(@event, eventLogFormattingOptions, arg); + } + } +} diff --git a/src/MongoDB.Driver.Core/Core/Logging/StructuredLogTemplateProvidersCluster.cs b/src/MongoDB.Driver.Core/Core/Logging/StructuredLogTemplateProvidersCluster.cs new file mode 100644 index 00000000000..395acc9f3ab --- /dev/null +++ b/src/MongoDB.Driver.Core/Core/Logging/StructuredLogTemplateProvidersCluster.cs @@ -0,0 +1,94 @@ +/* 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 Microsoft.Extensions.Logging; +using MongoDB.Driver.Core.Events; + +namespace MongoDB.Driver.Core.Logging +{ + internal static partial class StructuredLogTemplateProviders + { + private static string[] __clusterCommonParams = new[] + { + ClusterId, + Message, + }; + + private static string ClusterCommonParams(params string[] @params) => Concat(__clusterCommonParams, @params); + + private static void AddClusterTemplates() + { + AddTemplateProvider( + LogLevel.Debug, + ClusterCommonParams(), + (e, _) => GetParams(e.ClusterId, "Description changed")); + + AddTemplateProvider( + LogLevel.Debug, + ClusterCommonParams(), + (e, _) => GetParams(e.ClusterId, "Selecting server")); + + AddTemplateProvider( + LogLevel.Debug, + ClusterCommonParams(), + (e, _) => GetParams(e.ClusterId, "Selected server")); + + AddTemplateProvider( + LogLevel.Debug, + ClusterCommonParams(), + (e, _) => GetParams(e.ClusterId, "Selecting server failed")); + + AddTemplateProvider( + LogLevel.Debug, + ClusterCommonParams(), + (e, _) => GetParams(e.ClusterId, "Cluster closing")); + + AddTemplateProvider( + LogLevel.Debug, + ClusterCommonParams(), + (e, _) => GetParams(e.ClusterId, "Cluster closed")); + + AddTemplateProvider( + LogLevel.Debug, + ClusterCommonParams(), + (e, _) => GetParams(e.ClusterId, "Cluster opening")); + + AddTemplateProvider( + LogLevel.Debug, + ClusterCommonParams(), + (e, _) => GetParams(e.ClusterId, "Cluster opened")); + + AddTemplateProvider( + LogLevel.Debug, + CmapCommonParams(), + (e, _) => GetParams(e.ClusterId, e.EndPoint, "Adding server")); + + AddTemplateProvider( + LogLevel.Debug, + CmapCommonParams(), + (e, _) => GetParams(e.ServerId, "Added server")); + + AddTemplateProvider( + LogLevel.Debug, + CmapCommonParams(), + (e, _) => GetParams(e.ServerId, "Removing server")); + + AddTemplateProvider( + LogLevel.Debug, + CmapCommonParams(), + (e, _) => GetParams(e.ServerId, "Removed server")); + } + } +} diff --git a/src/MongoDB.Driver.Core/Core/Logging/StructuredLogTemplateProvidersCmap.cs b/src/MongoDB.Driver.Core/Core/Logging/StructuredLogTemplateProvidersCmap.cs new file mode 100644 index 00000000000..4b9ac28fedc --- /dev/null +++ b/src/MongoDB.Driver.Core/Core/Logging/StructuredLogTemplateProvidersCmap.cs @@ -0,0 +1,144 @@ +/* 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 Microsoft.Extensions.Logging; +using MongoDB.Driver.Core.Configuration; +using MongoDB.Driver.Core.Events; + +namespace MongoDB.Driver.Core.Logging +{ + internal static partial class StructuredLogTemplateProviders + { + private static string[] __cmapCommonParams = new[] + { + ClusterId, + ServerHost, + ServerPort, + Message, + }; + + private static string CmapCommonParams(params string[] @params) => Concat(__cmapCommonParams, @params); + + private static void AddCmapTemplates() + { + AddTemplateProvider( + LogLevel.Debug, + CmapCommonParams(), + (e, _) => GetParams(e.ServerId, "Connection adding")); + + AddTemplateProvider( + LogLevel.Debug, + ConnectionCommonParams(), + (e, _) => GetParams(e.ConnectionId, "Connection checking in")); + + AddTemplateProvider( + LogLevel.Debug, + ConnectionCommonParams(), + (e, _) => GetParams(e.ConnectionId, "Connection checked in")); + + AddTemplateProvider( + LogLevel.Debug, + CmapCommonParams(), + (e, _) => GetParams(e.ServerId, "Connection checkout started")); + + AddTemplateProvider( + LogLevel.Debug, + ConnectionCommonParams(), + (e, _) => GetParams(e.ConnectionId, "Connection checked out")); + + AddTemplateProvider( + LogLevel.Debug, + CmapCommonParams(Reason, Error), + (e, o) => GetParams( + e.ServerId, + "Connection checkout failed", + GetCheckoutFailedReason(e.Reason), + FormatException(e.Exception, o))); + + AddTemplateProvider( + LogLevel.Debug, + CmapCommonParams(), + (e, _) => GetParams(e.ServerId, "Connection removing")); + + AddTemplateProvider( + LogLevel.Debug, + CmapCommonParams(), + (e, _) => GetParams(e.ServerId, "Connection removed")); + +#pragma warning disable CS0618 // Type or member is obsolete + AddTemplate( + LogLevel.Debug, + CmapCommonParams(MaxIdleTimeMS, MinPoolSize, MaxPoolSize, MaxConnecting, WaitQueueTimeoutMS, WaitQueueSize), + (e, _, s) => GetParams( + e.ServerId, + "Connection pool opening", + s.MaxIdleTime.TotalMilliseconds, + e.ConnectionPoolSettings.MinConnections, + e.ConnectionPoolSettings.MaxConnections, + e.ConnectionPoolSettings.MaxConnecting, + e.ConnectionPoolSettings.WaitQueueTimeout.TotalMilliseconds, + e.ConnectionPoolSettings.WaitQueueSize)); + + AddTemplate( + LogLevel.Debug, + CmapCommonParams(MaxIdleTimeMS, MinPoolSize, MaxPoolSize, MaxConnecting, WaitQueueTimeoutMS, WaitQueueSize), + (e, _, s) => GetParams( + e.ServerId, + "Connection pool created", + s.MaxIdleTime.TotalMilliseconds, + e.ConnectionPoolSettings.MinConnections, + e.ConnectionPoolSettings.MaxConnections, + e.ConnectionPoolSettings.MaxConnecting, + e.ConnectionPoolSettings.WaitQueueTimeout.TotalMilliseconds, + e.ConnectionPoolSettings.WaitQueueSize)); + +#pragma warning restore CS0618 // Type or member is obsolete + + AddTemplateProvider( + LogLevel.Debug, + CmapCommonParams(), + (e, _) => GetParams(e.ServerId, "Connection pool ready")); + + AddTemplateProvider( + LogLevel.Debug, + CmapCommonParams(ServiceId), + (e, _) => GetParams(e.ServerId, "Connection pool clearing", e.ServiceId)); + + AddTemplateProvider( + LogLevel.Debug, + CmapCommonParams(ServiceId), + (e, _) => GetParams(e.ServerId, "Connection pool cleared", e.ServiceId)); + + AddTemplateProvider( + LogLevel.Debug, + CmapCommonParams(), + (e, _) => GetParams(e.ServerId, "Connection pool closing")); + + AddTemplateProvider( + LogLevel.Debug, + CmapCommonParams(), + (e, _) => GetParams(e.ServerId, "Connection pool closed")); + } + + private static string GetCheckoutFailedReason(ConnectionCheckOutFailedReason connectionCheckOutFailedReason) => + connectionCheckOutFailedReason switch + { + ConnectionCheckOutFailedReason.ConnectionError => "An error occurred while trying to establish a new connection", + ConnectionCheckOutFailedReason.PoolClosed => "Connection pool was closed", + ConnectionCheckOutFailedReason.Timeout => "Wait queue timeout elapsed without a connection becoming available", + _ => null + }; + } +} diff --git a/src/MongoDB.Driver.Core/Core/Logging/StructuredLogTemplateProvidersCommand.cs b/src/MongoDB.Driver.Core/Core/Logging/StructuredLogTemplateProvidersCommand.cs new file mode 100644 index 00000000000..6df6b8a73ea --- /dev/null +++ b/src/MongoDB.Driver.Core/Core/Logging/StructuredLogTemplateProvidersCommand.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.Linq; +using Microsoft.Extensions.Logging; +using MongoDB.Bson; +using MongoDB.Driver.Core.Events; + +namespace MongoDB.Driver.Core.Logging +{ + internal static partial class StructuredLogTemplateProviders + { + private static string[] __commandCommonParams = new[] + { + ClusterId, + DriverConnectionId, + ServerHost, + ServerPort, + ServerConnectionId, + RequestId, + OperationId, + Message, + CommandName + }; + + private static string[] CommandCommonParams(params string[] @params) => new[] + { + Concat(__commandCommonParams, @params), + Concat(__commandCommonParams, @params.Concat(new[] { ServiceId }).ToArray()) + }; + + private static void AddCommandTemplates() + { + AddTemplateProvider( + LogLevel.Debug, + CommandCommonParams(DatabaseName, Command), + (e, o) => GetParamsOmitNull( + e.ConnectionId, + e.ConnectionId.LongServerValue, + e.RequestId, + e.OperationId, + "Command started", + e.CommandName, + e.DatabaseNamespace.DatabaseName, + CommandDocumentToString(e.Command, o), + ommitableParam: e.ServiceId), + (e, s) => e.ServiceId == null ? s.Templates[0] : s.Templates[1]); + + AddTemplateProvider( + LogLevel.Debug, + CommandCommonParams(DurationMS, Reply), + (e, o) => GetParamsOmitNull( + e.ConnectionId, + e.ConnectionId.LongServerValue, + e.RequestId, + e.OperationId, + "Command succeeded", + e.CommandName, + e.Duration.TotalMilliseconds, + CommandDocumentToString(e.Reply, o), + ommitableParam: e.ServiceId), + (e, s) => e.ServiceId == null ? s.Templates[0] : s.Templates[1]); + + AddTemplateProvider( + LogLevel.Debug, + CommandCommonParams(DurationMS, Failure), + (e, o) => GetParamsOmitNull( + e.ConnectionId, + e.ConnectionId.LongServerValue, + e.RequestId, + e.OperationId, + "Command failed", + e.CommandName, + e.Duration.TotalMilliseconds, + FormatCommandException(e.Failure, o), + ommitableParam: e.ServiceId), + (e, s) => e.ServiceId == null ? s.Templates[0] : s.Templates[1]); + } + + private static string CommandDocumentToString(BsonDocument document, EventLogFormattingOptions eventLogFormattingOptions) + { + if (document == null) + { + return null; + } + + return TruncateIfNeeded(document.ToString(), eventLogFormattingOptions.MaxDocumentSize); + } + + private static string FormatCommandException(Exception exception, EventLogFormattingOptions eventLogFormattingOptions) + { + if (exception == null) + { + return null; + } + + var serverResult = (exception as MongoCommandException)?.Result; + var result = serverResult != null ? $"{exception} server reply: {serverResult}" : exception.ToString(); + + return TruncateIfNeeded(result, eventLogFormattingOptions.MaxDocumentSize); + } + } +} diff --git a/src/MongoDB.Driver.Core/Core/Logging/StructuredLogTemplateProvidersConnection.cs b/src/MongoDB.Driver.Core/Core/Logging/StructuredLogTemplateProvidersConnection.cs new file mode 100644 index 00000000000..716c0d7e1ac --- /dev/null +++ b/src/MongoDB.Driver.Core/Core/Logging/StructuredLogTemplateProvidersConnection.cs @@ -0,0 +1,107 @@ +/* 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 Microsoft.Extensions.Logging; +using MongoDB.Driver.Core.Events; + +namespace MongoDB.Driver.Core.Logging +{ + internal static partial class StructuredLogTemplateProviders + { + private static string[] __connectionCommonParams = new[] + { + ClusterId, + DriverConnectionId, + ServerHost, + ServerPort, + Message, + }; + + private static string ConnectionCommonParams(params string[] @params) => Concat(__connectionCommonParams, @params); + + private static void AddConnectionTemplates() + { + AddTemplateProvider( + LogLevel.Debug, + ConnectionCommonParams(), + (e, _) => GetParams(e.ConnectionId, "Connection added")); + + AddTemplateProvider( + LogLevel.Debug, + ConnectionCommonParams(), + (e, _) => GetParams(e.ConnectionId, "Connection ready")); + + AddTemplateProvider( + LogLevel.Debug, + ConnectionCommonParams(), + (e, _) => GetParams(e.ConnectionId, "Connection opening")); + + AddTemplateProvider( + LogLevel.Debug, + ConnectionCommonParams(Reason), + (e, _) => GetParams(e.ConnectionId, "Connection opening failed", e.Exception?.ToString())); + + AddTemplateProvider( + LogLevel.Debug, + ConnectionCommonParams(), + (e, _) => GetParams(e.ConnectionId, "Connection created")); + + AddTemplateProvider( + LogLevel.Debug, + ConnectionCommonParams(Reason), + (e, _) => GetParams(e.ConnectionId, "Connection failed", e.Exception?.ToString())); + + AddTemplateProvider( + LogLevel.Debug, + ConnectionCommonParams(Reason), + (e, _) => GetParams(e.ConnectionId, "Connection closing", "Unknown")); + + AddTemplateProvider( + LogLevel.Debug, + ConnectionCommonParams(Reason), + (e, _) => GetParams(e.ConnectionId, "Connection closed", "Unknown")); + + AddTemplateProvider( + LogLevel.Trace, + ConnectionCommonParams(), + (e, _) => GetParams(e.ConnectionId, "Received")); + + AddTemplateProvider( + LogLevel.Trace, + ConnectionCommonParams(), + (e, _) => GetParams(e.ConnectionId, "Receiving")); + + AddTemplateProvider( + LogLevel.Trace, + ConnectionCommonParams(Reason), + (e, _) => GetParams(e.ConnectionId, "Receiving failed", e.Exception?.ToString())); + + AddTemplateProvider( + LogLevel.Trace, + ConnectionCommonParams(), + (e, _) => GetParams(e.ConnectionId, "Sending")); + + AddTemplateProvider( + LogLevel.Trace, + ConnectionCommonParams(Reason), + (e, _) => GetParams(e.ConnectionId, "Sending failed", e.Exception?.ToString())); + + AddTemplateProvider( + LogLevel.Trace, + ConnectionCommonParams(), + (e, _) => GetParams(e.ConnectionId, "Sent")); + } + } +} diff --git a/src/MongoDB.Driver.Core/Core/Logging/StructuredLogTemplateProvidersSdam.cs b/src/MongoDB.Driver.Core/Core/Logging/StructuredLogTemplateProvidersSdam.cs new file mode 100644 index 00000000000..b0f71c89fc0 --- /dev/null +++ b/src/MongoDB.Driver.Core/Core/Logging/StructuredLogTemplateProvidersSdam.cs @@ -0,0 +1,82 @@ +/* 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 Microsoft.Extensions.Logging; +using MongoDB.Driver.Core.Events; + +namespace MongoDB.Driver.Core.Logging +{ + internal static partial class StructuredLogTemplateProviders + { + private static string[] __sdamCommonParams = new[] + { + ClusterId, + DriverConnectionId, + ServerHost, + ServerPort, + Message, + }; + + private static string SdamCommonParams(params string[] @params) => Concat(__sdamCommonParams, @params); + + private static void AddSdamTemplates() + { + AddTemplateProvider( + LogLevel.Debug, + SdamCommonParams(), + (e, _) => GetParams(e.ConnectionId, "Heartbeat started")); + + AddTemplateProvider( + LogLevel.Debug, + SdamCommonParams(), + (e, _) => GetParams(e.ConnectionId, "Heartbeat succeeded")); + + AddTemplateProvider( + LogLevel.Debug, + SdamCommonParams(), + (e, _) => GetParams(e.ConnectionId, "Heartbeat failed")); + + AddTemplateProvider( + LogLevel.Debug, + Concat(new[] { Message }), + (e, _) => new[] { e.Message }); + + AddTemplateProvider( + LogLevel.Debug, + CmapCommonParams(), + (e, _) => GetParams(e.ServerId, "Server opening")); + + AddTemplateProvider( + LogLevel.Debug, + CmapCommonParams(), + (e, _) => GetParams(e.ServerId, "Server opened")); + + AddTemplateProvider( + LogLevel.Debug, + CmapCommonParams(), + (e, _) => GetParams(e.ServerId, "Server closing")); + + AddTemplateProvider( + LogLevel.Debug, + CmapCommonParams(), + (e, _) => GetParams(e.ServerId, "Server closed")); + + AddTemplateProvider( + LogLevel.Debug, + CmapCommonParams(Description), + (e, _) => GetParams(e.ServerId, "Description changed", e.NewDescription)); + } + } +} diff --git a/src/MongoDB.Driver.Core/Core/Misc/EndPointHelper.cs b/src/MongoDB.Driver.Core/Core/Misc/EndPointHelper.cs index 1da0831bc39..7d99df91a52 100644 --- a/src/MongoDB.Driver.Core/Core/Misc/EndPointHelper.cs +++ b/src/MongoDB.Driver.Core/Core/Misc/EndPointHelper.cs @@ -157,6 +157,9 @@ public static EndPoint Parse(string value) return endPoint; } + internal static (string Host, int Port) GetHostAndPort(this EndPoint endPoint) => + (endPoint is DnsEndPoint dnsEndPoint) ? (dnsEndPoint.Host, dnsEndPoint.Port) : default; + /// /// Returns a that represents the end point. /// diff --git a/src/MongoDB.Driver.Core/Core/Misc/Ensure.cs b/src/MongoDB.Driver.Core/Core/Misc/Ensure.cs index 9d9def7ebe4..10560a1cbd7 100644 --- a/src/MongoDB.Driver.Core/Core/Misc/Ensure.cs +++ b/src/MongoDB.Driver.Core/Core/Misc/Ensure.cs @@ -27,6 +27,22 @@ namespace MongoDB.Driver.Core.Misc [DebuggerStepThrough] public static class Ensure { + /// + /// Ensures that the value of a parameter is not null. + /// + /// Type type of the value. + /// The value of the parameter. + /// The name of the parameter. + /// The value of the parameter. + public static Nullable HasValue(Nullable value, string paramName) where T : struct + { + if (!value.HasValue) + { + throw new ArgumentException("The Nullable parameter must have a value.", paramName); + } + return value; + } + /// /// Ensures that the value of a parameter is between a minimum and a maximum value. /// @@ -65,34 +81,36 @@ public static T IsEqualTo(T value, T comparand, string paramName) } /// - /// Ensures that the value of a parameter is greater than or equal to a comparand. + /// Ensures that the value of a parameter is greater than a comparand. /// /// Type type of the value. /// The value of the parameter. /// The comparand. /// The name of the parameter. /// The value of the parameter. - public static T IsGreaterThanOrEqualTo(T value, T comparand, string paramName) where T : IComparable + public static T IsGreaterThan(T value, T comparand, string paramName) where T : IComparable { - if (value.CompareTo(comparand) < 0) + if (value.CompareTo(comparand) <= 0) { - var message = string.Format("Value is not greater than or equal to {1}: {0}.", value, comparand); + var message = $"Value is not greater than {comparand}: {value}."; throw new ArgumentOutOfRangeException(paramName, message); } return value; } /// - /// Ensures that the value of a parameter is greater than or equal to zero. + /// Ensures that the value of a parameter is greater than or equal to a comparand. /// + /// Type type of the value. /// The value of the parameter. + /// The comparand. /// The name of the parameter. /// The value of the parameter. - public static int IsGreaterThanOrEqualToZero(int value, string paramName) + public static T IsGreaterThanOrEqualTo(T value, T comparand, string paramName) where T : IComparable { - if (value < 0) + if (value.CompareTo(comparand) < 0) { - var message = string.Format("Value is not greater than or equal to 0: {0}.", value); + var message = string.Format("Value is not greater than or equal to {1}: {0}.", value, comparand); throw new ArgumentOutOfRangeException(paramName, message); } return value; @@ -104,15 +122,8 @@ public static int IsGreaterThanOrEqualToZero(int value, string paramName) /// The value of the parameter. /// The name of the parameter. /// The value of the parameter. - public static long IsGreaterThanOrEqualToZero(long value, string paramName) - { - if (value < 0) - { - var message = string.Format("Value is not greater than or equal to 0: {0}.", value); - throw new ArgumentOutOfRangeException(paramName, message); - } - return value; - } + public static int IsGreaterThanOrEqualToZero(int value, string paramName) => + IsGreaterThanOrEqualTo(value, 0, paramName); /// /// Ensures that the value of a parameter is greater than or equal to zero. @@ -120,15 +131,17 @@ public static long IsGreaterThanOrEqualToZero(long value, string paramName) /// The value of the parameter. /// The name of the parameter. /// The value of the parameter. - public static TimeSpan IsGreaterThanOrEqualToZero(TimeSpan value, string paramName) - { - if (value < TimeSpan.Zero) - { - var message = string.Format("Value is not greater than or equal to zero: {0}.", TimeSpanParser.ToString(value)); - throw new ArgumentOutOfRangeException(paramName, message); - } - return value; - } + public static long IsGreaterThanOrEqualToZero(long value, string paramName) => + IsGreaterThanOrEqualTo(value, 0, paramName); + + /// + /// Ensures that the value of a parameter is greater than or equal to zero. + /// + /// The value of the parameter. + /// The name of the parameter. + /// The value of the parameter. + public static TimeSpan IsGreaterThanOrEqualToZero(TimeSpan value, string paramName) => + IsGreaterThanOrEqualTo(value, TimeSpan.Zero, paramName); /// /// Ensures that the value of a parameter is greater than zero. @@ -136,15 +149,8 @@ public static TimeSpan IsGreaterThanOrEqualToZero(TimeSpan value, string paramNa /// The value of the parameter. /// The name of the parameter. /// The value of the parameter. - public static int IsGreaterThanZero(int value, string paramName) - { - if (value <= 0) - { - var message = string.Format("Value is not greater than zero: {0}.", value); - throw new ArgumentOutOfRangeException(paramName, message); - } - return value; - } + public static int IsGreaterThanZero(int value, string paramName) => + IsGreaterThan(value, 0, paramName); /// /// Ensures that the value of a parameter is greater than zero. @@ -152,15 +158,8 @@ public static int IsGreaterThanZero(int value, string paramName) /// The value of the parameter. /// The name of the parameter. /// The value of the parameter. - public static long IsGreaterThanZero(long value, string paramName) - { - if (value <= 0) - { - var message = string.Format("Value is not greater than zero: {0}.", value); - throw new ArgumentOutOfRangeException(paramName, message); - } - return value; - } + public static long IsGreaterThanZero(long value, string paramName) => + IsGreaterThan(value, 0, paramName); /// /// Ensures that the value of a parameter is greater than zero. @@ -168,15 +167,17 @@ public static long IsGreaterThanZero(long value, string paramName) /// The value of the parameter. /// The name of the parameter. /// The value of the parameter. - public static TimeSpan IsGreaterThanZero(TimeSpan value, string paramName) - { - if (value <= TimeSpan.Zero) - { - var message = string.Format("Value is not greater than zero: {0}.", value); - throw new ArgumentOutOfRangeException(paramName, message); - } - return value; - } + public static double IsGreaterThanZero(double value, string paramName) => + IsGreaterThan(value, 0, paramName); + + /// + /// Ensures that the value of a parameter is greater than zero. + /// + /// The value of the parameter. + /// The name of the parameter. + /// The value of the parameter. + public static TimeSpan IsGreaterThanZero(TimeSpan value, string paramName) => + IsGreaterThan(value, TimeSpan.Zero, paramName); /// /// Ensures that the value of a parameter is infinite or greater than or equal to zero. @@ -247,22 +248,6 @@ public static IEnumerable IsNotNullAndDoesNotContainAnyNulls(IEnumerable - /// Ensures that the value of a parameter is not null. - /// - /// Type type of the value. - /// The value of the parameter. - /// The name of the parameter. - /// The value of the parameter. - public static Nullable HasValue(Nullable value, string paramName) where T : struct - { - if (!value.HasValue) - { - throw new ArgumentException("The Nullable parameter must have a value.", paramName); - } - return value; - } - /// /// Ensures that the value of a parameter is not null or empty. /// @@ -298,6 +283,24 @@ public static T IsNull(T value, string paramName) where T : class return value; } + /// + /// Ensures that the value of a parameter is null or is between a minimum and a maximum value. + /// + /// Type type of the value. + /// The value of the parameter. + /// The minimum value. + /// The maximum value. + /// The name of the parameter. + /// The value of the parameter. + public static T? IsNullOrBetween(T? value, T min, T max, string paramName) where T : struct, IComparable + { + if (value != null) + { + IsBetween(value.Value, min, max, paramName); + } + return value; + } + /// /// Ensures that the value of a parameter is null or greater than or equal to zero. /// diff --git a/src/MongoDB.Driver.Core/Core/Misc/EnvironmentVariableProvider.cs b/src/MongoDB.Driver.Core/Core/Misc/EnvironmentVariableProvider.cs new file mode 100644 index 00000000000..d0d8eb83341 --- /dev/null +++ b/src/MongoDB.Driver.Core/Core/Misc/EnvironmentVariableProvider.cs @@ -0,0 +1,32 @@ +/* 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; + +namespace MongoDB.Driver.Core.Misc +{ + internal interface IEnvironmentVariableProvider + { + string GetEnvironmentVariable(string name); + } + + internal class EnvironmentVariableProvider : IEnvironmentVariableProvider + { + private static readonly IEnvironmentVariableProvider __instance = new EnvironmentVariableProvider(); + public static IEnvironmentVariableProvider Instance => __instance; + + public string GetEnvironmentVariable(string name) => Environment.GetEnvironmentVariable(name); + } +} diff --git a/src/MongoDB.Driver.Core/Core/Misc/Feature.cs b/src/MongoDB.Driver.Core/Core/Misc/Feature.cs index 62dd73f40ee..9fb626e26ca 100644 --- a/src/MongoDB.Driver.Core/Core/Misc/Feature.cs +++ b/src/MongoDB.Driver.Core/Core/Misc/Feature.cs @@ -57,10 +57,12 @@ public class Feature private static readonly Feature __createIndexCommitQuorum = new Feature("CreateIndexCommitQuorum", WireVersion.Server44); private static readonly Feature __createIndexesCommand = new Feature("CreateIndexesCommand", WireVersion.Server26); private static readonly Feature __createIndexesUsingInsertOperations = new Feature("CreateIndexesUsingInsertOperations", WireVersion.Zero, WireVersion.Server42); + private static readonly Feature __csfleRangeAlgorithm = new Feature("CsfleRangeAlgorithm", WireVersion.Server62); private static readonly Feature __csfle2 = new Feature("Csfle2", WireVersion.Server60); private static readonly Feature __currentOpCommand = new Feature("CurrentOpCommand", WireVersion.Server32); private static readonly Feature __dateOperatorsNewIn50 = new Feature("DateOperatorsNewIn50", WireVersion.Server50); private static readonly Feature __densifyStage = new Feature("DensifyStage", WireVersion.Server51); + private static readonly Feature __documentsStage = new Feature("DocumentsStage", WireVersion.Server51); private static readonly Feature __documentValidation = new Feature("DocumentValidation", WireVersion.Server32); private static readonly Feature __directConnectionSetting = new Feature("DirectConnectionSetting", WireVersion.Server44); private static readonly Feature __eval = new Feature("Eval", WireVersion.Zero, WireVersion.Server42); @@ -73,6 +75,7 @@ public class Feature private static readonly Feature __findAllowDiskUse = new Feature("FindAllowDiskUse", WireVersion.Server44); private static readonly Feature __findAndModifyWriteConcern = new Feature("FindAndModifyWriteConcern", WireVersion.Server32); private static readonly Feature __findCommand = new Feature("FindCommand", WireVersion.Server32); + private static readonly Feature __findProjectionExpressions = new Feature("FindProjectionExpressions", WireVersion.Server44); private static readonly Feature __geoNearCommand = new Feature("GeoNearCommand", WireVersion.Zero, WireVersion.Server42); private static readonly Feature __getField = new Feature("GetField", WireVersion.Server50); private static readonly Feature __getMoreComment = new Feature("GetMoreComment", WireVersion.Server44); @@ -115,7 +118,10 @@ public class Feature private static readonly Feature __speculativeAuthentication = new Feature("SpeculativeAuthentication", WireVersion.Server44); private static readonly Feature __streamingHello = new Feature("StreamingHello", WireVersion.Server44); private static readonly Feature __tailableCursor = new Feature("TailableCursor", WireVersion.Server32); + private static readonly Feature __toConversionOperators = new Feature("ToConversionOperators", WireVersion.Server40); + private static readonly Feature __trigOperators = new Feature("TrigOperators", WireVersion.Server42); private static readonly Feature __transactions = new Feature("Transactions", WireVersion.Server40); + private static readonly Feature __updateWithAggregationPipeline = new Feature("UpdateWithAggregationPipeline", WireVersion.Server42); private static readonly Feature __userManagementCommands = new Feature("UserManagementCommands", WireVersion.Server26); private static readonly Feature __views = new Feature("Views", WireVersion.Server34); private static readonly Feature __wildcardIndexes = new Feature("WildcardIndexes", WireVersion.Server42); @@ -312,6 +318,11 @@ public class Feature /// public static Feature CreateIndexesUsingInsertOperations => __createIndexesUsingInsertOperations; + /// + /// Gets the csfle range algorithm feature. + /// + public static Feature CsfleRangeAlgorithm => __csfleRangeAlgorithm; + /// /// Gets the client side field level encryption 2 feature. /// @@ -333,6 +344,11 @@ public class Feature /// public static Feature DensifyStage => __densifyStage; + /// + /// Gets the documents stage feature. + /// + public static Feature DocumentsStage => __documentsStage; + /// /// Gets the document validation feature. /// @@ -400,6 +416,11 @@ public class Feature [Obsolete("This property will be removed in a later release.")] public static Feature FindCommand => __findCommand; + /// + /// Gets the find projection expressions feature. + /// + public static Feature FindProjectionExpressions => __findProjectionExpressions; + /// /// Gets the geoNear command feature. /// @@ -640,17 +661,32 @@ public class Feature [Obsolete("This property will be removed in a later release.")] public static Feature TailableCursor => __tailableCursor; + /// + /// Gets the $toXyz conversion operators feature ($toDouble etc.). + /// + public static Feature ToConversionOperators => __toConversionOperators; + /// /// Gets the transactions feature. /// public static Feature Transactions => __transactions; + /// + /// Gets the trig operators feature. + /// + public static Feature TrigOperators => __trigOperators; + /// /// Gets the user management commands feature. /// [Obsolete("This property will be removed in a later release.")] public static Feature UserManagementCommands => __userManagementCommands; + /// + /// Gets the update with aggregation pipeline feature. + /// + public static Feature UpdateWithAggregationPipeline => __updateWithAggregationPipeline; + /// /// Gets the views feature. /// diff --git a/src/MongoDB.Driver.Core/Core/Misc/IDictionaryExtensions.cs b/src/MongoDB.Driver.Core/Core/Misc/IDictionaryExtensions.cs new file mode 100644 index 00000000000..9a99976f2d9 --- /dev/null +++ b/src/MongoDB.Driver.Core/Core/Misc/IDictionaryExtensions.cs @@ -0,0 +1,30 @@ +/* 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.Collections.Generic; + +namespace MongoDB.Driver.Core +{ + internal static class IDictionaryExtensions + { + public static void AddRange(this IDictionary dictionary, IEnumerable> values) + { + foreach (var p in values) + { + dictionary.Add(p); + } + } + } +} diff --git a/src/MongoDB.Driver.Core/Core/Misc/OperatingSystemHelper.cs b/src/MongoDB.Driver.Core/Core/Misc/OperatingSystemHelper.cs index 326e2b435d7..bd70290b0f3 100644 --- a/src/MongoDB.Driver.Core/Core/Misc/OperatingSystemHelper.cs +++ b/src/MongoDB.Driver.Core/Core/Misc/OperatingSystemHelper.cs @@ -1,4 +1,4 @@ -/* Copyright 2020-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. @@ -27,30 +27,32 @@ internal enum OperatingSystemPlatform internal static class OperatingSystemHelper { - public static OperatingSystemPlatform CurrentOperatingSystem + public static OperatingSystemPlatform CurrentOperatingSystem => __currentOperatingSystem.Value; + + private static readonly Lazy __currentOperatingSystem = new Lazy(DetermineOperatingSystemPlatform); + + private static OperatingSystemPlatform DetermineOperatingSystemPlatform() { - get - { #if NET472 - return OperatingSystemPlatform.Windows; + return OperatingSystemPlatform.Windows; #else - if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - return OperatingSystemPlatform.MacOS; - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - return OperatingSystemPlatform.Linux; - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - return OperatingSystemPlatform.Windows; - } - - // should not be reached. If we're here, then there is a bug in the library + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + return OperatingSystemPlatform.Linux; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + return OperatingSystemPlatform.MacOS; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return OperatingSystemPlatform.Windows; + } + else + { throw new PlatformNotSupportedException($"Unsupported platform '{RuntimeInformation.OSDescription}'."); -#endif } +#endif } } } diff --git a/src/MongoDB.Driver.Core/Core/Misc/WireVersion.cs b/src/MongoDB.Driver.Core/Core/Misc/WireVersion.cs index 4ad2fabce9f..b3440a1f3ea 100644 --- a/src/MongoDB.Driver.Core/Core/Misc/WireVersion.cs +++ b/src/MongoDB.Driver.Core/Core/Misc/WireVersion.cs @@ -88,6 +88,14 @@ internal static class WireVersion /// Wire version 17. /// public const int Server60 = 17; + /// + /// Wire version 18. + /// + public const int Server61 = 18; + /// + /// Wire version 19. + /// + public const int Server62 = 19; #region static private static List __knownWireVersions = new() @@ -114,10 +122,12 @@ internal static class WireVersion new WireVersionInfo(wireVersion: 14, major: 5, minor: 1), new WireVersionInfo(wireVersion: 15, major: 5, minor: 2), new WireVersionInfo(wireVersion: 16, major: 5, minor: 3), - new WireVersionInfo(wireVersion: 17, major: 6, minor: 0) + new WireVersionInfo(wireVersion: 17, major: 6, minor: 0), + new WireVersionInfo(wireVersion: 18, major: 6, minor: 1), + new WireVersionInfo(wireVersion: 19, major: 6, minor: 2) }; - private static Range __supportedWireVersionRange = CreateSupportedWireVersionRange(minWireVersion: 6, maxWireVersion: 17); + private static Range __supportedWireVersionRange = CreateSupportedWireVersionRange(minWireVersion: 6, maxWireVersion: 19); private static Range CreateSupportedWireVersionRange(int minWireVersion, int maxWireVersion) { diff --git a/src/MongoDB.Driver.Core/Core/NativeLibraryLoader/DarwinLibraryLoader.cs b/src/MongoDB.Driver.Core/Core/NativeLibraryLoader/DarwinLibraryLoader.cs deleted file mode 100644 index 5e1fd71a13c..00000000000 --- a/src/MongoDB.Driver.Core/Core/NativeLibraryLoader/DarwinLibraryLoader.cs +++ /dev/null @@ -1,66 +0,0 @@ -/* Copyright 2019-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.IO; -using System.Runtime.InteropServices; -using MongoDB.Driver.Core.Misc; - -namespace MongoDB.Driver.Core.NativeLibraryLoader -{ - internal class DarwinLibraryLoader : INativeLibraryLoader - { - // See dlfcn.h - // #define RTLD_LAZY 0x1 - // #define RTLD_NOW 0x2 - // #define RTLD_LOCAL 0x4 - // #define RTLD_GLOBAL 0x8 - private const int RTLD_GLOBAL = 0x8; - private const int RTLD_NOW = 0x2; - - private readonly IntPtr _handle; - - public DarwinLibraryLoader(string path) - { - Ensure.IsNotNullOrEmpty(path, nameof(path)); - - _handle = NativeMethods.dlopen(path, RTLD_GLOBAL | RTLD_NOW); - if (_handle == IntPtr.Zero) - { - throw new FileNotFoundException(path); - } - } - - // public methods - public IntPtr GetFunctionPointer(string name) - { - Ensure.IsNotNullOrEmpty(name, nameof(name)); - - return NativeMethods.dlsym(_handle, name); - } - - // nested types - private static class NativeMethods - { -#pragma warning disable CA2101 // Specify marshaling for P/Invoke string arguments - [DllImport("libdl", CharSet = CharSet.Ansi)] - public static extern IntPtr dlopen(string filename, int flags); - - [DllImport("libdl", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr dlsym(IntPtr handle, string symbol); -#pragma warning restore CA2101 // Specify marshaling for P/Invoke string arguments - } - } -} diff --git a/src/MongoDB.Driver.Core/Core/NativeLibraryLoader/LibraryLoader.cs b/src/MongoDB.Driver.Core/Core/NativeLibraryLoader/LibraryLoader.cs deleted file mode 100644 index 2503223507f..00000000000 --- a/src/MongoDB.Driver.Core/Core/NativeLibraryLoader/LibraryLoader.cs +++ /dev/null @@ -1,79 +0,0 @@ -/* Copyright 2019-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.Runtime.InteropServices; -using MongoDB.Driver.Core.Misc; - -namespace MongoDB.Driver.Core.NativeLibraryLoader -{ - internal class LibraryLoader - { - private readonly INativeLibraryLoader _nativeLoader; - - public LibraryLoader(ILibraryLocator libraryLocator) - { - Ensure.IsNotNull(libraryLocator, nameof(libraryLocator)); - if (!libraryLocator.IsX32ModeSupported) - { - ThrowIfNot64BitProcess(libraryLocator); - } - _nativeLoader = CreateNativeLoader(libraryLocator); - } - - // public methods - public T GetDelegate(string name) - { - IntPtr ptr = _nativeLoader.GetFunctionPointer(name); - if (ptr == IntPtr.Zero) - { - throw new TypeLoadException($"The function {name} was not found."); - } - - return Marshal.GetDelegateForFunctionPointer(ptr); - } - - // private methods - private INativeLibraryLoader CreateNativeLoader(ILibraryLocator libraryLocator) - { - var currentPlatform = OperatingSystemHelper.CurrentOperatingSystem; - var absolutePath = libraryLocator.GetLibraryAbsolutePath(currentPlatform); - return CreateNativeLoader(currentPlatform, absolutePath, libraryLocator.LibraryName); - } - - private INativeLibraryLoader CreateNativeLoader(OperatingSystemPlatform currentPlatform, string libraryPath, string libraryName) - { - switch (currentPlatform) - { - case OperatingSystemPlatform.Linux: - return new LinuxLibraryLoader(libraryPath); - case OperatingSystemPlatform.MacOS: - return new DarwinLibraryLoader(libraryPath); - case OperatingSystemPlatform.Windows: - return new WindowsLibraryLoader(libraryPath); - default: - throw new PlatformNotSupportedException($"Error loading library {libraryName}: Unexpected platform {currentPlatform}."); - } - } - - private void ThrowIfNot64BitProcess(ILibraryLocator libraryLocator) - { - if (!Environment.Is64BitProcess) - { - throw new PlatformNotSupportedException($"{libraryLocator.LibraryName} can be loaded only in a 64-bit process."); - } - } - } -} diff --git a/src/MongoDB.Driver.Core/Core/NativeLibraryLoader/LinuxLibraryLoader.cs b/src/MongoDB.Driver.Core/Core/NativeLibraryLoader/LinuxLibraryLoader.cs deleted file mode 100644 index ab78644d148..00000000000 --- a/src/MongoDB.Driver.Core/Core/NativeLibraryLoader/LinuxLibraryLoader.cs +++ /dev/null @@ -1,65 +0,0 @@ -/* Copyright 2019-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.IO; -using System.Runtime.InteropServices; -using MongoDB.Driver.Core.Misc; - -namespace MongoDB.Driver.Core.NativeLibraryLoader -{ - internal class LinuxLibraryLoader : INativeLibraryLoader - { - // See dlfcn.h - // #define RTLD_LAZY 0x1 - // #define RTLD_NOW 0x2 - // #define RTLD_LOCAL 0x4 - // #define RTLD_GLOBAL 0x100 - private const int RTLD_GLOBAL = 0x100; - private const int RTLD_NOW = 0x2; - - private readonly IntPtr _handle; - - public LinuxLibraryLoader(string path) - { - Ensure.IsNotNullOrEmpty(path, nameof(path)); - - _handle = NativeMethods.dlopen(path, RTLD_GLOBAL | RTLD_NOW); - if (_handle == IntPtr.Zero) - { - throw new FileNotFoundException(path); - } - } - - // public methods - public IntPtr GetFunctionPointer(string name) - { - Ensure.IsNotNullOrEmpty(name, nameof(name)); - - return NativeMethods.dlsym(_handle, name); - } - - // nested types - private static class NativeMethods - { -#pragma warning disable CA2101 // Specify marshaling for P/Invoke string arguments - [DllImport("libdl", CharSet = CharSet.Ansi)] - public static extern IntPtr dlopen(string filename, int flags); - [DllImport("libdl", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr dlsym(IntPtr handle, string symbol); -#pragma warning restore CA2101 // Specify marshaling for P/Invoke string arguments - } - } -} diff --git a/src/MongoDB.Driver.Core/Core/NativeLibraryLoader/PinnedBuffer.cs b/src/MongoDB.Driver.Core/Core/NativeLibraryLoader/PinnedBuffer.cs deleted file mode 100644 index 911ec574330..00000000000 --- a/src/MongoDB.Driver.Core/Core/NativeLibraryLoader/PinnedBuffer.cs +++ /dev/null @@ -1,49 +0,0 @@ -/* Copyright 2019–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.Runtime.InteropServices; - -namespace MongoDB.Driver.Core.NativeLibraryLoader -{ - internal class PinnedBuffer : IDisposable - { - private GCHandle _handle; // not readonly to prevent a temporary copy from being created when calling Free - private readonly IntPtr _intPtr; - - public PinnedBuffer(byte[] bytes, int offset) - { - // The array must be pinned by using a GCHandle before it is passed to UnsafeAddrOfPinnedArrayElement. - // For maximum performance, this method does not validate the array passed to it; this can result in unexpected behavior. - _handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); - _intPtr = Marshal.UnsafeAddrOfPinnedArrayElement(bytes, offset); - } - - public IntPtr IntPtr => _intPtr; - - // public methods - public void Dispose() - { - try - { - _handle.Free(); - } - catch - { - // ignore exceptions - } - } - } -} diff --git a/src/MongoDB.Driver.Core/Core/NativeLibraryLoader/RelativeLibraryLocatorBase.cs b/src/MongoDB.Driver.Core/Core/NativeLibraryLoader/RelativeLibraryLocatorBase.cs deleted file mode 100644 index 761a4bdef58..00000000000 --- a/src/MongoDB.Driver.Core/Core/NativeLibraryLoader/RelativeLibraryLocatorBase.cs +++ /dev/null @@ -1,88 +0,0 @@ -/* Copyright 2019-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.IO; -using System.Reflection; -using MongoDB.Driver.Core.Misc; - -namespace MongoDB.Driver.Core.NativeLibraryLoader -{ - internal abstract class RelativeLibraryLocatorBase : ILibraryLocator - { - // public properties - public virtual bool IsX32ModeSupported => false; - - public abstract string LibraryName { get; } - - // public methods - public string GetBaseAssemblyDirectory() - { - var baseAssemblyPathUri = GetBaseAssemblyUri(); - var uri = new Uri(baseAssemblyPathUri); - var absoluteAssemblyPath = Uri.UnescapeDataString(uri.AbsolutePath); - return Path.GetDirectoryName(absoluteAssemblyPath); - } - - public virtual string GetBaseAssemblyUri() => typeof(RelativeLibraryLocatorBase).GetTypeInfo().Assembly.CodeBase; - - public string GetLibraryAbsolutePath(OperatingSystemPlatform currentPlatform) - { - var relativePath = GetLibraryDirectoryRelativePath(currentPlatform); - var libraryName = GetLibraryFileName(currentPlatform); - return GetAbsolutePath(relativePath, libraryName); - } - - public virtual string GetLibraryDirectoryRelativePath(OperatingSystemPlatform currentPlatform) - { - var rid = GetCurrentPlatformRuntimeIdentifier(currentPlatform); - return Path.Combine("runtimes", rid, "native"); - } - - public abstract string GetLibraryFileName(OperatingSystemPlatform currentPlatform); - - public virtual string GetCurrentPlatformRuntimeIdentifier(OperatingSystemPlatform currentPlatform) - => currentPlatform switch - { - OperatingSystemPlatform.Windows => "win", - OperatingSystemPlatform.Linux => "linux", - OperatingSystemPlatform.MacOS => "osx", - _ => $"The provided platform {currentPlatform} is not currently supported.", - }; - - // private methods - private string GetAbsolutePath(string relativeLibraryDirectoryPath, string libraryName) - { - var libraryBasePath = GetBaseAssemblyDirectory(); - - var absolutePathsToCheck = new[] - { - Path.Combine(libraryBasePath, libraryName), // look in the current assembly folder - Path.Combine(libraryBasePath, "..", "..", relativeLibraryDirectoryPath, libraryName), // with step back on two folders - Path.Combine(libraryBasePath, relativeLibraryDirectoryPath, libraryName) - }; - - foreach (var absolutePath in absolutePathsToCheck) - { - if (File.Exists(absolutePath)) - { - return absolutePath; - } - } - - throw new FileNotFoundException($"Could not find library {libraryName}. Checked {string.Join(";", absolutePathsToCheck)}."); - } - } -} diff --git a/src/MongoDB.Driver.Core/Core/NativeLibraryLoader/WindowsLibraryLoader.cs b/src/MongoDB.Driver.Core/Core/NativeLibraryLoader/WindowsLibraryLoader.cs deleted file mode 100644 index a9849c2f68b..00000000000 --- a/src/MongoDB.Driver.Core/Core/NativeLibraryLoader/WindowsLibraryLoader.cs +++ /dev/null @@ -1,71 +0,0 @@ -/* Copyright 2019-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.Runtime.InteropServices; -using MongoDB.Driver.Core.Misc; -using MongoDB.Libmongocrypt; - -namespace MongoDB.Driver.Core.NativeLibraryLoader -{ - internal class WindowsLibraryLoader : INativeLibraryLoader - { - private readonly IntPtr _handle; - - public WindowsLibraryLoader(string path) - { - Ensure.IsNotNullOrEmpty(path, nameof(path)); - - _handle = NativeMethods.LoadLibrary(path); - if (_handle == IntPtr.Zero) - { - var gle = Marshal.GetLastWin32Error(); - - // error code 193 indicates that a 64-bit OS has tried to load a 32-bit dll - // https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499- - throw new LibraryLoadingException($"Error loading library: {path}. Windows Error code: {gle}."); - } - } - - // public methods - public IntPtr GetFunctionPointer(string name) - { - Ensure.IsNotNullOrEmpty(name, nameof(name)); - - var ptr = NativeMethods.GetProcAddress(_handle, name); - if (ptr == null) - { - var gle = Marshal.GetLastWin32Error(); - throw new TypeLoadException($"Error loading function: {name}. Windows Error code: {gle}."); - } - - return ptr; - } - - // nested types - private static class NativeMethods - { -#pragma warning disable CA2101 - [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Ansi)] - public static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)]string lpFileName); -#pragma warning restore CA2101 - -#pragma warning disable CA2101 - [DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)] - public static extern IntPtr GetProcAddress(IntPtr hModule, string procName); -#pragma warning restore CA2101 - } - } -} diff --git a/src/MongoDB.Driver.Core/Core/Servers/DefaultServer.cs b/src/MongoDB.Driver.Core/Core/Servers/DefaultServer.cs index 76050194c00..e41d94a8ad2 100644 --- a/src/MongoDB.Driver.Core/Core/Servers/DefaultServer.cs +++ b/src/MongoDB.Driver.Core/Core/Servers/DefaultServer.cs @@ -19,12 +19,13 @@ using System.Linq; using System.Net; using System.Net.Sockets; +using Microsoft.Extensions.Logging; using MongoDB.Bson; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.ConnectionPools; using MongoDB.Driver.Core.Connections; -using MongoDB.Driver.Core.Events; +using MongoDB.Driver.Core.Logging; using MongoDB.Driver.Core.Misc; namespace MongoDB.Driver.Core.Servers @@ -58,8 +59,8 @@ public DefaultServer( EndPoint endPoint, IConnectionPoolFactory connectionPoolFactory, IServerMonitorFactory monitorFactory, - IEventSubscriber eventSubscriber, - ServerApi serverApi) + ServerApi serverApi, + EventLogger eventLogger) : base( clusterId, clusterClock, @@ -69,8 +70,8 @@ public DefaultServer( settings, endPoint, connectionPoolFactory, - eventSubscriber, - serverApi) + serverApi, + eventLogger) { _monitor = Ensure.IsNotNull(monitorFactory, nameof(monitorFactory)).Create(ServerId, endPoint); _baseDescription = _currentDescription = new ServerDescription(ServerId, endPoint, reasonChanged: "ServerInitialDescription", heartbeatInterval: settings.HeartbeatInterval); @@ -171,6 +172,16 @@ protected override void Invalidate(string reasonInvalidated, bool clearConnectio $"InvalidatedBecause:{reasonInvalidated}", lastUpdateTimestamp: DateTime.UtcNow, topologyVersion: topologyVersion); + + var (host, port) = ServerId.EndPoint.GetHostAndPort(); + EventLogger.Logger?.LogDebug( + StructuredLogTemplateProviders.ServerId_Message_Description, + ServerId.ClusterId.Value, + host, + port, + newDescription, + "Invalidating description"); + SetDescription(newDescription, clearConnectionPool); // TODO: make the heartbeat request conditional so we adhere to this part of the spec // > Network error when reading or writing: ... Clients MUST NOT request an immediate check of the server; diff --git a/src/MongoDB.Driver.Core/Core/Servers/LoadBalancedServer.cs b/src/MongoDB.Driver.Core/Core/Servers/LoadBalancedServer.cs index 4d2e994228a..458c5400de9 100644 --- a/src/MongoDB.Driver.Core/Core/Servers/LoadBalancedServer.cs +++ b/src/MongoDB.Driver.Core/Core/Servers/LoadBalancedServer.cs @@ -16,11 +16,13 @@ using System; using System.Net; using System.Threading; +using Microsoft.Extensions.Logging; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.ConnectionPools; using MongoDB.Driver.Core.Connections; using MongoDB.Driver.Core.Events; +using MongoDB.Driver.Core.Logging; namespace MongoDB.Driver.Core.Servers { @@ -36,8 +38,8 @@ public LoadBalancedServer( ServerSettings serverSettings, EndPoint endPoint, IConnectionPoolFactory connectionPoolFactory, - IEventSubscriber eventSubscriber, - ServerApi serverApi) + ServerApi serverApi, + EventLogger eventLogger) : base( clusterId, clusterClock, @@ -49,8 +51,8 @@ public LoadBalancedServer( serverSettings, endPoint, connectionPoolFactory, - eventSubscriber, - serverApi) + serverApi, + eventLogger) { _baseDescription = _currentDescription = new ServerDescription(ServerId, endPoint, reasonChanged: "ServerInitialDescription"); } diff --git a/src/MongoDB.Driver.Core/Core/Servers/RoundTripTimeMonitor.cs b/src/MongoDB.Driver.Core/Core/Servers/RoundTripTimeMonitor.cs index 499f9bad24c..f7471e46584 100644 --- a/src/MongoDB.Driver.Core/Core/Servers/RoundTripTimeMonitor.cs +++ b/src/MongoDB.Driver.Core/Core/Servers/RoundTripTimeMonitor.cs @@ -17,7 +17,9 @@ using System.Diagnostics; using System.Net; using System.Threading; +using Microsoft.Extensions.Logging; using MongoDB.Driver.Core.Connections; +using MongoDB.Driver.Core.Logging; using MongoDB.Driver.Core.Misc; namespace MongoDB.Driver.Core.Servers @@ -44,13 +46,15 @@ internal sealed class RoundTripTimeMonitor : IRoundTripTimeMonitor private Thread _roundTripTimeMonitorThread; private readonly ServerApi _serverApi; private readonly ServerId _serverId; + private readonly ILogger _logger; public RoundTripTimeMonitor( IConnectionFactory connectionFactory, ServerId serverId, EndPoint endpoint, TimeSpan heartbeatInterval, - ServerApi serverApi) + ServerApi serverApi, + ILogger logger) { _connectionFactory = Ensure.IsNotNull(connectionFactory, nameof(connectionFactory)); _serverId = Ensure.IsNotNull(serverId, nameof(serverId)); @@ -59,6 +63,8 @@ public RoundTripTimeMonitor( _serverApi = serverApi; _cancellationTokenSource = new CancellationTokenSource(); _cancellationToken = _cancellationTokenSource.Token; + + _logger = logger; } public TimeSpan Average @@ -77,11 +83,15 @@ public void Dispose() { if (!_disposed) { + _logger?.LogDebug(_serverId, "Disposing"); + _disposed = true; _cancellationTokenSource.Cancel(); _cancellationTokenSource.Dispose(); try { _roundTripTimeConnection?.Dispose(); } catch { } + + _logger?.LogDebug(_serverId, "Disposed"); } } @@ -106,6 +116,8 @@ void ThreadStart() // private methods private void MonitorServer() { + _logger?.LogDebug(_serverId, "Monitoring started"); + var helloOk = false; while (!_cancellationToken.IsCancellationRequested) { @@ -127,7 +139,7 @@ private void MonitorServer() helloOk = helloResult.HelloOk; } } - catch (Exception) + catch (Exception ex) { IConnection toDispose; lock (_lock) @@ -135,7 +147,10 @@ private void MonitorServer() toDispose = _roundTripTimeConnection; _roundTripTimeConnection = null; } + var connectionId = toDispose?.ConnectionId; toDispose?.Dispose(); + + _logger?.LogDebug(ex, StructuredLogTemplateProviders.DriverConnectionId_Message, connectionId?.LongLocalValue, "Monitoring exception"); } ThreadHelper.Sleep(_heartbeatInterval, _cancellationToken); } diff --git a/src/MongoDB.Driver.Core/Core/Servers/Server.cs b/src/MongoDB.Driver.Core/Core/Servers/Server.cs index 3b16ad2741b..478f5677a48 100644 --- a/src/MongoDB.Driver.Core/Core/Servers/Server.cs +++ b/src/MongoDB.Driver.Core/Core/Servers/Server.cs @@ -28,6 +28,7 @@ using MongoDB.Driver.Core.ConnectionPools; using MongoDB.Driver.Core.Connections; using MongoDB.Driver.Core.Events; +using MongoDB.Driver.Core.Logging; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.WireProtocol; using MongoDB.Driver.Core.WireProtocol.Messages; @@ -53,15 +54,10 @@ internal abstract class Server : IClusterableServer, IConnectionExceptionHandler private readonly ServerSettings _settings; private readonly InterlockedInt32 _state; private readonly ServerApi _serverApi; + private readonly EventLogger _eventLogger; private int _outstandingOperationsCount; - private readonly Action _openingEventHandler; - private readonly Action _openedEventHandler; - private readonly Action _closingEventHandler; - private readonly Action _closedEventHandler; - private readonly Action _descriptionChangedEventHandler; - // constructors public Server( ClusterId clusterId, @@ -74,8 +70,8 @@ public Server( ServerSettings settings, EndPoint endPoint, IConnectionPoolFactory connectionPoolFactory, - IEventSubscriber eventSubscriber, - ServerApi serverApi) + ServerApi serverApi, + EventLogger eventLogger) { ClusterConnectionModeHelper.EnsureConnectionModeValuesAreValid(clusterConnectionMode, connectionModeSwitch, directConnection); @@ -85,7 +81,6 @@ public Server( _directConnection = directConnection; _settings = Ensure.IsNotNull(settings, nameof(settings)); _endPoint = Ensure.IsNotNull(endPoint, nameof(endPoint)); - Ensure.IsNotNull(eventSubscriber, nameof(eventSubscriber)); _serverId = new ServerId(clusterId, endPoint); _connectionPool = Ensure.IsNotNull(connectionPoolFactory, nameof(connectionPoolFactory)).CreateConnectionPool(_serverId, endPoint, this); @@ -93,11 +88,7 @@ public Server( _serverApi = serverApi; _outstandingOperationsCount = 0; - eventSubscriber.TryGetEventHandler(out _openingEventHandler); - eventSubscriber.TryGetEventHandler(out _openedEventHandler); - eventSubscriber.TryGetEventHandler(out _closingEventHandler); - eventSubscriber.TryGetEventHandler(out _closedEventHandler); - eventSubscriber.TryGetEventHandler(out _descriptionChangedEventHandler); + _eventLogger = Ensure.IsNotNull(eventLogger, nameof(eventLogger)); } // events @@ -110,6 +101,7 @@ public Server( public EndPoint EndPoint => _endPoint; public bool IsInitialized => _state.Value != State.Initial; public ServerId ServerId => _serverId; + protected EventLogger EventLogger => _eventLogger; int IClusterableServer.OutstandingOperationsCount => Interlocked.CompareExchange(ref _outstandingOperationsCount, 0, 0); @@ -118,10 +110,7 @@ public void Dispose() { if (_state.TryChange(State.Disposed)) { - if (_closingEventHandler != null) - { - _closingEventHandler(new ServerClosingEvent(_serverId)); - } + _eventLogger.LogAndPublish(new ServerClosingEvent(_serverId)); var stopwatch = Stopwatch.StartNew(); @@ -130,10 +119,7 @@ public void Dispose() _connectionPool.Dispose(); stopwatch.Stop(); - if (_closedEventHandler != null) - { - _closedEventHandler(new ServerClosedEvent(_serverId, stopwatch.Elapsed)); - } + _eventLogger.LogAndPublish(new ServerClosedEvent(_serverId, stopwatch.Elapsed)); } } @@ -181,20 +167,14 @@ public void Initialize() { if (_state.TryChange(State.Initial, State.Open)) { - if (_openingEventHandler != null) - { - _openingEventHandler(new ServerOpeningEvent(_serverId, _settings)); - } + _eventLogger.LogAndPublish(new ServerOpeningEvent(_serverId, _settings)); var stopwatch = Stopwatch.StartNew(); _connectionPool.Initialize(); InitializeSubClass(); stopwatch.Stop(); - if (_openedEventHandler != null) - { - _openedEventHandler(new ServerOpenedEvent(_serverId, _settings, stopwatch.Elapsed)); - } + _eventLogger.LogAndPublish(new ServerOpenedEvent(_serverId, _settings, stopwatch.Elapsed)); } } @@ -242,10 +222,9 @@ protected bool IsDirectConnection() protected void TriggerServerDescriptionChanged(object sender, ServerDescriptionChangedEventArgs e) { - var shouldServerDescriptionChangedEventBePublished = !e.OldServerDescription.SdamEquals(e.NewServerDescription); - if (shouldServerDescriptionChangedEventBePublished && _descriptionChangedEventHandler != null) + if (!e.OldServerDescription.SdamEquals(e.NewServerDescription)) { - _descriptionChangedEventHandler(new ServerDescriptionChangedEvent(e.OldServerDescription, e.NewServerDescription)); + _eventLogger.LogAndPublish(new ServerDescriptionChangedEvent(e.OldServerDescription, e.NewServerDescription)); } var handler = DescriptionChanged; diff --git a/src/MongoDB.Driver.Core/Core/Servers/ServerFactory.cs b/src/MongoDB.Driver.Core/Core/Servers/ServerFactory.cs index 0dc29ab5eed..b8925339c52 100644 --- a/src/MongoDB.Driver.Core/Core/Servers/ServerFactory.cs +++ b/src/MongoDB.Driver.Core/Core/Servers/ServerFactory.cs @@ -14,10 +14,12 @@ */ using System.Net; +using Microsoft.Extensions.Logging; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.ConnectionPools; using MongoDB.Driver.Core.Events; +using MongoDB.Driver.Core.Logging; using MongoDB.Driver.Core.Misc; namespace MongoDB.Driver.Core.Servers @@ -35,6 +37,7 @@ internal class ServerFactory : IClusterableServerFactory private readonly IEventSubscriber _eventSubscriber; private readonly ServerApi _serverApi; private readonly ServerSettings _settings; + private readonly ILoggerFactory _loggerFactory; // constructors public ServerFactory( @@ -47,7 +50,8 @@ public ServerFactory( IConnectionPoolFactory connectionPoolFactory, IServerMonitorFactory serverMonitoryFactory, IEventSubscriber eventSubscriber, - ServerApi serverApi) + ServerApi serverApi, + ILoggerFactory loggerFactory) { ClusterConnectionModeHelper.EnsureConnectionModeValuesAreValid(clusterConnectionMode, connectionModeSwitch, directConnection); @@ -59,6 +63,7 @@ public ServerFactory( _serverMonitorFactory = Ensure.IsNotNull(serverMonitoryFactory, nameof(serverMonitoryFactory)); _eventSubscriber = Ensure.IsNotNull(eventSubscriber, nameof(eventSubscriber)); _serverApi = serverApi; // can be null + _loggerFactory = loggerFactory; } // methods @@ -73,8 +78,8 @@ public IClusterableServer CreateServer(ClusterType clusterType, ClusterId cluste _settings, endPoint, _connectionPoolFactory, - _eventSubscriber, - _serverApi), + _serverApi, + _loggerFactory.CreateEventLogger(_eventSubscriber)), _ => new DefaultServer( @@ -87,8 +92,8 @@ public IClusterableServer CreateServer(ClusterType clusterType, ClusterId cluste endPoint, _connectionPoolFactory, _serverMonitorFactory, - _eventSubscriber, - _serverApi) + _serverApi, + _loggerFactory.CreateEventLogger(_eventSubscriber)) }; } } diff --git a/src/MongoDB.Driver.Core/Core/Servers/ServerMonitor.cs b/src/MongoDB.Driver.Core/Core/Servers/ServerMonitor.cs index 79afe1566e8..3f07c6ae7d9 100644 --- a/src/MongoDB.Driver.Core/Core/Servers/ServerMonitor.cs +++ b/src/MongoDB.Driver.Core/Core/Servers/ServerMonitor.cs @@ -17,9 +17,11 @@ using System.Diagnostics; using System.Net; using System.Threading; +using Microsoft.Extensions.Logging; using MongoDB.Bson; using MongoDB.Driver.Core.Connections; using MongoDB.Driver.Core.Events; +using MongoDB.Driver.Core.Logging; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.WireProtocol; @@ -35,6 +37,8 @@ internal sealed class ServerMonitor : IServerMonitor private readonly EndPoint _endPoint; private HeartbeatDelay _heartbeatDelay; private readonly object _lock = new object(); + private readonly EventLogger _eventLoggerSdam; + private readonly ILogger _logger; private readonly CancellationToken _monitorCancellationToken; // used to cancel the entire monitor private readonly CancellationTokenSource _monitorCancellationTokenSource; // used to cancel the entire monitor private readonly IRoundTripTimeMonitor _roundTripTimeMonitor; @@ -45,11 +49,6 @@ internal sealed class ServerMonitor : IServerMonitor private Thread _serverMonitorThread; - private readonly Action _heartbeatStartedEventHandler; - private readonly Action _heartbeatSucceededEventHandler; - private readonly Action _heartbeatFailedEventHandler; - private readonly Action _sdamInformationEventHandler; - public event EventHandler DescriptionChanged; public ServerMonitor( @@ -58,7 +57,8 @@ public ServerMonitor( IConnectionFactory connectionFactory, ServerMonitorSettings serverMonitorSettings, IEventSubscriber eventSubscriber, - ServerApi serverApi) + ServerApi serverApi, + ILoggerFactory loggerFactory) : this( serverId, endPoint, @@ -70,8 +70,10 @@ public ServerMonitor( serverId, endPoint, Ensure.IsNotNull(serverMonitorSettings, nameof(serverMonitorSettings)).HeartbeatInterval, - serverApi), - serverApi) + serverApi, + loggerFactory?.CreateLogger()), + serverApi, + loggerFactory) { } @@ -82,7 +84,8 @@ public ServerMonitor( ServerMonitorSettings serverMonitorSettings, IEventSubscriber eventSubscriber, IRoundTripTimeMonitor roundTripTimeMonitor, - ServerApi serverApi) + ServerApi serverApi, + ILoggerFactory loggerFactory) { _monitorCancellationTokenSource = new CancellationTokenSource(); _serverId = Ensure.IsNotNull(serverId, nameof(serverId)); @@ -95,14 +98,13 @@ public ServerMonitor( _roundTripTimeMonitor = Ensure.IsNotNull(roundTripTimeMonitor, nameof(roundTripTimeMonitor)); _state = new InterlockedInt32(State.Initial); - eventSubscriber.TryGetEventHandler(out _heartbeatStartedEventHandler); - eventSubscriber.TryGetEventHandler(out _heartbeatSucceededEventHandler); - eventSubscriber.TryGetEventHandler(out _heartbeatFailedEventHandler); - eventSubscriber.TryGetEventHandler(out _sdamInformationEventHandler); _serverApi = serverApi; _monitorCancellationToken = _monitorCancellationTokenSource.Token; _heartbeatCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(_monitorCancellationToken); + + _logger = loggerFactory?.CreateLogger(); + _eventLoggerSdam = loggerFactory.CreateEventLogger(eventSubscriber); } public ServerDescription Description => Interlocked.CompareExchange(ref _currentDescription, null, null); @@ -133,6 +135,8 @@ public void Dispose() { if (_state.TryChange(State.Disposed)) { + _logger?.LogDebug(_serverId, "Disposing"); + _monitorCancellationTokenSource.Cancel(); _monitorCancellationTokenSource.Dispose(); if (_connection != null) @@ -140,6 +144,8 @@ public void Dispose() _connection.Dispose(); } _roundTripTimeMonitor.Dispose(); + + _logger?.LogDebug(_serverId, "Disposed"); } } @@ -147,9 +153,13 @@ public void Initialize() { if (_state.TryChange(State.Initial, State.Open)) { + _logger?.LogDebug(_serverId, "Initializing"); + _roundTripTimeMonitor.Start(); _serverMonitorThread = new Thread(new ParameterizedThreadStart(ThreadStart)) { IsBackground = true }; _serverMonitorThread.Start(_monitorCancellationToken); + + _logger?.LogDebug(_serverId, "Initialized"); } void ThreadStart(object monitorCancellationToken) @@ -237,22 +247,10 @@ private void MonitorServer(CancellationToken monitorCancellationToken) catch (Exception unexpectedException) { // if we catch an exception here it's because of a bug in the driver (but we need to defend ourselves against that) - - var handler = _sdamInformationEventHandler; - if (handler != null) - { - try - { - handler.Invoke(new SdamInformationEvent(() => - string.Format( - "Unexpected exception in ServerMonitor.MonitorServer: {0}", - unexpectedException.ToString()))); - } - catch - { - // ignore any exceptions thrown by the handler (note: event handlers aren't supposed to throw exceptions) - } - } + _eventLoggerSdam.LogAndPublish(new SdamInformationEvent( + "Unexpected exception in ServerMonitor.MonitorServer: {0}", + unexpectedException), + unexpectedException); // since an unexpected exception was thrown set the server description to Unknown (with the unexpected exception) try @@ -427,10 +425,8 @@ private HelloResult GetHelloResult( CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - if (_heartbeatStartedEventHandler != null) - { - _heartbeatStartedEventHandler(new ServerHeartbeatStartedEvent(connection.ConnectionId, connection.Description.HelloResult.TopologyVersion != null)); - } + + _eventLoggerSdam.LogAndPublish(new ServerHeartbeatStartedEvent(connection.ConnectionId, connection.Description.HelloResult.TopologyVersion != null)); try { @@ -438,19 +434,14 @@ private HelloResult GetHelloResult( var helloResult = HelloHelper.GetResult(connection, helloProtocol, cancellationToken); stopwatch.Stop(); - if (_heartbeatSucceededEventHandler != null) - { - _heartbeatSucceededEventHandler(new ServerHeartbeatSucceededEvent(connection.ConnectionId, stopwatch.Elapsed, connection.Description.HelloResult.TopologyVersion != null)); - } + _eventLoggerSdam.LogAndPublish(new ServerHeartbeatSucceededEvent(connection.ConnectionId, stopwatch.Elapsed, connection.Description.HelloResult.TopologyVersion != null)); return helloResult; } catch (Exception ex) { - if (_heartbeatFailedEventHandler != null) - { - _heartbeatFailedEventHandler(new ServerHeartbeatFailedEvent(connection.ConnectionId, ex, connection.Description.HelloResult.TopologyVersion != null)); - } + _eventLoggerSdam.LogAndPublish(new ServerHeartbeatFailedEvent(connection.ConnectionId, ex, connection.Description.HelloResult.TopologyVersion != null)); + throw; } } diff --git a/src/MongoDB.Driver.Core/Core/Servers/ServerMonitorFactory.cs b/src/MongoDB.Driver.Core/Core/Servers/ServerMonitorFactory.cs index 1f08841fa04..81babd21467 100644 --- a/src/MongoDB.Driver.Core/Core/Servers/ServerMonitorFactory.cs +++ b/src/MongoDB.Driver.Core/Core/Servers/ServerMonitorFactory.cs @@ -14,6 +14,7 @@ */ using System.Net; +using Microsoft.Extensions.Logging; using MongoDB.Driver.Core.Connections; using MongoDB.Driver.Core.Events; using MongoDB.Driver.Core.Misc; @@ -24,6 +25,7 @@ internal class ServerMonitorFactory : IServerMonitorFactory { private readonly IConnectionFactory _connectionFactory; private readonly IEventSubscriber _eventSubscriber; + private readonly ILoggerFactory _loggerFactory; private readonly ServerMonitorSettings _serverMonitorSettings; private readonly ServerApi _serverApi; @@ -31,18 +33,20 @@ public ServerMonitorFactory( ServerMonitorSettings serverMonitorSettings, IConnectionFactory connectionFactory, IEventSubscriber eventSubscriber, - ServerApi serverApi) + ServerApi serverApi, + ILoggerFactory loggerFactory) { _serverMonitorSettings = Ensure.IsNotNull(serverMonitorSettings, nameof(serverMonitorSettings)); _connectionFactory = Ensure.IsNotNull(connectionFactory, nameof(connectionFactory)); _eventSubscriber = Ensure.IsNotNull(eventSubscriber, nameof(eventSubscriber)); _serverApi = serverApi; + _loggerFactory = loggerFactory; } /// public IServerMonitor Create(ServerId serverId, EndPoint endPoint) { - return new ServerMonitor(serverId, endPoint, _connectionFactory, _serverMonitorSettings, _eventSubscriber, _serverApi); + return new ServerMonitor(serverId, endPoint, _connectionFactory, _serverMonitorSettings, _eventSubscriber, _serverApi, _loggerFactory); } } } diff --git a/src/MongoDB.Driver.Core/MongoCursorNotFoundException.cs b/src/MongoDB.Driver.Core/MongoCursorNotFoundException.cs index ff6c5ded58d..b7af5fb82d2 100644 --- a/src/MongoDB.Driver.Core/MongoCursorNotFoundException.cs +++ b/src/MongoDB.Driver.Core/MongoCursorNotFoundException.cs @@ -35,7 +35,7 @@ private static string FormatMessage(ConnectionId connectionId, long cursorId) "Cursor {0} not found on server {1} using connection {2}.", cursorId, EndPointHelper.ToString(connectionId.ServerId.EndPoint), - connectionId.ServerValue); + connectionId.LongServerValue); } #endregion diff --git a/src/MongoDB.Driver.Core/MongoDB.Driver.Core.csproj b/src/MongoDB.Driver.Core/MongoDB.Driver.Core.csproj index b0ee192a6c0..c45a6823ac0 100644 --- a/src/MongoDB.Driver.Core/MongoDB.Driver.Core.csproj +++ b/src/MongoDB.Driver.Core/MongoDB.Driver.Core.csproj @@ -1,51 +1,16 @@ - true - true - true - - - netstandard2.0;netstandard2.1;net472 - netstandard2.0;netstandard2.1 - 9 - true - ..\..\MongoDB.ruleset MongoDB.Driver.Core MongoDB.Driver.Core - MongoDB Inc. - Copyright © 2010-present MongoDB Inc. Official MongoDB supported Driver Core library. See https://www.mongodb.com/docs/drivers/csharp/ for more details. - MongoDB Inc. - packageIcon.png - true Core Component of the Official MongoDB .NET Driver. - https://www.mongodb.com/docs/drivers/csharp/ - License.txt - mongodb;mongo;nosql - en-US - true - snupkg - true - - - - - - - - 0.0.0-local - TRACE - - - - true MongoDB.Driver @@ -53,137 +18,15 @@ NU5100 - - - runtimes/win/native - runtimes/linux/native - runtimes/osx/native - - - - - Core/Compression/Snappy/lib/win - Core/Compression/Snappy/lib/linux - Core/Compression/Snappy/lib/osx - - - - - - - - true - $(CompressionWinRuntimesPath) - - - - - true - $(CompressionLinuxRuntimesPath) - - - - - true - $(CompressionOsxRuntimesPath) - - - - - - - - - - PreserveNewest - %(FileName)%(Extension) - - - - - PreserveNewest - %(FileName)%(Extension) - - - - - PreserveNewest - %(FileName)%(Extension) - - - - - - Core/Compression/Zstandard/lib/win - Core/Compression/Zstandard/lib/linux - Core/Compression/Zstandard/lib/osx - - - - - - - - true - $(CompressionWinRuntimesPath) - - - - - true - $(CompressionLinuxRuntimesPath) - - - - - true - $(CompressionOsxRuntimesPath) - - - - - - - - - - PreserveNewest - %(FileName)%(Extension) - - - - - PreserveNewest - %(FileName)%(Extension) - - - - - PreserveNewest - %(FileName)%(Extension) - - - - - - - - - true - build - - - + - - + + + + - - all - runtime; build; native; contentfiles; analyzers - @@ -200,9 +43,7 @@ - - diff --git a/src/MongoDB.Driver.Core/MongoDB.Driver.Core.targets b/src/MongoDB.Driver.Core/MongoDB.Driver.Core.targets deleted file mode 100644 index f0397a0ad47..00000000000 --- a/src/MongoDB.Driver.Core/MongoDB.Driver.Core.targets +++ /dev/null @@ -1,35 +0,0 @@ - - - - - runtimes/win/native - runtimes/linux/native - runtimes/osx/native - - - - - - - - PreserveNewest - %(FileName)%(Extension) - - - - - - - PreserveNewest - %(FileName)%(Extension) - - - - - - - PreserveNewest - %(FileName)%(Extension) - - - diff --git a/src/MongoDB.Driver.Core/MongoInternalDefaults.cs b/src/MongoDB.Driver.Core/MongoInternalDefaults.cs index 43e69374426..2ae113b5cca 100644 --- a/src/MongoDB.Driver.Core/MongoInternalDefaults.cs +++ b/src/MongoDB.Driver.Core/MongoInternalDefaults.cs @@ -17,6 +17,11 @@ namespace MongoDB.Driver { internal static class MongoInternalDefaults { + public static class Logging + { + public const int MaxDocumentSize = 1000; + } + public static class ConnectionPool { public const int MaxConnecting = 2; diff --git a/src/MongoDB.Driver.Core/Properties/AssemblyInfo.cs b/src/MongoDB.Driver.Core/Properties/AssemblyInfo.cs index 526c3b1bfe9..9ff4bda3868 100644 --- a/src/MongoDB.Driver.Core/Properties/AssemblyInfo.cs +++ b/src/MongoDB.Driver.Core/Properties/AssemblyInfo.cs @@ -26,6 +26,7 @@ [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")] diff --git a/src/MongoDB.Driver.GridFS/GridFSDownloadStreamBase.cs b/src/MongoDB.Driver.GridFS/GridFSDownloadStreamBase.cs index 668e1ff6ad2..4e2089eb4eb 100644 --- a/src/MongoDB.Driver.GridFS/GridFSDownloadStreamBase.cs +++ b/src/MongoDB.Driver.GridFS/GridFSDownloadStreamBase.cs @@ -100,12 +100,13 @@ public override void Close(CancellationToken cancellationToken) public override void Flush() { - throw new NotSupportedException(); + // CSHARP-4377: Flush on read-only stream should be a NOP } public override Task FlushAsync(CancellationToken cancellationToken) { - throw new NotSupportedException(); + // CSHARP-4377: Flush on read-only stream should be a NOP + return Task.CompletedTask; } public override void SetLength(long value) diff --git a/src/MongoDB.Driver.GridFS/MongoDB.Driver.GridFS.csproj b/src/MongoDB.Driver.GridFS/MongoDB.Driver.GridFS.csproj index eb07c7da513..3ad6a159c19 100644 --- a/src/MongoDB.Driver.GridFS/MongoDB.Driver.GridFS.csproj +++ b/src/MongoDB.Driver.GridFS/MongoDB.Driver.GridFS.csproj @@ -1,69 +1,21 @@ - - true - - netstandard2.0;netstandard2.1;net472 - netstandard2.0;netstandard2.1 - 9 - true - ..\..\MongoDB.ruleset MongoDB.Driver.GridFS MongoDB.Driver.GridFS - MongoDB Inc. - Copyright © 2010-present MongoDB Inc. Official MongoDB supported driver for MongoDB GridFS implementation. See https://www.mongodb.com/docs/drivers/csharp/ for more details. - MongoDB Inc. - packageIcon.png - true GridFS Component of the Official MongoDB .NET Driver. - https://www.mongodb.com/docs/drivers/csharp/ - License.txt - mongodb;mongo;nosql;gridfs - en-US - true - snupkg - true - - - - 0.0.0-local - - - - TRACE + $(PackageTags);gridfs - - - - - - true - - - - - - all - runtime; build; native; contentfiles; analyzers - - - - - - - - diff --git a/src/MongoDB.Driver.Legacy/MongoDB.Driver.Legacy.csproj b/src/MongoDB.Driver.Legacy/MongoDB.Driver.Legacy.csproj index 45513eba927..7d38736a63b 100644 --- a/src/MongoDB.Driver.Legacy/MongoDB.Driver.Legacy.csproj +++ b/src/MongoDB.Driver.Legacy/MongoDB.Driver.Legacy.csproj @@ -1,55 +1,19 @@ - - true - - netstandard2.0;netstandard2.1;net472 - netstandard2.0;netstandard2.1 - 9 - true - ..\..\MongoDBLegacy.ruleset MongoDB.Driver.Legacy MongoDB.Driver.Legacy - MongoDB Inc. - Copyright © 2010-present MongoDB Inc. Legacy MongoDB supported driver for MongoDB. See https://www.mongodb.com/docs/drivers/csharp/ for more details. - MongoDB Inc. mongocsharpdriver - packageIcon.png - true This package contains the legacy driver. The new driver's package name is MongoDB.Driver - https://www.mongodb.com/docs/drivers/csharp/ - License.txt - en-US - true - snupkg - true - - - - 0.0.0-local - - - - all - runtime; build; native; contentfiles; analyzers - - - - TRACE - - - - true MongoDB.Driver @@ -59,9 +23,4 @@ - - - - - diff --git a/src/MongoDB.Driver.Legacy/MongoServerSettings.cs b/src/MongoDB.Driver.Legacy/MongoServerSettings.cs index 129cafc85e8..71c4e9bdd43 100644 --- a/src/MongoDB.Driver.Legacy/MongoServerSettings.cs +++ b/src/MongoDB.Driver.Legacy/MongoServerSettings.cs @@ -565,12 +565,13 @@ public ConnectionStringScheme Scheme /// /// Gets or set the name of the SDAM log file. Null turns logging off. stdout will log to console. /// + [Obsolete("Use MongoClientSettings.LoggerFactory instead.")] public string SdamLogFilename { get { return _sdamLogFilename; } set { - if (_isFrozen) { throw new InvalidOperationException("MongoClientSettings is frozen."); } + if (_isFrozen) { throw new InvalidOperationException("MongoServerSettings is frozen."); } _sdamLogFilename = value; } } @@ -821,7 +822,9 @@ public static MongoServerSettings FromClientSettings(MongoClientSettings clientS serverSettings.RetryWrites = clientSettings.RetryWrites; serverSettings.LocalThreshold = clientSettings.LocalThreshold; serverSettings.Scheme = clientSettings.Scheme; +#pragma warning disable CS0618 // Type or member is obsolete serverSettings.SdamLogFilename = clientSettings.SdamLogFilename; +#pragma warning restore CS0618 // Type or member is obsolete serverSettings.Servers = new List(clientSettings.Servers); serverSettings.ServerSelectionTimeout = clientSettings.ServerSelectionTimeout; serverSettings.SocketTimeout = clientSettings.SocketTimeout; @@ -897,7 +900,9 @@ public static MongoServerSettings FromUrl(MongoUrl url) serverSettings.RetryWrites = url.RetryWrites ?? true; serverSettings.LocalThreshold = url.LocalThreshold; serverSettings.Scheme = url.Scheme; +#pragma warning disable CS0618 // Type or member is obsolete serverSettings.SdamLogFilename = null; // SdamLogFilename must be provided in code +#pragma warning restore CS0618 // Type or member is obsolete serverSettings.Servers = new List(url.Servers); serverSettings.ServerSelectionTimeout = url.ServerSelectionTimeout; serverSettings.SocketTimeout = url.SocketTimeout; @@ -1204,6 +1209,7 @@ internal ClusterKey ToClusterKey() _ipv6, loadBalanced: false, // not supported for legacy, so turn it off _localThreshold, + loggingSettings: null, maxConnecting: MongoInternalDefaults.ConnectionPool.MaxConnecting, _maxConnectionIdleTime, _maxConnectionLifeTime, diff --git a/src/MongoDB.Driver/AggregateBucketAutoResultIdSerializer.cs b/src/MongoDB.Driver/AggregateBucketAutoResultIdSerializer.cs new file mode 100644 index 00000000000..7185dbdd45d --- /dev/null +++ b/src/MongoDB.Driver/AggregateBucketAutoResultIdSerializer.cs @@ -0,0 +1,103 @@ +/* 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 MongoDB.Bson; +using MongoDB.Bson.IO; +using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Serializers; +using MongoDB.Driver.Core.Misc; + +namespace MongoDB.Driver +{ + /// + /// Static factory class for AggregateBucketAutoResultIdSerializer. + /// + public static class AggregateBucketAutoResultIdSerializer + { + /// + /// Creates an instance of AggregateBucketAutoResultIdSerializer. + /// + /// The value type. + /// The value serializer. + /// A AggregateBucketAutoResultIdSerializer. + public static IBsonSerializer> Create(IBsonSerializer valueSerializer) + { + return new AggregateBucketAutoResultIdSerializer(valueSerializer); + } + } + + /// + /// A serializer for AggregateBucketAutoResultId. + /// + /// The type of the values. + public class AggregateBucketAutoResultIdSerializer : ClassSerializerBase>, IBsonDocumentSerializer + { + private readonly IBsonSerializer _valueSerializer; + + /// + /// Initializes a new instance of the class. + /// + /// The value serializer. + public AggregateBucketAutoResultIdSerializer(IBsonSerializer valueSerializer) + { + _valueSerializer = Ensure.IsNotNull(valueSerializer, nameof(valueSerializer)); + } + + /// + protected override AggregateBucketAutoResultId DeserializeValue(BsonDeserializationContext context, BsonDeserializationArgs args) + { + var reader = context.Reader; + reader.ReadStartDocument(); + TValue min = default; + TValue max = default; + while (reader.ReadBsonType() != 0) + { + var name = reader.ReadName(); + switch (name) + { + case "min": min = _valueSerializer.Deserialize(context); break; + case "max": max = _valueSerializer.Deserialize(context); break; + default: throw new BsonSerializationException($"Invalid element name for AggregateBucketAutoResultId: {name}."); + } + } + reader.ReadEndDocument(); + return new AggregateBucketAutoResultId(min, max); + } + + /// + protected override void SerializeValue(BsonSerializationContext context, BsonSerializationArgs args, AggregateBucketAutoResultId value) + { + var writer = context.Writer; + writer.WriteStartDocument(); + writer.WriteName("min"); + _valueSerializer.Serialize(context, value.Min); + writer.WriteName("max"); + _valueSerializer.Serialize(context, value.Max); + writer.WriteEndDocument(); + } + + /// + public bool TryGetMemberSerializationInfo(string memberName, out BsonSerializationInfo serializationInfo) + { + serializationInfo = memberName switch + { + "Min" => new BsonSerializationInfo("min", _valueSerializer, _valueSerializer.ValueType), + "Max" => new BsonSerializationInfo("max", _valueSerializer, _valueSerializer.ValueType), + _ => null + }; + return serializationInfo != null; + } + } +} diff --git a/src/MongoDB.Driver/AggregateExpressionDefinition.cs b/src/MongoDB.Driver/AggregateExpressionDefinition.cs index 7bc36877946..aff5764d911 100644 --- a/src/MongoDB.Driver/AggregateExpressionDefinition.cs +++ b/src/MongoDB.Driver/AggregateExpressionDefinition.cs @@ -14,11 +14,13 @@ */ using System; +using System.Collections.Generic; using System.Linq.Expressions; using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Linq; +using MongoDB.Driver.Linq.Linq3Implementation.Misc; using MongoDB.Driver.Linq.Linq3Implementation.Translators; namespace MongoDB.Driver @@ -147,4 +149,38 @@ public override BsonValue Render(IBsonSerializer sourceSerializer, IBso return linqProvider.GetAdapter().TranslateExpressionToAggregateExpression(_expression, sourceSerializer, serializerRegistry, _translationOptions, contextData); } } + + /// + /// An aggregate expression for the $documents stage. + /// + /// The type of the documents. + /// + public sealed class DocumentsAggregateExpressionDefinition : AggregateExpressionDefinition> + { + // private fields + private readonly IReadOnlyList _documents; + private readonly IBsonSerializer _documentSerializer; + + // constructors + /// + /// Initializes a new instance of the class. + /// + /// The documents. + /// The document serializer. + public DocumentsAggregateExpressionDefinition( + IEnumerable documents, + IBsonSerializer documentSerializer = null) + { + _documents = Ensure.IsNotNull(documents, nameof(documents)).AsReadOnlyList(); + _documentSerializer = documentSerializer; // can be null + } + + // public methods + /// + public override BsonValue Render(IBsonSerializer sourceSerializer, IBsonSerializerRegistry serializerRegistry, LinqProvider linqProvider) + { + var documentSerializer = _documentSerializer ?? serializerRegistry.GetSerializer(); + return SerializationHelper.SerializeValues(documentSerializer, _documents); + } + } } diff --git a/src/MongoDB.Driver/AggregateFluent.cs b/src/MongoDB.Driver/AggregateFluent.cs index 6479c61822c..f4732d3fc69 100644 --- a/src/MongoDB.Driver/AggregateFluent.cs +++ b/src/MongoDB.Driver/AggregateFluent.cs @@ -13,13 +13,14 @@ * limitations under the License. */ -using MongoDB.Bson; -using MongoDB.Bson.Serialization; -using MongoDB.Driver.Core.Misc; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MongoDB.Bson; +using MongoDB.Bson.Serialization; +using MongoDB.Driver.Core.Misc; +using MongoDB.Driver.Search; namespace MongoDB.Driver { @@ -146,7 +147,7 @@ public override IAggregateFluent Group(ProjectionDefinit return WithPipeline(_pipeline.Group(group)); } - public override IAggregateFluent Limit(int limit) + public override IAggregateFluent Limit(long limit) { return WithPipeline(_pipeline.Limit(limit)); } @@ -238,6 +239,24 @@ public override IAggregateFluent ReplaceWith(AggregateEx return WithPipeline(_pipeline.ReplaceWith(newRoot)); } + public override IAggregateFluent Search( + SearchDefinition searchDefinition, + SearchHighlightOptions highlight = null, + string indexName = null, + SearchCountOptions count = null, + bool returnStoredSource = false) + { + return WithPipeline(_pipeline.Search(searchDefinition, highlight, indexName, count, returnStoredSource)); + } + + public override IAggregateFluent SearchMeta( + SearchDefinition searchDefinition, + string indexName = null, + SearchCountOptions count = null) + { + return WithPipeline(_pipeline.SearchMeta(searchDefinition, indexName, count)); + } + public override IAggregateFluent SetWindowFields( AggregateExpressionDefinition, TWindowFields> output) { @@ -259,7 +278,7 @@ public override IAggregateFluent SetWindowFields Skip(int skip) + public override IAggregateFluent Skip(long skip) { return WithPipeline(_pipeline.Skip(skip)); } diff --git a/src/MongoDB.Driver/AggregateFluentBase.cs b/src/MongoDB.Driver/AggregateFluentBase.cs index 2393da49074..8239750962a 100644 --- a/src/MongoDB.Driver/AggregateFluentBase.cs +++ b/src/MongoDB.Driver/AggregateFluentBase.cs @@ -19,6 +19,7 @@ using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.Serialization; +using MongoDB.Driver.Search; namespace MongoDB.Driver { @@ -141,7 +142,7 @@ public virtual IAggregateFluent GraphLookup Group(ProjectionDefinition group); /// - public abstract IAggregateFluent Limit(int limit); + public abstract IAggregateFluent Limit(long limit); /// public virtual IAggregateFluent Lookup(string foreignCollectionName, FieldDefinition localField, FieldDefinition foreignField, FieldDefinition @as, AggregateLookupOptions options) @@ -215,6 +216,26 @@ public virtual IAggregateFluent ReplaceWith(AggregateExp throw new NotImplementedException(); } + /// + public virtual IAggregateFluent Search( + SearchDefinition searchDefinition, + SearchHighlightOptions highlight = null, + string indexName = null, + SearchCountOptions count = null, + bool returnStoredSource = false) + { + throw new NotImplementedException(); + } + + /// + public virtual IAggregateFluent SearchMeta( + SearchDefinition searchDefinition, + string indexName = null, + SearchCountOptions count = null) + { + throw new NotImplementedException(); + } + /// public virtual IAggregateFluent SetWindowFields( AggregateExpressionDefinition, TWindowFields> output) @@ -240,7 +261,7 @@ public virtual IAggregateFluent SetWindowFields - public abstract IAggregateFluent Skip(int skip); + public abstract IAggregateFluent Skip(long skip); /// public abstract IAggregateFluent Sort(SortDefinition sort); diff --git a/src/MongoDB.Driver/Builders.cs b/src/MongoDB.Driver/Builders.cs index 2748d915ce5..cace9a352f0 100644 --- a/src/MongoDB.Driver/Builders.cs +++ b/src/MongoDB.Driver/Builders.cs @@ -13,6 +13,8 @@ * limitations under the License. */ +using MongoDB.Driver.Search; + namespace MongoDB.Driver { /// @@ -21,50 +23,38 @@ namespace MongoDB.Driver /// The type of the document. public static class Builders { - private static FilterDefinitionBuilder __filter = new FilterDefinitionBuilder(); - private static IndexKeysDefinitionBuilder __index = new IndexKeysDefinitionBuilder(); - private static ProjectionDefinitionBuilder __projection = new ProjectionDefinitionBuilder(); - private static SortDefinitionBuilder __sort = new SortDefinitionBuilder(); - private static UpdateDefinitionBuilder __update = new UpdateDefinitionBuilder(); + /// Gets a . + public static FilterDefinitionBuilder Filter { get; } = new FilterDefinitionBuilder(); + + /// Gets an . + public static IndexKeysDefinitionBuilder IndexKeys { get; } = new IndexKeysDefinitionBuilder(); + + /// Gets a . + public static ProjectionDefinitionBuilder Projection { get; } = new ProjectionDefinitionBuilder(); + + /// Gets a . + public static SortDefinitionBuilder Sort { get; } = new SortDefinitionBuilder(); + + /// Gets an . + public static UpdateDefinitionBuilder Update { get; } = new UpdateDefinitionBuilder(); + + // Search builders + /// Gets a . + public static SearchFacetBuilder SearchFacet { get; } = new SearchFacetBuilder(); - /// - /// Gets a . - /// - public static FilterDefinitionBuilder Filter - { - get { return __filter; } - } + /// Gets a . + public static SearchPathDefinitionBuilder SearchPath { get; } = new SearchPathDefinitionBuilder(); - /// - /// Gets an . - /// - public static IndexKeysDefinitionBuilder IndexKeys - { - get { return __index; } - } + /// Gets a . + public static SearchScoreDefinitionBuilder SearchScore { get; } = new SearchScoreDefinitionBuilder(); - /// - /// Gets a . - /// - public static ProjectionDefinitionBuilder Projection - { - get { return __projection; } - } + /// Gets a . + public static SearchScoreFunctionBuilder SearchScoreFunction { get; } = new SearchScoreFunctionBuilder(); - /// - /// Gets a . - /// - public static SortDefinitionBuilder Sort - { - get { return __sort; } - } + /// Gets a . + public static SearchDefinitionBuilder Search { get; } = new SearchDefinitionBuilder(); - /// - /// Gets an . - /// - public static UpdateDefinitionBuilder Update - { - get { return __update; } - } + /// Gets a . + public static SearchSpanDefinitionBuilder SearchSpan { get; } = new SearchSpanDefinitionBuilder(); } } diff --git a/src/MongoDB.Driver/ChangeStreamHelper.cs b/src/MongoDB.Driver/ChangeStreamHelper.cs index c477d8b0efa..55be5d30a3a 100644 --- a/src/MongoDB.Driver/ChangeStreamHelper.cs +++ b/src/MongoDB.Driver/ChangeStreamHelper.cs @@ -32,7 +32,7 @@ public static ChangeStreamOperation CreateChangeStreamOperation CreateChangeStreamOperation( @@ -65,7 +65,7 @@ public static ChangeStreamOperation CreateChangeStreamOperation CreateChangeStreamOperation( @@ -101,7 +101,7 @@ public static ChangeStreamOperation CreateChangeStreamOperation CreateChangeStreamOperation( diff --git a/src/MongoDB.Driver/ClusterKey.cs b/src/MongoDB.Driver/ClusterKey.cs index 939fe390899..7decd94f8ea 100644 --- a/src/MongoDB.Driver/ClusterKey.cs +++ b/src/MongoDB.Driver/ClusterKey.cs @@ -16,6 +16,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.Extensions.Logging; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Configuration; using MongoDB.Shared; @@ -43,6 +44,7 @@ internal class ClusterKey private readonly bool _ipv6; private readonly bool _loadBalanced; private readonly TimeSpan _localThreshold; + private readonly LoggingSettings _loggingSettings; private readonly int _maxConnecting; private readonly TimeSpan _maxConnectionIdleTime; private readonly TimeSpan _maxConnectionLifeTime; @@ -82,6 +84,7 @@ public ClusterKey( bool ipv6, bool loadBalanced, TimeSpan localThreshold, + LoggingSettings loggingSettings, int maxConnecting, TimeSpan maxConnectionIdleTime, TimeSpan maxConnectionLifeTime, @@ -119,6 +122,7 @@ public ClusterKey( _ipv6 = ipv6; _loadBalanced = loadBalanced; _localThreshold = localThreshold; + _loggingSettings = loggingSettings; _maxConnecting = maxConnecting; _maxConnectionIdleTime = maxConnectionIdleTime; _maxConnectionLifeTime = maxConnectionLifeTime; @@ -182,6 +186,7 @@ public bool? DirectConnection public bool IPv6 { get { return _ipv6; } } public bool LoadBalanced => _loadBalanced; public TimeSpan LocalThreshold { get { return _localThreshold; } } + public LoggingSettings LoggingSettings { get { return _loggingSettings; } } public int MaxConnecting{ get { return _maxConnecting; } } public TimeSpan MaxConnectionIdleTime { get { return _maxConnectionIdleTime; } } public TimeSpan MaxConnectionLifeTime { get { return _maxConnectionLifeTime; } } @@ -236,6 +241,7 @@ public override bool Equals(object obj) _ipv6 == rhs._ipv6 && _loadBalanced == rhs._loadBalanced && _localThreshold == rhs._localThreshold && + _loggingSettings == rhs._loggingSettings && _maxConnecting == rhs._maxConnecting && _maxConnectionIdleTime == rhs._maxConnectionIdleTime && _maxConnectionLifeTime == rhs._maxConnectionLifeTime && diff --git a/src/MongoDB.Driver/ClusterRegistry.cs b/src/MongoDB.Driver/ClusterRegistry.cs index 2d979394227..b947f067828 100644 --- a/src/MongoDB.Driver/ClusterRegistry.cs +++ b/src/MongoDB.Driver/ClusterRegistry.cs @@ -54,13 +54,16 @@ public static ClusterRegistry Instance // methods private ICluster CreateCluster(ClusterKey clusterKey) { +#pragma warning disable CS0618 // Type or member is obsolete var builder = new ClusterBuilder() .ConfigureCluster(settings => ConfigureCluster(settings, clusterKey)) .ConfigureServer(settings => ConfigureServer(settings, clusterKey)) .ConfigureConnectionPool(settings => ConfigureConnectionPool(settings, clusterKey)) .ConfigureConnection(settings => ConfigureConnection(settings, clusterKey)) .ConfigureTcp(settings => ConfigureTcp(settings, clusterKey)) - .ConfigureSdamLogging(settings => ConfigureSdamLogging(settings, clusterKey)); + .ConfigureSdamLogging(settings => ConfigureSdamLogging(settings, clusterKey)) + .ConfigureLoggingSettings(_ => clusterKey.LoggingSettings); +#pragma warning restore CS0618 // Type or member is obsolete if (clusterKey.UseTls) { @@ -124,7 +127,9 @@ private ConnectionSettings ConfigureConnection(ConnectionSettings settings, Clus applicationName: clusterKey.ApplicationName); } +#pragma warning disable CS0618 // Type or member is obsolete private SdamLoggingSettings ConfigureSdamLogging(SdamLoggingSettings settings, ClusterKey clusterKey) +#pragma warning restore CS0618 // Type or member is obsolete { return settings.With(logFilename: clusterKey.SdamLogFilename); } diff --git a/src/MongoDB.Driver/CreateCollectionOptions.cs b/src/MongoDB.Driver/CreateCollectionOptions.cs index 1b3cbb5929f..7a79c9cd3b7 100644 --- a/src/MongoDB.Driver/CreateCollectionOptions.cs +++ b/src/MongoDB.Driver/CreateCollectionOptions.cs @@ -196,6 +196,27 @@ public DocumentValidationLevel? ValidationLevel get { return _validationLevel; } set { _validationLevel = value; } } + + internal virtual CreateCollectionOptions Clone() => + new CreateCollectionOptions + { + _autoIndexId = _autoIndexId, + _capped = _capped, + _changeStreamPreAndPostImagesOptions = _changeStreamPreAndPostImagesOptions, + _collation = _collation, + _encryptedFields = _encryptedFields, + _expireAfter = _expireAfter, + _indexOptionDefaults = _indexOptionDefaults, + _maxDocuments = _maxDocuments, + _maxSize = _maxSize, + _noPadding = _noPadding, + _serializerRegistry = _serializerRegistry, + _storageEngine = _storageEngine, + _timeSeriesOptions = _timeSeriesOptions, + _usePowerOf2Sizes = _usePowerOf2Sizes, + _validationAction = _validationAction, + _validationLevel = _validationLevel + }; } /// @@ -282,5 +303,32 @@ public FilterDefinition Validator get { return _validator; } set { _validator = value; } } + + internal override CreateCollectionOptions Clone() => + new CreateCollectionOptions + { + #pragma warning disable CS0618 // Type or member is obsolete + AutoIndexId = base.AutoIndexId, + #pragma warning restore CS0618 // Type or member is obsolete + Capped = base.Capped, + ChangeStreamPreAndPostImagesOptions = base.ChangeStreamPreAndPostImagesOptions, + Collation = base.Collation, + EncryptedFields = base.EncryptedFields, + ExpireAfter = base.ExpireAfter, + IndexOptionDefaults = base.IndexOptionDefaults, + MaxDocuments = base.MaxDocuments, + MaxSize = base.MaxSize, + NoPadding = base.NoPadding, + SerializerRegistry = base.SerializerRegistry, + StorageEngine = base.StorageEngine, + TimeSeriesOptions = base.TimeSeriesOptions, + UsePowerOf2Sizes = base.UsePowerOf2Sizes, + ValidationAction = base.ValidationAction, + ValidationLevel = base.ValidationLevel, + + _clusteredIndex = _clusteredIndex, + _documentSerializer = _documentSerializer, + _validator = _validator + }; } } diff --git a/src/MongoDB.Driver/Encryption/AutoEncryptionLibMongoController.cs b/src/MongoDB.Driver/Encryption/AutoEncryptionLibMongoController.cs index 61512dfcee2..e1adb9950d9 100644 --- a/src/MongoDB.Driver/Encryption/AutoEncryptionLibMongoController.cs +++ b/src/MongoDB.Driver/Encryption/AutoEncryptionLibMongoController.cs @@ -19,7 +19,6 @@ using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; -using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; using MongoDB.Driver.Core.WireProtocol; using MongoDB.Libmongocrypt; @@ -66,11 +65,7 @@ private AutoEncryptionLibMongoCryptController( IMongoClient metadataClient, CryptClient cryptClient, AutoEncryptionOptions autoEncryptionOptions) - : base( - Ensure.IsNotNull(cryptClient, nameof(cryptClient)), - Ensure.IsNotNull(keyVaultClient, nameof(keyVaultClient)), - Ensure.IsNotNull(Ensure.IsNotNull(autoEncryptionOptions, nameof(autoEncryptionOptions)).KeyVaultNamespace, nameof(autoEncryptionOptions.KeyVaultNamespace)), - Ensure.IsNotNull(Ensure.IsNotNull(autoEncryptionOptions, nameof(autoEncryptionOptions)).TlsOptions, nameof(autoEncryptionOptions.TlsOptions))) + : base(cryptClient, keyVaultClient, autoEncryptionOptions) { _internalClient = internalClient; // can be null _metadataClient = metadataClient; // can be null diff --git a/src/MongoDB.Driver/Encryption/AutoEncryptionOptions.cs b/src/MongoDB.Driver/Encryption/AutoEncryptionOptions.cs index 2af8e9fd06f..6ba5f8c62ca 100644 --- a/src/MongoDB.Driver/Encryption/AutoEncryptionOptions.cs +++ b/src/MongoDB.Driver/Encryption/AutoEncryptionOptions.cs @@ -30,7 +30,7 @@ namespace MongoDB.Driver.Encryption /// /// Auto encryption options. /// - public class AutoEncryptionOptions + public class AutoEncryptionOptions : IEncryptionOptions { // private fields private readonly bool _bypassAutoEncryption; @@ -77,7 +77,7 @@ public AutoEncryptionOptions( _tlsOptions = tlsOptions.WithDefault(new Dictionary()); _encryptedFieldsMap = encryptedFieldsMap.WithDefault(null); - EncryptionExtraOptionsValidator.EnsureThatExtraOptionsAreValid(_extraOptions); + EncryptionExtraOptionsHelper.EnsureThatExtraOptionsAreValid(_extraOptions); KmsProvidersHelper.EnsureKmsProvidersAreValid(_kmsProviders); KmsProvidersHelper.EnsureKmsProvidersTlsSettingsAreValid(_tlsOptions); EncryptedCollectionHelper.EnsureCollectionsValid(_schemaMap, _encryptedFieldsMap); @@ -273,10 +273,10 @@ public override string ToString() internal CryptClientSettings ToCryptClientSettings() => new CryptClientSettings( _bypassQueryAnalysis, - ExtraOptions.GetValueOrDefault("cryptSharedLibPath"), + EncryptionExtraOptionsHelper.ExtractCryptSharedLibPath(ExtraOptions), cryptSharedLibSearchPath: _bypassAutoEncryption ? null : "$SYSTEM", _encryptedFieldsMap, - ExtraOptions.GetValueOrDefault("cryptSharedLibRequired"), + EncryptionExtraOptionsHelper.ExtractCryptSharedLibRequired(ExtraOptions), _kmsProviders, _schemaMap); diff --git a/src/MongoDB.Driver/Encryption/ClientEncryption.cs b/src/MongoDB.Driver/Encryption/ClientEncryption.cs index ce01732ff5b..dddf27588a8 100644 --- a/src/MongoDB.Driver/Encryption/ClientEncryption.cs +++ b/src/MongoDB.Driver/Encryption/ClientEncryption.cs @@ -20,6 +20,7 @@ using MongoDB.Bson; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Configuration; +using MongoDB.Driver.Core.Misc; using MongoDB.Libmongocrypt; namespace MongoDB.Driver.Encryption @@ -78,6 +79,90 @@ public BsonDocument AddAlternateKeyName(Guid id, string alternateKeyName, Cancel public Task AddAlternateKeyNameAsync(Guid id, string alternateKeyName, CancellationToken cancellationToken = default) => _libMongoCryptController.AddAlternateKeyNameAsync(id, alternateKeyName, cancellationToken); + /// + /// Create encrypted collection. + /// + /// The database. + /// The collection name. + /// The create collection options. + /// The kms provider. + /// The datakey options. + /// The cancellation token. + /// The operation result. + /// + /// if EncryptionFields contains a keyId with a null value, a data key will be automatically generated and assigned to keyId value. + /// + public CreateEncryptedCollectionResult CreateEncryptedCollection(IMongoDatabase database, string collectionName, CreateCollectionOptions createCollectionOptions, string kmsProvider, DataKeyOptions dataKeyOptions, CancellationToken cancellationToken = default) + { + Ensure.IsNotNull(database, nameof(database)); + Ensure.IsNotNull(collectionName, nameof(collectionName)); + Ensure.IsNotNull(createCollectionOptions, nameof(createCollectionOptions)); + Ensure.IsNotNull(dataKeyOptions, nameof(dataKeyOptions)); + Ensure.IsNotNull(kmsProvider, nameof(kmsProvider)); + + var encryptedFields = createCollectionOptions.EncryptedFields?.DeepClone()?.AsBsonDocument; + try + { + foreach (var fieldDocument in EncryptedCollectionHelper.IterateEmptyKeyIds(new CollectionNamespace(database.DatabaseNamespace.DatabaseName, collectionName), encryptedFields)) + { + var dataKey = CreateDataKey(kmsProvider, dataKeyOptions, cancellationToken); + EncryptedCollectionHelper.ModifyEncryptedFields(fieldDocument, dataKey); + } + + var effectiveCreateEncryptionOptions = createCollectionOptions.Clone(); + effectiveCreateEncryptionOptions.EncryptedFields = encryptedFields; + database.CreateCollection(collectionName, effectiveCreateEncryptionOptions, cancellationToken); + } + catch (Exception ex) + { + throw new MongoEncryptionCreateCollectionException(ex, encryptedFields); + } + + return new CreateEncryptedCollectionResult(encryptedFields); + } + + /// + /// Create encrypted collection. + /// + /// The database. + /// The collection name. + /// The create collection options. + /// The kms provider. + /// The datakey options. + /// The cancellation token. + /// The operation result. + /// + /// if EncryptionFields contains a keyId with a null value, a data key will be automatically generated and assigned to keyId value. + /// + public async Task CreateEncryptedCollectionAsync(IMongoDatabase database, string collectionName, CreateCollectionOptions createCollectionOptions, string kmsProvider, DataKeyOptions dataKeyOptions, CancellationToken cancellationToken = default) + { + Ensure.IsNotNull(database, nameof(database)); + Ensure.IsNotNull(collectionName, nameof(collectionName)); + Ensure.IsNotNull(createCollectionOptions, nameof(createCollectionOptions)); + Ensure.IsNotNull(dataKeyOptions, nameof(dataKeyOptions)); + Ensure.IsNotNull(kmsProvider, nameof(kmsProvider)); + + var encryptedFields = createCollectionOptions.EncryptedFields?.DeepClone()?.AsBsonDocument; + try + { + foreach (var fieldDocument in EncryptedCollectionHelper.IterateEmptyKeyIds(new CollectionNamespace(database.DatabaseNamespace.DatabaseName, collectionName), encryptedFields)) + { + var dataKey = await CreateDataKeyAsync(kmsProvider, dataKeyOptions, cancellationToken).ConfigureAwait(false); + EncryptedCollectionHelper.ModifyEncryptedFields(fieldDocument, dataKey); + } + + var effectiveCreateEncryptionOptions = createCollectionOptions.Clone(); + effectiveCreateEncryptionOptions.EncryptedFields = encryptedFields; + await database.CreateCollectionAsync(collectionName, effectiveCreateEncryptionOptions, cancellationToken).ConfigureAwait(false); + } + catch (Exception ex) + { + throw new MongoEncryptionCreateCollectionException(ex, encryptedFields); + } + + return new CreateEncryptedCollectionResult(encryptedFields); + } + /// /// An alias function equivalent to createKey. /// @@ -155,7 +240,8 @@ public void Dispose() /// The encrypt options. /// The cancellation token. /// The encrypted value. - public BsonBinaryData Encrypt(BsonValue value, EncryptOptions encryptOptions, CancellationToken cancellationToken = default) => _libMongoCryptController.EncryptField(value, encryptOptions, cancellationToken); + public BsonBinaryData Encrypt(BsonValue value, EncryptOptions encryptOptions, CancellationToken cancellationToken = default) => + EnsureEncryptedData(_libMongoCryptController.EncryptField(value, encryptOptions, isExpressionMode: false, cancellationToken)); /// /// Encrypts the specified value. @@ -164,7 +250,48 @@ public void Dispose() /// The encrypt options. /// The cancellation token. /// The encrypted value. - public Task EncryptAsync(BsonValue value, EncryptOptions encryptOptions, CancellationToken cancellationToken = default) => _libMongoCryptController.EncryptFieldAsync(value, encryptOptions, cancellationToken); + public async Task EncryptAsync(BsonValue value, EncryptOptions encryptOptions, CancellationToken cancellationToken = default) => + EnsureEncryptedData(await _libMongoCryptController.EncryptFieldAsync(value, encryptOptions, isExpressionMode: false, cancellationToken).ConfigureAwait(false)); + + /// + /// Encrypts a Match Expression or Aggregate Expression to query a range index. + /// + /// The expression that is expected to be a BSON document of one of the following forms: + /// 1. A Match Expression of this form: + /// {$and: [{"field": {$gt: "value1"}}, {"field": {$lt: "value2" }}]} + /// 2. An Aggregate Expression of this form: + /// {$and: [{$gt: ["fieldpath", "value1"]}, {$lt: ["fieldpath", "value2"]}] + /// $gt may also be $gte. $lt may also be $lte. + /// + /// The encryption options. + /// The cancellation token. + /// The encrypted expression. + /// + /// Only supported for queryType "rangePreview" + /// The Range algorithm is experimental only. It is not intended for public use. It is subject to breaking changes. + /// + public BsonDocument EncryptExpression(BsonDocument expression, EncryptOptions encryptOptions, CancellationToken cancellationToken = default) => + EnsureEncryptedData(_libMongoCryptController.EncryptField(expression, encryptOptions, isExpressionMode: true, cancellationToken)); + + /// + /// Encrypts a Match Expression or Aggregate Expression to query a range index. + /// + /// The expression that is expected to be a BSON document of one of the following forms: + /// 1. A Match Expression of this form: + /// {$and: [{"field": {$gt: "value1"}}, {"field": {$lt: "value2" }}]} + /// 2. An Aggregate Expression of this form: + /// {$and: [{$gt: ["fieldpath", "value1"]}, {$lt: ["fieldpath", "value2"]}] + /// $gt may also be $gte. $lt may also be $lte. + /// + /// The encryption options. + /// The cancellation token. + /// the encrypted expression. + /// + /// Only supported for queryType "rangePreview" + /// The Range algorithm is experimental only. It is not intended for public use. It is subject to breaking changes. + /// + public async Task EncryptExpressionAsync(BsonDocument expression, EncryptOptions encryptOptions, CancellationToken cancellationToken = default) => + EnsureEncryptedData(await _libMongoCryptController.EncryptFieldAsync(expression, encryptOptions, isExpressionMode: true, cancellationToken).ConfigureAwait(false)); /// /// Finds a single key document with the given UUID (BSON binary subtype 0x04). @@ -257,5 +384,19 @@ public RewrapManyDataKeyResult RewrapManyDataKey(FilterDefinition /// The result. public Task RewrapManyDataKeyAsync(FilterDefinition filter, RewrapManyDataKeyOptions options, CancellationToken cancellationToken = default) => _libMongoCryptController.RewrapManyDataKeyAsync(filter, options, cancellationToken); + + // private methods + private TEncryptedValue EnsureEncryptedData(BsonValue encryptedValue) where TEncryptedValue : BsonValue + { + if (encryptedValue is TEncryptedValue convertedValue) + { + return convertedValue; + } + else + { + // should not be reached + throw new InvalidOperationException($"The encrypted data must be {typeof(TEncryptedValue).Name}, but was {encryptedValue?.GetType()?.Name ?? "null"}."); + } + } } } diff --git a/src/MongoDB.Driver/Encryption/ClientEncryptionOptions.cs b/src/MongoDB.Driver/Encryption/ClientEncryptionOptions.cs index e413e457198..17fbdac20fb 100644 --- a/src/MongoDB.Driver/Encryption/ClientEncryptionOptions.cs +++ b/src/MongoDB.Driver/Encryption/ClientEncryptionOptions.cs @@ -21,7 +21,7 @@ namespace MongoDB.Driver.Encryption /// /// Client encryption options. /// - public class ClientEncryptionOptions + public class ClientEncryptionOptions : IEncryptionOptions { // private fields private readonly IMongoClient _keyVaultClient; diff --git a/src/MongoDB.Driver/Encryption/CreateEncryptedCollectionResult.cs b/src/MongoDB.Driver/Encryption/CreateEncryptedCollectionResult.cs new file mode 100644 index 00000000000..30476cf8f0a --- /dev/null +++ b/src/MongoDB.Driver/Encryption/CreateEncryptedCollectionResult.cs @@ -0,0 +1,38 @@ +/* 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 MongoDB.Bson; + +namespace MongoDB.Driver.Encryption +{ + /// + /// Represents the result of a create encrypted collection. + /// + public sealed class CreateEncryptedCollectionResult + { + private readonly BsonDocument _encryptedFields; + + /// + /// Initializes a new instance of the class. + /// + /// The encrypted fields document. + public CreateEncryptedCollectionResult(BsonDocument encryptedFields) => _encryptedFields = encryptedFields; + + /// + /// The encrypted fields document. + /// + public BsonDocument EncryptedFields => _encryptedFields; + } +} diff --git a/src/MongoDB.Driver/Encryption/EncryptOptions.cs b/src/MongoDB.Driver/Encryption/EncryptOptions.cs index d151d16e071..b566acee96d 100644 --- a/src/MongoDB.Driver/Encryption/EncryptOptions.cs +++ b/src/MongoDB.Driver/Encryption/EncryptOptions.cs @@ -13,11 +13,78 @@ * limitations under the License. */ +using MongoDB.Bson; using MongoDB.Driver.Core.Misc; using System; namespace MongoDB.Driver.Encryption { + /// + /// Range options. + /// + /// + /// The Range algorithm is experimental only. It is not intended for public use. + /// RangeOpts specifies index options for a Queryable Encryption field supporting "rangePreview" queries. + /// min, max, sparsity, and range must match the values set in the encryptedFields of the destination collection. + /// For double and decimal128, min/max/precision must all be set, or all be unset. + /// RangeOptions only applies when algorithm is "rangePreview". + /// + public sealed class RangeOptions + { + private readonly BsonValue _max; + private readonly BsonValue _min; + private readonly int? _precision; + private readonly long _sparsity; + + /// + /// Initializes a new instance of the class. + /// + /// The sparsity. + /// The min range. + /// The max range. + /// The precision range. + public RangeOptions(long sparsity, Optional min = default, Optional max = default, Optional precision = default) + { + _sparsity = sparsity; + _min = min.WithDefault(null); + _max = max.WithDefault(null); + _precision = precision.WithDefault(null); + } + + // public properties + /// + /// Minimum value. + /// + /// Min is required if precision is set. + public BsonValue Min => _min; + /// + /// Maximum value. + /// + /// Max is required if precision is set. + public BsonValue Max => _max; + /// + /// Gets the precision. + /// + /// + /// Precision may only be set for double or decimal128. + /// + public int? Precision => _precision; + /// + /// Gets the sparsity. + /// + public long Sparsity => _sparsity; + + // internal methods + internal BsonDocument CreateDocument() => + new BsonDocument + { + { "min", _min, _min != null }, + { "max", _max, _max != null }, + { "precision", _precision, _precision != null }, + { "sparsity", _sparsity } + }; + } + /// /// Encryption options for explicit encryption. /// @@ -38,6 +105,7 @@ private static string ConvertEnumAlgorithmToString(EncryptionAlgorithm encryptio private readonly string _alternateKeyName; private readonly long? _contentionFactor; private readonly Guid? _keyId; + private readonly RangeOptions _rangeOptions; private readonly string _queryType; // constructors @@ -49,12 +117,14 @@ private static string ConvertEnumAlgorithmToString(EncryptionAlgorithm encryptio /// The key Id. /// [Beta] The contention factor. /// [Beta] The query type. + /// The range options. public EncryptOptions( string algorithm, Optional alternateKeyName = default, Optional keyId = default, Optional contentionFactor = default, - Optional queryType = default) + Optional queryType = default, + Optional rangeOptions = default) { Ensure.IsNotNull(algorithm, nameof(algorithm)); if (Enum.TryParse(algorithm, out var @enum)) @@ -69,6 +139,7 @@ public EncryptOptions( _alternateKeyName = alternateKeyName.WithDefault(null); _contentionFactor = contentionFactor.WithDefault(null); _keyId = keyId.WithDefault(null); + _rangeOptions = rangeOptions.WithDefault(null); _queryType = queryType.WithDefault(null); EnsureThatOptionsAreValid(); } @@ -81,18 +152,21 @@ public EncryptOptions( /// The key Id. /// [Beta] The contention factor. /// [Beta] The query type. + /// The range options. public EncryptOptions( EncryptionAlgorithm algorithm, Optional alternateKeyName = default, Optional keyId = default, Optional contentionFactor = default, - Optional queryType = default) + Optional queryType = default, + Optional rangeOptions = default) : this( algorithm: ConvertEnumAlgorithmToString(algorithm), alternateKeyName, keyId, contentionFactor, - queryType) + queryType, + rangeOptions) { } @@ -137,6 +211,19 @@ public EncryptOptions( /// public string QueryType => _queryType; + /// + /// Gets the range options. + /// + /// + /// The range options. + /// + /// + /// The Range algorithm is experimental only. It is not intended for public use. + /// RangeOpts specifies index options for a Queryable Encryption field supporting "rangePreview" queries. + /// RangeOptions only applies when algorithm is "rangePreview". + /// + public RangeOptions RangeOptions => _rangeOptions; + /// /// Returns a new EncryptOptions instance with some settings changed. /// @@ -145,20 +232,23 @@ public EncryptOptions( /// The keyId. /// [Beta] The contention factor. /// [Beta] The query type. + /// The range options. /// A new EncryptOptions instance. public EncryptOptions With( Optional algorithm = default, Optional alternateKeyName = default, Optional keyId = default, Optional contentionFactor = default, - Optional queryType = default) + Optional queryType = default, + Optional rangeOptions = default) { return new EncryptOptions( algorithm: algorithm.WithDefault(_algorithm), alternateKeyName: alternateKeyName.WithDefault(_alternateKeyName), keyId: keyId.WithDefault(_keyId), contentionFactor: contentionFactor.WithDefault(_contentionFactor), - queryType: queryType.WithDefault(_queryType)); + queryType: queryType.WithDefault(_queryType), + rangeOptions: rangeOptions.WithDefault(_rangeOptions)); } // private methods @@ -166,8 +256,9 @@ private void EnsureThatOptionsAreValid() { Ensure.That(!(!_keyId.HasValue && _alternateKeyName == null), "Key Id and AlternateKeyName may not both be null."); Ensure.That(!(_keyId.HasValue && _alternateKeyName != null), "Key Id and AlternateKeyName may not both be set."); - Ensure.That(!(_contentionFactor.HasValue && _algorithm != EncryptionAlgorithm.Indexed.ToString()), "ContentionFactor only applies for Indexed algorithm."); - Ensure.That(!(_queryType != null && _algorithm != EncryptionAlgorithm.Indexed.ToString()), "QueryType only applies for Indexed algorithm."); + Ensure.That(!(_contentionFactor.HasValue && (_algorithm != EncryptionAlgorithm.Indexed.ToString() && _algorithm != EncryptionAlgorithm.RangePreview.ToString())), "ContentionFactor only applies for Indexed or RangePreview algorithm."); + Ensure.That(!(_queryType != null && (_algorithm != EncryptionAlgorithm.Indexed.ToString() && _algorithm != EncryptionAlgorithm.RangePreview.ToString())), "QueryType only applies for Indexed or RangePreview algorithm."); + Ensure.That(!(_rangeOptions != null && _algorithm != EncryptionAlgorithm.RangePreview.ToString()), "RangeOptions only applies for RangePreview algorithm."); } } } diff --git a/src/MongoDB.Driver/Encryption/EncryptionAlgorithm.cs b/src/MongoDB.Driver/Encryption/EncryptionAlgorithm.cs index 84e91229706..c94bcab79db 100644 --- a/src/MongoDB.Driver/Encryption/EncryptionAlgorithm.cs +++ b/src/MongoDB.Driver/Encryption/EncryptionAlgorithm.cs @@ -39,6 +39,15 @@ public enum EncryptionAlgorithm /// /// [Beta] Unindexed algorithm. /// - Unindexed + Unindexed, + /// + /// RangePreview algorithm. + /// + /// + /// The Range algorithm is experimental only. It is not intended for public use. + /// To insert or query with an "Indexed" encrypted payload, use a MongoClient configured with AutoEncryptionOptions. + /// AutoEncryptionOptions.BypassQueryAnalysis may be true. AutoEncryptionOptions.BypassAutoEncryption must be false. + /// + RangePreview } } diff --git a/src/MongoDB.Driver/Encryption/ExplicitEncryptionLibMongoCryptController.cs b/src/MongoDB.Driver/Encryption/ExplicitEncryptionLibMongoCryptController.cs index 9c715a9fb2a..bb0e3c8a3aa 100644 --- a/src/MongoDB.Driver/Encryption/ExplicitEncryptionLibMongoCryptController.cs +++ b/src/MongoDB.Driver/Encryption/ExplicitEncryptionLibMongoCryptController.cs @@ -19,7 +19,6 @@ using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; -using MongoDB.Bson.IO; using MongoDB.Bson.Serialization; using MongoDB.Driver.Core.Misc; using MongoDB.Libmongocrypt; @@ -32,11 +31,9 @@ internal sealed class ExplicitEncryptionLibMongoCryptController : LibMongoCryptC public ExplicitEncryptionLibMongoCryptController( CryptClient cryptClient, ClientEncryptionOptions clientEncryptionOptions) - : base( - Ensure.IsNotNull(cryptClient, nameof(cryptClient)), + : base(cryptClient, Ensure.IsNotNull(Ensure.IsNotNull(clientEncryptionOptions, nameof(clientEncryptionOptions)).KeyVaultClient, nameof(clientEncryptionOptions.KeyVaultClient)), - Ensure.IsNotNull(Ensure.IsNotNull(clientEncryptionOptions, nameof(clientEncryptionOptions)).KeyVaultNamespace, nameof(clientEncryptionOptions.KeyVaultNamespace)), - Ensure.IsNotNull(Ensure.IsNotNull(clientEncryptionOptions, nameof(clientEncryptionOptions)).TlsOptions, nameof(clientEncryptionOptions.TlsOptions))) + clientEncryptionOptions) { } @@ -213,9 +210,10 @@ public async Task DeleteKeyAsync(Guid id, CancellationToken cancel } } - public BsonBinaryData EncryptField( + public BsonValue EncryptField( BsonValue value, EncryptOptions encryptOptions, + bool isExpressionMode, CancellationToken cancellationToken) { Ensure.IsNotNull(value, nameof(value)); @@ -231,12 +229,14 @@ public BsonBinaryData EncryptField( queryType: encryptOptions.QueryType, contentionFactor: encryptOptions.ContentionFactor, encryptOptions.Algorithm, - wrappedValueBytes); + wrappedValueBytes, + ToBsonIfNotNull(encryptOptions?.RangeOptions?.CreateDocument()), + isExpressionMode); using (context) { var wrappedBytes = ProcessStates(context, databaseName: null, cancellationToken); - return UnwrapValue(wrappedBytes).AsBsonBinaryData; + return UnwrapValue(wrappedBytes); } } catch (Exception ex) @@ -245,9 +245,10 @@ public BsonBinaryData EncryptField( } } - public async Task EncryptFieldAsync( + public async Task EncryptFieldAsync( BsonValue value, EncryptOptions encryptOptions, + bool isExpressionMode, CancellationToken cancellationToken) { Ensure.IsNotNull(value, nameof(value)); @@ -263,12 +264,14 @@ public async Task EncryptFieldAsync( queryType: encryptOptions.QueryType, contentionFactor: encryptOptions.ContentionFactor, encryptOptions.Algorithm, - wrappedValueBytes); + wrappedValueBytes, + ToBsonIfNotNull(encryptOptions?.RangeOptions?.CreateDocument()), + isExpressionMode); using (context) { var wrappedBytes = await ProcessStatesAsync(context, databaseName: null, cancellationToken).ConfigureAwait(false); - return UnwrapValue(wrappedBytes).AsBsonBinaryData; + return UnwrapValue(wrappedBytes); } } catch (Exception ex) @@ -547,23 +550,6 @@ private BsonValue RenderFilter(FilterDefinition filter) return filter.Render(serializer, registry); } - private byte[] ToBsonIfNotNull(BsonValue value) - { - if (value != null) - { - var writerSettings = BsonBinaryWriterSettings.Defaults.Clone(); -#pragma warning disable 618 - if (BsonDefaults.GuidRepresentationMode == GuidRepresentationMode.V2) - { - writerSettings.GuidRepresentation = GuidRepresentation.Unspecified; - } -#pragma warning restore 618 - return value.ToBson(writerSettings: writerSettings); - } - - return null; - } - private Guid UnwrapKeyId(RawBsonDocument wrappedKeyDocument) { var keyId = wrappedKeyDocument["_id"].AsBsonBinaryData; diff --git a/src/MongoDB.Driver/Encryption/IEncryptionOptions.cs b/src/MongoDB.Driver/Encryption/IEncryptionOptions.cs new file mode 100644 index 00000000000..bf020a2d15c --- /dev/null +++ b/src/MongoDB.Driver/Encryption/IEncryptionOptions.cs @@ -0,0 +1,28 @@ +/* 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.Collections.Generic; + +namespace MongoDB.Driver.Encryption +{ + internal interface IEncryptionOptions + { + CollectionNamespace KeyVaultNamespace { get; } + + IReadOnlyDictionary> KmsProviders { get; } + + IReadOnlyDictionary TlsOptions { get; } + } +} diff --git a/src/MongoDB.Driver/Encryption/LibMongoCryptControllerBase.cs b/src/MongoDB.Driver/Encryption/LibMongoCryptControllerBase.cs index 3a0e67d2bf0..e7464d3d8ee 100644 --- a/src/MongoDB.Driver/Encryption/LibMongoCryptControllerBase.cs +++ b/src/MongoDB.Driver/Encryption/LibMongoCryptControllerBase.cs @@ -16,6 +16,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Net; using System.Net.Sockets; using System.Text.RegularExpressions; @@ -23,6 +24,7 @@ using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.IO; +using MongoDB.Driver.Core.Authentication.External; using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.Connections; using MongoDB.Driver.Core.Misc; @@ -39,24 +41,29 @@ internal abstract class LibMongoCryptControllerBase protected readonly CollectionNamespace _keyVaultNamespace; // private fields - private readonly IReadOnlyDictionary _tlsOptions; + private readonly IReadOnlyDictionary> _kmsProviders; private readonly IStreamFactory _networkStreamFactory; + private readonly IReadOnlyDictionary _tlsOptions; // constructors protected LibMongoCryptControllerBase( CryptClient cryptClient, IMongoClient keyVaultClient, - CollectionNamespace keyVaultNamespace, - IReadOnlyDictionary tlsOptions) + IEncryptionOptions encryptionOptions) { - _cryptClient = cryptClient; - _keyVaultClient = keyVaultClient; // _keyVaultClient might not be fully constructed at this point, don't call any instance methods on it yet - _keyVaultNamespace = keyVaultNamespace; + Ensure.IsNotNull(encryptionOptions, nameof(encryptionOptions)); + _cryptClient = Ensure.IsNotNull(cryptClient, nameof(cryptClient)); + _keyVaultClient = Ensure.IsNotNull(keyVaultClient, nameof(keyVaultClient)); // _keyVaultClient might not be fully constructed at this point, don't call any instance methods on it yet + _keyVaultNamespace = Ensure.IsNotNull(encryptionOptions.KeyVaultNamespace, nameof(encryptionOptions.KeyVaultNamespace)); _keyVaultCollection = new Lazy>(GetKeyVaultCollection); // delay use _keyVaultClient + _kmsProviders = Ensure.IsNotNull(encryptionOptions.KmsProviders, nameof(encryptionOptions.KmsProviders)); _networkStreamFactory = new NetworkStreamFactory(); - _tlsOptions = Ensure.IsNotNull(tlsOptions, nameof(tlsOptions)); + _tlsOptions = Ensure.IsNotNull(encryptionOptions.TlsOptions, nameof(encryptionOptions.TlsOptions)); } + // public properties + public IMongoClient KeyVaultClient => _keyVaultClient; + // protected methods protected void FeedResult(CryptContext context, BsonDocument document) { @@ -99,6 +106,9 @@ protected virtual void ProcessState(CryptContext context, string databaseName, C case CryptContext.StateCode.MONGOCRYPT_CTX_NEED_MONGO_KEYS: ProcessNeedMongoKeysState(context, cancellationToken); break; + case CryptContext.StateCode.MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS: + ProcessNeedKmsCredentials(context, cancellationToken); + break; default: throw new InvalidOperationException($"Unexpected context state: {context.State}."); } @@ -114,6 +124,9 @@ protected virtual async Task ProcessStateAsync(CryptContext context, string data case CryptContext.StateCode.MONGOCRYPT_CTX_NEED_MONGO_KEYS: await ProcessNeedMongoKeysStateAsync(context, cancellationToken).ConfigureAwait(false); break; + case CryptContext.StateCode.MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS: + await ProcessNeedKmsCredentialsAsync(context, cancellationToken).ConfigureAwait(false); + break; default: throw new InvalidOperationException($"Unexpected context state: {context.State}."); } @@ -153,6 +166,23 @@ protected async Task ProcessStatesAsync(CryptContext context, string dat return result; } + protected byte[] ToBsonIfNotNull(BsonValue value) + { + if (value != null) + { + var writerSettings = BsonBinaryWriterSettings.Defaults.Clone(); +#pragma warning disable 618 + if (BsonDefaults.GuidRepresentationMode == GuidRepresentationMode.V2) + { + writerSettings.GuidRepresentation = GuidRepresentation.Unspecified; + } +#pragma warning restore 618 + return value.ToBson(writerSettings: writerSettings); + } + + return null; + } + // private methods private IMongoCollection GetKeyVaultCollection() { @@ -231,6 +261,32 @@ private void ProcessNeedMongoKeysState(CryptContext context, CancellationToken c FeedResults(context, results); } + private void ProcessNeedKmsCredentials(CryptContext context, CancellationToken cancellationToken) => + // inner machinery in the below method doesn't support sync approach + ProcessNeedKmsCredentialsAsync(context, cancellationToken).GetAwaiter().GetResult(); + + private async Task ProcessNeedKmsCredentialsAsync(CryptContext context, CancellationToken cancellationToken) + { + var newCredentialsList = new List(); + foreach (var kmsProvider in _kmsProviders.Where(k => k.Value.Count == 0)) + { + IExternalCredentials credentialsBody = kmsProvider.Key switch + { + "aws" => await ExternalCredentialsAuthenticators.Instance.Aws.CreateCredentialsFromExternalSourceAsync(cancellationToken).ConfigureAwait(false), + "azure" => await ExternalCredentialsAuthenticators.Instance.Azure.CreateCredentialsFromExternalSourceAsync(cancellationToken).ConfigureAwait(false), + "gcp" => await ExternalCredentialsAuthenticators.Instance.Gcp.CreateCredentialsFromExternalSourceAsync(cancellationToken).ConfigureAwait(false), + _ => null, + }; + + if (credentialsBody != null) + { + newCredentialsList.Add(new BsonElement(kmsProvider.Key, credentialsBody.GetKmsCredentials())); + } + } + + context.SetCredentials(ToBsonIfNotNull(new BsonDocument(newCredentialsList))); + } + private async Task ProcessNeedMongoKeysStateAsync(CryptContext context, CancellationToken cancellationToken) { var filterBytes = context.GetOperation().ToArray(); diff --git a/src/MongoDB.Driver/Encryption/MongoEncryptionCreateCollectionException.cs b/src/MongoDB.Driver/Encryption/MongoEncryptionCreateCollectionException.cs new file mode 100644 index 00000000000..9a581233538 --- /dev/null +++ b/src/MongoDB.Driver/Encryption/MongoEncryptionCreateCollectionException.cs @@ -0,0 +1,69 @@ +/* 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.Runtime.Serialization; +using MongoDB.Bson; + +namespace MongoDB.Driver.Encryption +{ + /// + /// Represents an encryption exception. + /// + [Serializable] + public class MongoEncryptionCreateCollectionException : MongoEncryptionException + { + private readonly BsonDocument _encryptedFields; + + /// + /// Initializes a new instance of the class. + /// + /// The inner exception. + /// The encrypted fields. + public MongoEncryptionCreateCollectionException(Exception innerException, BsonDocument encryptedFields) + : base(innerException) + { + _encryptedFields = encryptedFields; + } + + /// + /// Initializes a new instance of the class (this overload used by deserialization). + /// + /// The SerializationInfo. + /// The StreamingContext. + protected MongoEncryptionCreateCollectionException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + _encryptedFields = (BsonDocument)info.GetValue(nameof(_encryptedFields), typeof(BsonDocument)); + } + + /// + /// The encrypted fields. + /// + public BsonDocument EncryptedFields => _encryptedFields; + + // public methods + /// + /// Gets the object data. + /// + /// The information. + /// The context. + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + info.AddValue(nameof(_encryptedFields), _encryptedFields); + } + } +} diff --git a/src/MongoDB.Driver/Encryption/MongoEncryptionException.cs b/src/MongoDB.Driver/Encryption/MongoEncryptionException.cs index 5e264539c07..78b6ca522db 100644 --- a/src/MongoDB.Driver/Encryption/MongoEncryptionException.cs +++ b/src/MongoDB.Driver/Encryption/MongoEncryptionException.cs @@ -15,7 +15,6 @@ using System; using System.Runtime.Serialization; -using MongoDB.Driver.Core.Misc; namespace MongoDB.Driver.Encryption { diff --git a/src/MongoDB.Driver/Encryption/MongocryptdFactory.cs b/src/MongoDB.Driver/Encryption/MongocryptdFactory.cs index f6b22d421ac..a39c5fa90b7 100644 --- a/src/MongoDB.Driver/Encryption/MongocryptdFactory.cs +++ b/src/MongoDB.Driver/Encryption/MongocryptdFactory.cs @@ -20,13 +20,13 @@ using System.IO; using System.Linq; using System.Reflection; +using MongoDB.Driver.Core; using MongoDB.Driver.Core.Misc; namespace MongoDB.Driver.Encryption { - internal static class EncryptionExtraOptionsValidator + internal static class EncryptionExtraOptionsHelper { - #region static private static readonly Dictionary __supportedExtraOptions = new Dictionary { { "cryptSharedLibPath", new [] { typeof(string) } }, @@ -36,7 +36,6 @@ internal static class EncryptionExtraOptionsValidator { "mongocryptdSpawnPath", new [] { typeof(string) } }, { "mongocryptdSpawnArgs", new [] { typeof(string), typeof(IEnumerable) } } }; - #endregion public static void EnsureThatExtraOptionsAreValid(IReadOnlyDictionary extraOptions) { @@ -63,6 +62,13 @@ public static void EnsureThatExtraOptionsAreValid(IReadOnlyDictionary dict) => + dict.GetValueOrDefault("cryptSharedLibPath"); + + public static bool? ExtractCryptSharedLibRequired(IReadOnlyDictionary dict) => + dict.GetValueOrDefault("cryptSharedLibRequired"); + } internal class MongocryptdFactory @@ -154,7 +160,7 @@ private bool ShouldMongocryptdBeSpawned(out string path, out string args) case IEnumerable enumerable: foreach (var item in enumerable) { - args += $"--{item.ToString().TrimStart('-')} "; + args += $"--{item.ToString().TrimStart(' ').TrimStart('-')} "; } break; default: diff --git a/src/MongoDB.Driver/FieldDefinition.cs b/src/MongoDB.Driver/FieldDefinition.cs index 2875778684a..5bb86181e4c 100644 --- a/src/MongoDB.Driver/FieldDefinition.cs +++ b/src/MongoDB.Driver/FieldDefinition.cs @@ -143,7 +143,7 @@ public abstract class FieldDefinition /// A . public virtual RenderedFieldDefinition Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) { - return Render(documentSerializer, serializerRegistry, LinqProvider.V2); + return Render(documentSerializer, serializerRegistry, LinqProvider.V3); } /// @@ -188,7 +188,7 @@ public abstract class FieldDefinition /// A . public virtual RenderedFieldDefinition Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) { - return Render(documentSerializer, serializerRegistry, LinqProvider.V2); + return Render(documentSerializer, serializerRegistry, LinqProvider.V3); } /// @@ -203,7 +203,7 @@ public virtual RenderedFieldDefinition Render( IBsonSerializerRegistry serializerRegistry, bool allowScalarValueForArrayField) { - return Render(documentSerializer, serializerRegistry, LinqProvider.V2, allowScalarValueForArrayField); + return Render(documentSerializer, serializerRegistry, LinqProvider.V3, allowScalarValueForArrayField); } /// diff --git a/src/MongoDB.Driver/FilterDefinition.cs b/src/MongoDB.Driver/FilterDefinition.cs index 1e8e1c94f64..e17e02a33ca 100644 --- a/src/MongoDB.Driver/FilterDefinition.cs +++ b/src/MongoDB.Driver/FilterDefinition.cs @@ -46,7 +46,7 @@ public static FilterDefinition Empty /// A . public virtual BsonDocument Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) { - return Render(documentSerializer, serializerRegistry, LinqProvider.V2); + return Render(documentSerializer, serializerRegistry, LinqProvider.V3); } /// diff --git a/src/MongoDB.Driver/FilteredMongoCollectionBase.cs b/src/MongoDB.Driver/FilteredMongoCollectionBase.cs index e86475207fd..468a711bd79 100644 --- a/src/MongoDB.Driver/FilteredMongoCollectionBase.cs +++ b/src/MongoDB.Driver/FilteredMongoCollectionBase.cs @@ -404,7 +404,7 @@ private IEnumerable> CombineModelFilters(IEnumerable CreateFilteredPipeline(PipelineDefinition pipeline) diff --git a/src/MongoDB.Driver/GroupForLinq3Result.cs b/src/MongoDB.Driver/GroupForLinq3Result.cs deleted file mode 100644 index a5288aa96bc..00000000000 --- a/src/MongoDB.Driver/GroupForLinq3Result.cs +++ /dev/null @@ -1,57 +0,0 @@ -/* 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.Linq; - -namespace MongoDB.Driver -{ - /// - /// Represents the return result from PipelineDefinitionBuilder.GroupForLinq3 method. - /// - /// The type of the input documents. - /// The type of the values. - /// The type of the output documents. - public class GroupForLinq3Result - { - internal GroupForLinq3Result(PipelineStageDefinition> groupStage, PipelineStageDefinition, TOutput> projectStage) - { - GroupStage = groupStage; - ProjectStage = projectStage; - } - - /// - /// The resulting group stage. - /// - public PipelineStageDefinition> GroupStage { get; } - - /// - /// The resulting project stage. - /// - public PipelineStageDefinition, TOutput> ProjectStage { get; } - - /// - /// Deconstructs this class into its components. - /// - /// The group stage. - /// The project stage. - public void Deconstruct( - out PipelineStageDefinition> groupStage, - out PipelineStageDefinition, TOutput> projectStage) - { - groupStage = GroupStage; - projectStage = ProjectStage; - } - } -} diff --git a/src/MongoDB.Driver/IAggregateFluent.cs b/src/MongoDB.Driver/IAggregateFluent.cs index 703dba4956d..cca33c9e1c9 100644 --- a/src/MongoDB.Driver/IAggregateFluent.cs +++ b/src/MongoDB.Driver/IAggregateFluent.cs @@ -19,6 +19,7 @@ using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.Serialization; +using MongoDB.Driver.Search; namespace MongoDB.Driver { @@ -215,7 +216,7 @@ IAggregateFluent GraphLookup /// The limit. /// The fluent aggregate interface. - IAggregateFluent Limit(int limit); + IAggregateFluent Limit(long limit); /// /// Appends a lookup stage to the pipeline. @@ -353,6 +354,37 @@ IAggregateFluent Lookup SetWindowFields( AggregateExpressionDefinition, TWindowFields> output); + /// + /// Appends a $search stage to the pipeline. + /// + /// The search definition. + /// The highlight options. + /// The index name. + /// The count options. + /// + /// Flag that specifies whether to perform a full document lookup on the backend database + /// or return only stored source fields directly from Atlas Search. + /// + /// The fluent aggregate interface. + IAggregateFluent Search( + SearchDefinition searchDefinition, + SearchHighlightOptions highlight = null, + string indexName = null, + SearchCountOptions count = null, + bool returnStoredSource = false); + + /// + /// Appends a $searchMeta stage to the pipeline. + /// + /// The search definition. + /// The index name. + /// The count options. + /// The fluent aggregate interface. + IAggregateFluent SearchMeta( + SearchDefinition searchDefinition, + string indexName = null, + SearchCountOptions count = null); + /// /// Appends a $setWindowFields to the pipeline. /// @@ -384,7 +416,7 @@ IAggregateFluent SetWindowFields( /// /// The number of documents to skip. /// The fluent aggregate interface. - IAggregateFluent Skip(int skip); + IAggregateFluent Skip(long skip); /// /// Appends a sort stage to the pipeline. diff --git a/src/MongoDB.Driver/IAggregateFluentExtensions.cs b/src/MongoDB.Driver/IAggregateFluentExtensions.cs index 03fc3b0b6f7..6debce899fd 100644 --- a/src/MongoDB.Driver/IAggregateFluentExtensions.cs +++ b/src/MongoDB.Driver/IAggregateFluentExtensions.cs @@ -95,7 +95,7 @@ public static IAggregateFluent> BucketAuto - /// Appends a $bucketAuto stage to the pipeline. + /// Appends a $bucketAuto stage to the pipeline (this overload can only be used with LINQ3). /// /// The type of the result. /// The type of the value. @@ -110,13 +110,46 @@ public static IAggregateFluent BucketAuto aggregate, Expression> groupBy, int buckets, - Expression, TNewResult>> output, + Expression, TResult>, TNewResult>> output, AggregateBucketAutoOptions options = null) { Ensure.IsNotNull(aggregate, nameof(aggregate)); + if (aggregate.Database.Client.Settings.LinqProvider != LinqProvider.V3) + { + throw new InvalidOperationException("This overload of BucketAuto can only be used with LINQ3."); + } + return aggregate.AppendStage(PipelineStageDefinitionBuilder.BucketAuto(groupBy, buckets, output, options)); } + /// + /// Appends a $bucketAuto stage to the pipeline (this method can only be used with LINQ2). + /// + /// The type of the result. + /// The type of the value. + /// The type of the new result. + /// The aggregate. + /// The expression providing the value to group by. + /// The number of buckets. + /// The output projection. + /// The options (optional). + /// The fluent aggregate interface. + public static IAggregateFluent BucketAutoForLinq2( + this IAggregateFluent aggregate, + Expression> groupBy, + int buckets, + Expression, TNewResult>> output, // the IGrouping for BucketAuto has been wrong all along, only fixing it for LINQ3 + AggregateBucketAutoOptions options = null) + { + Ensure.IsNotNull(aggregate, nameof(aggregate)); + if (aggregate.Database.Client.Settings.LinqProvider != LinqProvider.V2) + { + throw new InvalidOperationException("The BucketAutoForLinq2 method can only be used with LINQ2."); + } + + return aggregate.AppendStage(PipelineStageDefinitionBuilder.BucketAutoForLinq2(groupBy, buckets, output, options)); + } + /// /// Appends a $densify stage to the pipeline. /// @@ -159,6 +192,40 @@ public static IAggregateFluent Densify( return aggregate.AppendStage(PipelineStageDefinitionBuilder.Densify(field, range, partitionByFields)); } + /// + /// Appends a $documents stage to the pipeline. + /// + /// The type of the result. + /// The aggregate. + /// The documents. + /// The document serializer. + /// The fluent aggregate interface. + public static IAggregateFluent Documents( + this IAggregateFluent aggregate, + AggregateExpressionDefinition> documents, + IBsonSerializer documentSerializer = null) + { + Ensure.IsNotNull(aggregate, nameof(aggregate)); + return aggregate.AppendStage(PipelineStageDefinitionBuilder.Documents(documents, documentSerializer)); + } + + /// + /// Appends a $documents stage to the pipeline. + /// + /// The type of the result. + /// The aggregate. + /// The documents. + /// The document serializer. + /// The fluent aggregate interface. + public static IAggregateFluent Documents( + this IAggregateFluent aggregate, + IEnumerable documents, + IBsonSerializer documentSerializer = null) + { + Ensure.IsNotNull(aggregate, nameof(aggregate)); + return aggregate.AppendStage(PipelineStageDefinitionBuilder.Documents(documents, documentSerializer)); + } + /// /// Appends a $facet stage to the pipeline. /// @@ -362,15 +429,7 @@ public static IAggregateFluent Group(this IAggregateFluen public static IAggregateFluent Group(this IAggregateFluent aggregate, Expression> id, Expression, TNewResult>> group) { Ensure.IsNotNull(aggregate, nameof(aggregate)); - if (aggregate.Database.Client.Settings.LinqProvider == LinqProvider.V2) - { - return aggregate.AppendStage(PipelineStageDefinitionBuilder.Group(id, group)); - } - else - { - var (groupStage, projectStage) = PipelineStageDefinitionBuilder.GroupForLinq3(id, group); - return aggregate.AppendStage(groupStage).AppendStage(projectStage); - } + return aggregate.AppendStage(PipelineStageDefinitionBuilder.Group(id, group)); } /// diff --git a/src/MongoDB.Driver/IMongoDatabaseExtensions.cs b/src/MongoDB.Driver/IMongoDatabaseExtensions.cs index 41cd63452e0..e8a41affbe0 100644 --- a/src/MongoDB.Driver/IMongoDatabaseExtensions.cs +++ b/src/MongoDB.Driver/IMongoDatabaseExtensions.cs @@ -17,7 +17,7 @@ using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Driver.Core.Misc; -using MongoDB.Driver.Core.Operations; +using MongoDB.Driver.Linq; namespace MongoDB.Driver { @@ -56,6 +56,34 @@ public static IAggregateFluent Aggregate(this IMongoDatabase da return new DatabaseAggregateFluent(session, database, emptyPipeline, options ?? new AggregateOptions()); } + /// + /// Creates a queryable source of documents. + /// + /// The database. + /// The aggregate options + /// A queryable source of documents. + public static IMongoQueryable AsQueryable(this IMongoDatabase database, AggregateOptions aggregateOptions = null) + { + Ensure.IsNotNull(database, nameof(database)); + + return AsQueryableHelper(database, session: null, aggregateOptions); + } + + /// + /// Creates a queryable source of documents. + /// + /// The collection. + /// The session. + /// The aggregate options + /// A queryable source of documents. + public static IMongoQueryable AsQueryable(this IMongoDatabase database, IClientSessionHandle session, AggregateOptions aggregateOptions = null) + { + Ensure.IsNotNull(database, nameof(database)); + Ensure.IsNotNull(session, nameof(session)); + + return AsQueryableHelper(database, session, aggregateOptions); + } + /// /// Watches changes on all collection in a database. /// @@ -137,5 +165,13 @@ public static Task>> Watc var emptyPipeline = new EmptyPipelineDefinition>(); return database.WatchAsync(session, emptyPipeline, options, cancellationToken); } + + // private static methods + private static IMongoQueryable AsQueryableHelper(IMongoDatabase database, IClientSessionHandle session, AggregateOptions aggregateOptions) + { + var linqProvider = database.Client.Settings.LinqProvider; + aggregateOptions = aggregateOptions ?? new AggregateOptions(); + return linqProvider.GetAdapter().AsQueryable(database, session, aggregateOptions); + } } } diff --git a/src/MongoDB.Driver/IndexKeysDefinition.cs b/src/MongoDB.Driver/IndexKeysDefinition.cs index d0a4e1eeacb..8190ad36ad7 100644 --- a/src/MongoDB.Driver/IndexKeysDefinition.cs +++ b/src/MongoDB.Driver/IndexKeysDefinition.cs @@ -35,7 +35,7 @@ public abstract class IndexKeysDefinition /// A . public virtual BsonDocument Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) { - return Render(documentSerializer, serializerRegistry, LinqProvider.V2); + return Render(documentSerializer, serializerRegistry, LinqProvider.V3); } /// diff --git a/src/MongoDB.Driver/Linq/DateTimeExtensions.cs b/src/MongoDB.Driver/Linq/DateTimeExtensions.cs index 45e12b65722..71d0731b80f 100644 --- a/src/MongoDB.Driver/Linq/DateTimeExtensions.cs +++ b/src/MongoDB.Driver/Linq/DateTimeExtensions.cs @@ -279,6 +279,18 @@ public static DateTime Subtract(this DateTime @this, long value, DateTimeUnit un throw new InvalidOperationException("This DateTime.Subtract method is only intended to be used in LINQ queries."); } + /// + /// Converts a DateTime value to a string. + /// + /// The DateTime value. + /// The format string (optional, can be null). + /// The timezone to use in the returned string (optional, can be null). + /// The DateTime value converted to a string. + public static string ToString(this DateTime @this, string format, string timezone) + { + throw new InvalidOperationException("This DateTime.ToString method is only intended to be used in LINQ queries."); + } + /// /// Truncates a DateTime value to the specified unit. /// @@ -315,5 +327,26 @@ public static DateTime Truncate(this DateTime @this, DateTimeUnit unit, long bin { throw new InvalidOperationException("This DateTime.Truncate method is only intended to be used in LINQ queries."); } + + /// + /// Returns the week number of a specified DateTime value. + /// + /// The DateTime value. + /// The week number of a specified DateTime value. + public static int Week(this DateTime @this) + { + throw new InvalidOperationException("This DateTime.Week method is only intended to be used in LINQ queries."); + } + + /// + /// Returns the week number of a specified DateTime value. + /// + /// The DateTime value. + /// The timezone to use (optional, can be null). + /// The week number of a specified DateTime value. + public static int Week(this DateTime @this, string timezone) + { + throw new InvalidOperationException("This DateTime.Week method is only intended to be used in LINQ queries."); + } } } diff --git a/src/MongoDB.Driver/Linq/IMongoQueryProvider.cs b/src/MongoDB.Driver/Linq/IMongoQueryProvider.cs index 6978e7974f7..751114e28a6 100644 --- a/src/MongoDB.Driver/Linq/IMongoQueryProvider.cs +++ b/src/MongoDB.Driver/Linq/IMongoQueryProvider.cs @@ -32,9 +32,9 @@ internal interface IMongoQueryProvider : IQueryProvider CollectionNamespace CollectionNamespace { get; } /// - /// Gets the collection document serializer. + /// Gets the pipeline input serializer (the DocumentSerializer for collection queries and NoPipelineInputSerializer for database queries). /// - IBsonSerializer CollectionDocumentSerializer { get; } + IBsonSerializer PipelineInputSerializer { get; } /// /// Gets the execution model. diff --git a/src/MongoDB.Driver/Linq/Linq2Implementation/LinqProviderAdapterV2.cs b/src/MongoDB.Driver/Linq/Linq2Implementation/LinqProviderAdapterV2.cs index 2034fd74744..165f538ae41 100644 --- a/src/MongoDB.Driver/Linq/Linq2Implementation/LinqProviderAdapterV2.cs +++ b/src/MongoDB.Driver/Linq/Linq2Implementation/LinqProviderAdapterV2.cs @@ -36,6 +36,14 @@ internal override IMongoQueryable AsQueryable( return new MongoQueryableImpl(provider); } + internal override IMongoQueryable AsQueryable( + IMongoDatabase database, + IClientSessionHandle session, + AggregateOptions options) + { + throw new InvalidOperationException("LINQ2 does not support AsQueryable against a database."); + } + public override string ToString() => "V2"; internal override BsonValue TranslateExpressionToAggregateExpression( diff --git a/src/MongoDB.Driver/Linq/Linq2Implementation/MongoQueryProviderImpl.cs b/src/MongoDB.Driver/Linq/Linq2Implementation/MongoQueryProviderImpl.cs index 3cb4676a7dc..783b6a31e19 100644 --- a/src/MongoDB.Driver/Linq/Linq2Implementation/MongoQueryProviderImpl.cs +++ b/src/MongoDB.Driver/Linq/Linq2Implementation/MongoQueryProviderImpl.cs @@ -21,7 +21,6 @@ using System.Threading.Tasks; using MongoDB.Bson.Serialization; using MongoDB.Driver.Core.Misc; -using MongoDB.Driver.Linq; using MongoDB.Driver.Linq.Linq2Implementation.Processors; using MongoDB.Driver.Linq.Linq2Implementation.Processors.Pipeline; using MongoDB.Driver.Linq.Linq2Implementation.Translators; @@ -44,7 +43,7 @@ public MongoQueryProviderImpl(IMongoCollection collection, IClientSes public CollectionNamespace CollectionNamespace => _collection.CollectionNamespace; - public IBsonSerializer CollectionDocumentSerializer => _collection.DocumentSerializer; + public IBsonSerializer PipelineInputSerializer => _collection.DocumentSerializer; public IQueryable CreateQuery(Expression expression) { diff --git a/src/MongoDB.Driver/Linq/Linq2Implementation/Processors/Pipeline/PipelineBinder.cs b/src/MongoDB.Driver/Linq/Linq2Implementation/Processors/Pipeline/PipelineBinder.cs index 7a33ac08ceb..50fe969ee4c 100644 --- a/src/MongoDB.Driver/Linq/Linq2Implementation/Processors/Pipeline/PipelineBinder.cs +++ b/src/MongoDB.Driver/Linq/Linq2Implementation/Processors/Pipeline/PipelineBinder.cs @@ -80,8 +80,8 @@ protected override Expression BindNonMethodCall(Expression node) var queryable = (IMongoQueryable)((ConstantExpression)node).Value; var provider = (IMongoQueryProvider)queryable.Provider; return new PipelineExpression( - new CollectionExpression(provider.CollectionNamespace, provider.CollectionDocumentSerializer), - new DocumentExpression(provider.CollectionDocumentSerializer)); + new CollectionExpression(provider.CollectionNamespace, provider.PipelineInputSerializer), + new DocumentExpression(provider.PipelineInputSerializer)); } var message = string.Format("The expression tree is not supported: {0}", diff --git a/src/MongoDB.Driver/Linq/Linq2Implementation/Processors/SerializationBinder.cs b/src/MongoDB.Driver/Linq/Linq2Implementation/Processors/SerializationBinder.cs index 7187c157060..f5984633917 100644 --- a/src/MongoDB.Driver/Linq/Linq2Implementation/Processors/SerializationBinder.cs +++ b/src/MongoDB.Driver/Linq/Linq2Implementation/Processors/SerializationBinder.cs @@ -96,7 +96,7 @@ protected override Expression VisitConstant(ConstantExpression node) var provider = (IMongoQueryProvider)queryable.Provider; return new CollectionExpression( provider.CollectionNamespace, - provider.CollectionDocumentSerializer); + provider.PipelineInputSerializer); } return base.VisitConstant(node); diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/AstNodeType.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/AstNodeType.cs index d8f3a3b1864..5aed9e20b53 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/AstNodeType.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/AstNodeType.cs @@ -53,6 +53,7 @@ internal enum AstNodeType DateTruncExpression, DensifyStage, DerivativeOrIntegralWindowExpression, + DocumentsStage, ElemMatchFilterOperation, ExistsFilterOperation, ExponentialMovingAverageWindowExpression, diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/AstPipeline.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/AstPipeline.cs index 337392afe17..8c190004577 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/AstPipeline.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/AstPipeline.cs @@ -72,6 +72,15 @@ public AstPipeline ReplaceLastStage( return new AstPipeline(stages, newOutputSerializer); } + public AstPipeline ReplaceStagesAtEnd( + IBsonSerializer newOutputSerializer, + int numberOfStagesToReplace, + params AstStage[] newStages) + { + var stages = _stages.Take(_stages.Count - numberOfStagesToReplace).Concat(newStages); + return new AstPipeline(stages, newOutputSerializer); + } + public AstPipeline Update(IEnumerable stages) { if (stages == _stages) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstBinaryOperator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstBinaryOperator.cs index f1862d65ddb..2e4110f7ddc 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstBinaryOperator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstBinaryOperator.cs @@ -20,7 +20,7 @@ namespace MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions internal enum AstBinaryOperator { ArrayElemAt, - ATan2, + Atan2, Cmp, Divide, Eq, @@ -50,7 +50,7 @@ public static string Render(this AstBinaryOperator @operator) return @operator switch { AstBinaryOperator.ArrayElemAt => "$arrayElemAt", - AstBinaryOperator.ATan2 => "$atan2", + AstBinaryOperator.Atan2 => "$atan2", AstBinaryOperator.Cmp => "$cmp", AstBinaryOperator.Divide => "$divide", AstBinaryOperator.Eq => "$eq", diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs index 8545101f183..b71cf7b9dff 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs @@ -152,6 +152,11 @@ public static AstExpression Avg(AstExpression array) return new AstUnaryExpression(AstUnaryOperator.Avg, array); } + public static AstExpression Binary(AstBinaryOperator @operator, AstExpression arg1, AstExpression arg2) + { + return new AstBinaryExpression(@operator, arg1, arg2); + } + public static AstExpression BinaryWindowExpression(AstBinaryWindowOperator @operator, AstExpression arg1, AstExpression arg2, AstWindow window) { return new AstBinaryWindowExpression(@operator, arg1, arg2, window); @@ -240,26 +245,34 @@ public static AstExpression Constant(BsonValue value) public static AstExpression Convert(AstExpression input, AstExpression to, AstExpression onError = null, AstExpression onNull = null) { - return new AstConvertExpression(input, to, onError, onNull); - } + Ensure.IsNotNull(input, nameof(input)); + Ensure.IsNotNull(to, nameof(to)); - public static AstExpression Convert(AstExpression input, Type toType, AstExpression onError = null, AstExpression onNull = null) - { - Ensure.IsNotNull(toType, nameof(toType)); - var to = toType.FullName switch + if (to is AstConstantExpression toConstantExpression && + (toConstantExpression.Value as BsonString)?.Value is string toValue && + toValue != null && + onError == null && + onNull == null) { - "MongoDB.Bson.ObjectId" => "objectId", - "System.Boolean" => "bool", - "System.DateTime" => "date", - "System.Decimal" => "decimal", - "System.Double" => "double", - "System.Int32" => "int", - "System.Int64" => "long", - "System.String" => "string", - _ => throw new ArgumentException($"Invalid toType: {toType.FullName}.", nameof(toType)) - }; + var unaryOperator = toValue switch + { + "bool" => AstUnaryOperator.ToBool, + "date" => AstUnaryOperator.ToDate, + "decimal" => AstUnaryOperator.ToDecimal, + "double" => AstUnaryOperator.ToDouble, + "int" => AstUnaryOperator.ToInt, + "long" => AstUnaryOperator.ToLong, + "objectId" => AstUnaryOperator.ToObjectId, + "string" => AstUnaryOperator.ToString, + _ => (AstUnaryOperator?)null + }; + if (unaryOperator.HasValue) + { + return AstExpression.Unary(unaryOperator.Value, input); + } + } - return AstExpression.Convert(input, to, onError, onNull); + return new AstConvertExpression(input, to, onError, onNull); } public static AstExpression DateAdd( @@ -834,6 +847,11 @@ public static AstExpression Trunc(AstExpression arg) return new AstUnaryExpression(AstUnaryOperator.Trunc, arg); } + public static AstExpression Unary(AstUnaryOperator @operator, AstExpression arg) + { + return new AstUnaryExpression(@operator, arg); + } + public static AstAccumulatorExpression UnaryAccumulator(AstUnaryAccumulatorOperator @operator, AstExpression arg) { return new AstUnaryAccumulatorExpression(@operator, arg); diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstUnaryOperator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstUnaryOperator.cs index 1ab7c982fbe..2b6216113bb 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstUnaryOperator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstUnaryOperator.cs @@ -20,21 +20,22 @@ namespace MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions internal enum AstUnaryOperator { Abs, - ACos, - ACosh, + Acos, + Acosh, AddToSet, AllElementsTrue, AnyElementTrue, ArrayToObject, - ASin, - ASinh, - ATan, - ATanh, + Asin, + Asinh, + Atan, + Atanh, Avg, BinarySize, BsonSize, Ceil, Cos, + Cosh, DegreesToRadians, Exp, First, @@ -56,6 +57,7 @@ internal enum AstUnaryOperator ReverseArray, Round, Sin, + Sinh, Size, Sqrt, StdDevPop, @@ -64,6 +66,7 @@ internal enum AstUnaryOperator StrLenCP, Sum, Tan, + Tanh, ToBool, ToDate, ToDecimal, @@ -103,21 +106,22 @@ public static string Render(this AstUnaryOperator @operator) return @operator switch { AstUnaryOperator.Abs => "$abs", - AstUnaryOperator.ACos => "$acos", - AstUnaryOperator.ACosh => "$acosh", + AstUnaryOperator.Acos => "$acos", + AstUnaryOperator.Acosh => "$acosh", AstUnaryOperator.AddToSet => "$addToSet", AstUnaryOperator.AllElementsTrue => "$allElementsTrue", AstUnaryOperator.AnyElementTrue => "$anyElementTrue", AstUnaryOperator.ArrayToObject => "$arrayToObject", - AstUnaryOperator.ASin => "$asin", - AstUnaryOperator.ASinh => "$asinh", - AstUnaryOperator.ATan => "$atan", - AstUnaryOperator.ATanh => "$atanh", + AstUnaryOperator.Asin => "$asin", + AstUnaryOperator.Asinh => "$asinh", + AstUnaryOperator.Atan => "$atan", + AstUnaryOperator.Atanh => "$atanh", AstUnaryOperator.Avg => "$avg", AstUnaryOperator.BinarySize => "$binarySize", AstUnaryOperator.BsonSize => "$bsonSize", AstUnaryOperator.Ceil => "$ceil", AstUnaryOperator.Cos => "$cos", + AstUnaryOperator.Cosh => "$cosh", AstUnaryOperator.DegreesToRadians => "$degreesToRadians", AstUnaryOperator.Exp => "$exp", AstUnaryOperator.First => "$first", @@ -139,6 +143,7 @@ public static string Render(this AstUnaryOperator @operator) AstUnaryOperator.ReverseArray => "$reverseArray", AstUnaryOperator.Round => "$round", AstUnaryOperator.Sin => "$sin", + AstUnaryOperator.Sinh => "$sinh", AstUnaryOperator.Size => "$size", AstUnaryOperator.Sqrt => "$sqrt", AstUnaryOperator.StdDevPop => "$stdDevPop", @@ -147,6 +152,7 @@ public static string Render(this AstUnaryOperator @operator) AstUnaryOperator.StrLenCP => "$strLenCP", AstUnaryOperator.Sum => "$sum", AstUnaryOperator.Tan => "$tan", + AstUnaryOperator.Tanh => "$tanh", AstUnaryOperator.ToBool => "$toBool", AstUnaryOperator.ToDate => "$toDate", AstUnaryOperator.ToDecimal => "$toDecimal", diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Filters/AstFieldOperationFilter.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Filters/AstFieldOperationFilter.cs index b17079e4d45..5e06a7f6432 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Filters/AstFieldOperationFilter.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Filters/AstFieldOperationFilter.cs @@ -38,6 +38,11 @@ field.Serializer is IRepresentationConfigurable representationConfigurable && // normally an ExpressionNotSupported should have been thrown before reaching here throw new ArgumentException($"Field must be represented as a string for regex filter operations: {field.Path}", nameof(field)); } + + if (_field.Path == "@") + { + throw new ExpressionNotSupportedException("Field path cannot be \"@\" in AstFieldOperationFilter."); + } } public new AstFilterField Field => _field; diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Optimizers/AstGroupPipelineOptimizer.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Optimizers/AstGroupingPipelineOptimizer.cs similarity index 78% rename from src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Optimizers/AstGroupPipelineOptimizer.cs rename to src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Optimizers/AstGroupingPipelineOptimizer.cs index feca877be92..400ebd8522a 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Optimizers/AstGroupPipelineOptimizer.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Optimizers/AstGroupingPipelineOptimizer.cs @@ -24,32 +24,42 @@ namespace MongoDB.Driver.Linq.Linq3Implementation.Ast.Optimizers { - internal class AstGroupPipelineOptimizer + internal class AstGroupingPipelineOptimizer { #region static public static AstPipeline Optimize(AstPipeline pipeline) { - var optimizer = new AstGroupPipelineOptimizer(); + var optimizer = new AstGroupingPipelineOptimizer(); for (var i = 0; i < pipeline.Stages.Count; i++) { var stage = pipeline.Stages[i]; - if (stage is AstGroupStage groupStage) + if (IsGroupingStage(stage)) { - pipeline = optimizer.OptimizeGroupStage(pipeline, i, groupStage); + pipeline = optimizer.OptimizeGroupingStage(pipeline, i, stage); } } return pipeline; + + static bool IsGroupingStage(AstStage stage) + { + return stage.NodeType switch + { + AstNodeType.GroupStage or AstNodeType.BucketStage or AstNodeType.BucketAutoStage => true, + _ => false + }; + } } #endregion private readonly AccumulatorSet _accumulators = new AccumulatorSet(); + private AstExpression _element; // normally either "$$ROOT" or "$_v" - private AstPipeline OptimizeGroupStage(AstPipeline pipeline, int i, AstGroupStage groupStage) + private AstPipeline OptimizeGroupingStage(AstPipeline pipeline, int i, AstStage groupingStage) { try { - if (IsOptimizableGroupStage(groupStage)) + if (IsOptimizableGroupingStage(groupingStage, out _element)) { var followingStages = GetFollowingStagesToOptimize(pipeline, i + 1); if (followingStages == null) @@ -57,7 +67,7 @@ private AstPipeline OptimizeGroupStage(AstPipeline pipeline, int i, AstGroupStag return pipeline; } - var mappings = OptimizeGroupAndFollowingStages(groupStage, followingStages); + var mappings = OptimizeGroupingAndFollowingStages(groupingStage, followingStages); if (mappings.Length > 0) { return (AstPipeline)AstNodeReplacer.Replace(pipeline, mappings); @@ -71,23 +81,57 @@ private AstPipeline OptimizeGroupStage(AstPipeline pipeline, int i, AstGroupStag return pipeline; - static bool IsOptimizableGroupStage(AstGroupStage groupStage) + static bool IsOptimizableGroupingStage(AstStage groupingStage, out AstExpression element) { - // { $group : { _id : ?, _elements : { $push : "$$ROOT" } } } - if (groupStage.Fields.Count == 1) + if (groupingStage is AstGroupStage groupStage) { - var field = groupStage.Fields[0]; - if (field.Path == "_elements" && - field.Value is AstUnaryAccumulatorExpression unaryAccumulatorExpression && - unaryAccumulatorExpression.Operator == AstUnaryAccumulatorOperator.Push && - unaryAccumulatorExpression.Arg is AstVarExpression varExpression && - varExpression.Name == "ROOT") + // { $group : { _id : ?, _elements : { $push : element } } } + if (groupStage.Fields.Count == 1) { - return true; + var field = groupStage.Fields[0]; + return IsElementsPush(field, out element); } } + if (groupingStage is AstBucketStage bucketStage) + { + // { $bucket : { groupBy : ?, boundaries : ?, default : ?, output : { _elements : { $push : element } } } } + if (bucketStage.Output.Count == 1) + { + var output = bucketStage.Output[0]; + return IsElementsPush(output, out element); + } + } + + if (groupingStage is AstBucketAutoStage bucketAutoStage) + { + // { $bucketAuto : { groupBy : ?, buckets : ?, granularity : ?, output : { _elements : { $push : element } } } } + if (bucketAutoStage.Output.Count == 1) + { + var output = bucketAutoStage.Output[0]; + return IsElementsPush(output, out element); + } + } + + element = null; return false; + + static bool IsElementsPush(AstAccumulatorField field, out AstExpression element) + { + if ( + field.Path == "_elements" && + field.Value is AstUnaryAccumulatorExpression unaryAccumulatorExpression && + unaryAccumulatorExpression.Operator == AstUnaryAccumulatorOperator.Push) + { + element = unaryAccumulatorExpression.Arg; + return true; + } + else + { + element = null; + return false; + } + } } static List GetFollowingStagesToOptimize(AstPipeline pipeline, int from) @@ -134,7 +178,7 @@ static bool IsLastStageThatCanBeOptimized(AstStage stage) } } - private (AstNode, AstNode)[] OptimizeGroupAndFollowingStages(AstGroupStage groupStage, List followingStages) + private (AstNode, AstNode)[] OptimizeGroupingAndFollowingStages(AstStage groupingStage, List followingStages) { var mappings = new List<(AstNode, AstNode)>(); @@ -147,10 +191,21 @@ static bool IsLastStageThatCanBeOptimized(AstStage stage) } } - var newGroupStage = AstStage.Group(groupStage.Id, _accumulators); - mappings.Add((groupStage, newGroupStage)); + var newGroupingStage = CreateNewGroupingStage(groupingStage, _accumulators); + mappings.Add((groupingStage, newGroupingStage)); return mappings.ToArray(); + + static AstStage CreateNewGroupingStage(AstStage groupingStage, AccumulatorSet accumulators) + { + return groupingStage switch + { + AstGroupStage groupStage => AstStage.Group(groupStage.Id, accumulators), + AstBucketStage bucketStage => AstStage.Bucket(bucketStage.GroupBy, bucketStage.Boundaries, bucketStage.Default, accumulators), + AstBucketAutoStage bucketAutoStage => AstStage.BucketAuto(bucketAutoStage.GroupBy, bucketAutoStage.Buckets, bucketAutoStage.Granularity, accumulators), + _ => throw new Exception($"Unexpected {nameof(groupingStage)} node type: {groupingStage.NodeType}.") + }; + } } private AstStage OptimizeFollowingStage(AstStage stage) @@ -173,7 +228,7 @@ private AstStage OptimizeLimitStage(AstLimitStage stage) private AstStage OptimizeMatchStage(AstMatchStage stage) { - var optimizedFilter = AccumulatorMover.MoveAccumulators(_accumulators, stage.Filter); + var optimizedFilter = AccumulatorMover.MoveAccumulators(_accumulators, _element, stage.Filter); return stage.Update(optimizedFilter); } @@ -201,7 +256,7 @@ private AstProjectStageSpecification OptimizeProjectStageSpecification(AstProjec private AstProjectStageSpecification OptimizeProjectStageSetFieldSpecification(AstProjectStageSetFieldSpecification specification) { - var optimizedValue = AccumulatorMover.MoveAccumulators(_accumulators, specification.Value); + var optimizedValue = AccumulatorMover.MoveAccumulators(_accumulators, _element, specification.Value); return specification.Update(optimizedValue); } @@ -249,27 +304,29 @@ public string AddAccumulatorExpression(AstAccumulatorExpression value) private class AccumulatorMover : AstNodeVisitor { #region static - public static TNode MoveAccumulators(AccumulatorSet accumulators, TNode node) + public static TNode MoveAccumulators(AccumulatorSet accumulators, AstExpression element, TNode node) where TNode : AstNode { - var mover = new AccumulatorMover(accumulators); + var mover = new AccumulatorMover(accumulators, element); return mover.VisitAndConvert(node); } #endregion private readonly AccumulatorSet _accumulators; + private readonly AstExpression _element; - private AccumulatorMover(AccumulatorSet accumulator) + private AccumulatorMover(AccumulatorSet accumulator, AstExpression element) { _accumulators = accumulator; + _element = element; } public override AstNode VisitFilterField(AstFilterField node) { - // "_elements.0.X" => { __agg0 : { $first : "$$ROOT" } } + "__agg0.X" + // "_elements.0.X" => { __agg0 : { $first : element } } + "__agg0.X" if (node.Path.StartsWith("_elements.0.")) { - var accumulatorExpression = AstExpression.UnaryAccumulator(AstUnaryAccumulatorOperator.First, AstExpression.Var("ROOT")); + var accumulatorExpression = AstExpression.UnaryAccumulator(AstUnaryAccumulatorOperator.First, _element); var accumulatorFieldName = _accumulators.AddAccumulatorExpression(accumulatorExpression); var restOfPath = node.Path.Substring("_elements.0.".Length); var rewrittenPath = $"{accumulatorFieldName}.{restOfPath}"; @@ -288,9 +345,7 @@ public override AstNode VisitGetFieldExpression(AstGetFieldExpression node) { if (node.FieldName is AstConstantExpression constantFieldName && constantFieldName.Value.IsString && - constantFieldName.Value.AsString == "_elements" && - node.Input is AstVarExpression varExpression && - varExpression.Name == "ROOT") + constantFieldName.Value.AsString == "_elements") { throw new UnableToRemoveReferenceToElementsException(); } @@ -300,7 +355,7 @@ node.Input is AstVarExpression varExpression && public override AstNode VisitMapExpression(AstMapExpression node) { - // { $map : { input : { $getField : { input : "$$ROOT", field : "_elements" } }, as : "x", in : f(x) } } => { __agg0 : { $push : f(x => root) } } + "$__agg0" + // { $map : { input : { $getField : { input : "$$ROOT", field : "_elements" } }, as : "x", in : f(x) } } => { __agg0 : { $push : f(x => element) } } + "$__agg0" if (node.Input is AstGetFieldExpression mapInputGetFieldExpression && mapInputGetFieldExpression.FieldName is AstConstantExpression mapInputconstantFieldExpression && mapInputconstantFieldExpression.Value.IsString && @@ -308,10 +363,10 @@ mapInputGetFieldExpression.FieldName is AstConstantExpression mapInputconstantFi mapInputGetFieldExpression.Input is AstVarExpression mapInputGetFieldVarExpression && mapInputGetFieldVarExpression.Name == "ROOT") { - var root = AstExpression.Var("ROOT", isCurrent: true); - var rewrittenArg = (AstExpression)AstNodeReplacer.Replace(node.In, (node.As, root)); + var rewrittenArg = (AstExpression)AstNodeReplacer.Replace(node.In, (node.As, _element)); var accumulatorExpression = AstExpression.UnaryAccumulator(AstUnaryAccumulatorOperator.Push, rewrittenArg); var accumulatorFieldName = _accumulators.AddAccumulatorExpression(accumulatorExpression); + var root = AstExpression.Var("ROOT", isCurrent: true); return AstExpression.GetField(root, accumulatorFieldName); } @@ -321,7 +376,7 @@ mapInputGetFieldExpression.Input is AstVarExpression mapInputGetFieldVarExpressi public override AstNode VisitPickExpression(AstPickExpression node) { // { $pickOperator : { source : { $getField : { input : "$$ROOT", field : "_elements" } }, as : "x", sortBy : s, selector : f(x) } } - // => { __agg0 : { $pickAccumulatorOperator : { sortBy : s, selector : f(x => root) } } } + "$__agg0" + // => { __agg0 : { $pickAccumulatorOperator : { sortBy : s, selector : f(x => element) } } } + "$__agg0" if (node.Source is AstGetFieldExpression getFieldExpression && getFieldExpression.Input is AstVarExpression varExpression && varExpression.Name == "ROOT" && @@ -330,10 +385,10 @@ getFieldExpression.FieldName is AstConstantExpression constantFieldNameExpressio constantFieldNameExpression.Value.AsString == "_elements") { var @operator = node.Operator.ToAccumulatorOperator(); - var root = AstExpression.Var("ROOT", isCurrent: true); - var rewrittenSelector = (AstExpression)AstNodeReplacer.Replace(node.Selector, (node.As, root)); + var rewrittenSelector = (AstExpression)AstNodeReplacer.Replace(node.Selector, (node.As, _element)); var accumulatorExpression = new AstPickAccumulatorExpression(@operator, node.SortBy, rewrittenSelector, node.N); var accumulatorFieldName = _accumulators.AddAccumulatorExpression(accumulatorExpression); + var root = AstExpression.Var("ROOT", isCurrent: true); return AstExpression.GetField(root, accumulatorFieldName); } @@ -384,7 +439,7 @@ argGetFieldExpression.FieldName is AstConstantExpression constantFieldNameExpres bool TryOptimizeAccumulatorOfElements(out AstExpression optimizedExpression) { - // { $accumulator : { $getField : { input : "$$ROOT", field : "_elements" } } } => { __agg0 : { $accumulator : "$$ROOT" } } + "$__agg0" + // { $accumulator : { $getField : { input : "$$ROOT", field : "_elements" } } } => { __agg0 : { $accumulator : element } } + "$__agg0" if (node.Operator.IsAccumulator(out var accumulatorOperator) && node.Arg is AstGetFieldExpression getFieldExpression && getFieldExpression.FieldName is AstConstantExpression getFieldConstantFieldNameExpression && @@ -393,7 +448,7 @@ getFieldExpression.FieldName is AstConstantExpression getFieldConstantFieldNameE getFieldExpression.Input is AstVarExpression getFieldInputVarExpression && getFieldInputVarExpression.Name == "ROOT") { - var accumulatorExpression = AstExpression.UnaryAccumulator(accumulatorOperator, root); + var accumulatorExpression = AstExpression.UnaryAccumulator(accumulatorOperator, _element); var accumulatorFieldName = _accumulators.AddAccumulatorExpression(accumulatorExpression); optimizedExpression = AstExpression.GetField(root, accumulatorFieldName); return true; @@ -406,7 +461,7 @@ getFieldExpression.Input is AstVarExpression getFieldInputVarExpression && bool TryOptimizeAccumulatorOfMappedElements(out AstExpression optimizedExpression) { - // { $accumulator : { $map : { input : { $getField : { input : "$$ROOT", field : "_elements" } }, as : "x", in : f(x) } } } => { __agg0 : { $accumulator : f(x => root) } } + "$__agg0" + // { $accumulator : { $map : { input : { $getField : { input : "$$ROOT", field : "_elements" } }, as : "x", in : f(x) } } } => { __agg0 : { $accumulator : f(x => element) } } + "$__agg0" if (node.Operator.IsAccumulator(out var accumulatorOperator) && node.Arg is AstMapExpression mapExpression && mapExpression.Input is AstGetFieldExpression mapInputGetFieldExpression && @@ -416,7 +471,7 @@ mapInputGetFieldExpression.FieldName is AstConstantExpression mapInputconstantFi mapInputGetFieldExpression.Input is AstVarExpression mapInputGetFieldVarExpression && mapInputGetFieldVarExpression.Name == "ROOT") { - var rewrittenArg = (AstExpression)AstNodeReplacer.Replace(mapExpression.In, (mapExpression.As, root)); + var rewrittenArg = (AstExpression)AstNodeReplacer.Replace(mapExpression.In, (mapExpression.As, _element)); var accumulatorExpression = AstExpression.UnaryAccumulator(accumulatorOperator, rewrittenArg); var accumulatorFieldName = _accumulators.AddAccumulatorExpression(accumulatorExpression); optimizedExpression = AstExpression.GetField(root, accumulatorFieldName); diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Optimizers/AstPipelineOptimizer.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Optimizers/AstPipelineOptimizer.cs index ccf430221e1..9cc5f8baf67 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Optimizers/AstPipelineOptimizer.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Optimizers/AstPipelineOptimizer.cs @@ -19,7 +19,7 @@ internal static class AstPipelineOptimizer { public static AstPipeline Optimize(AstPipeline pipeline) { - pipeline = AstGroupPipelineOptimizer.Optimize(pipeline); + pipeline = AstGroupingPipelineOptimizer.Optimize(pipeline); pipeline = AstSimplifier.SimplifyAndConvert(pipeline); return pipeline; } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Stages/AstBucketAutoStage.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Stages/AstBucketAutoStage.cs index 03bfab8f103..5aba7bf4019 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Stages/AstBucketAutoStage.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Stages/AstBucketAutoStage.cs @@ -57,7 +57,7 @@ public override BsonValue Render() { return new BsonDocument { - { "$group", new BsonDocument + { "$bucketAuto", new BsonDocument { { "groupBy", _groupBy.Render() }, { "buckets", _buckets }, diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Stages/AstBucketStage.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Stages/AstBucketStage.cs index 30dc82d9b53..6bc4ef5135d 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Stages/AstBucketStage.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Stages/AstBucketStage.cs @@ -57,7 +57,7 @@ public override BsonValue Render() { return new BsonDocument { - { "$group", new BsonDocument + { "$bucket", new BsonDocument { { "groupBy", _groupBy.Render() }, { "boundaries", new BsonArray(_boundaries) }, diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Stages/AstDocumentsStage.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Stages/AstDocumentsStage.cs new file mode 100644 index 00000000000..390337f9071 --- /dev/null +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Stages/AstDocumentsStage.cs @@ -0,0 +1,55 @@ +/* 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 MongoDB.Bson; +using MongoDB.Driver.Core.Misc; +using MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions; +using MongoDB.Driver.Linq.Linq3Implementation.Ast.Visitors; + +namespace MongoDB.Driver.Linq.Linq3Implementation.Ast.Stages +{ + internal sealed class AstDocumentsStage : AstStage + { + private readonly AstExpression _documents; + + public AstDocumentsStage(AstExpression documents) + { + _documents = Ensure.IsNotNull(documents, nameof(documents)); + } + + public new AstExpression Documents => _documents; + public override AstNodeType NodeType => AstNodeType.DocumentsStage; + + public override AstNode Accept(AstNodeVisitor visitor) + { + return visitor.VisitDocumentsStage(this); + } + + public override BsonValue Render() + { + return new BsonDocument("$documents", _documents.Render()); + } + + public AstDocumentsStage Update(AstExpression documents) + { + if (documents == _documents) + { + return this; + } + + return new AstDocumentsStage(documents); + } + } +} diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Stages/AstStage.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Stages/AstStage.cs index 9eed0df438f..4273e22dec7 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Stages/AstStage.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Stages/AstStage.cs @@ -77,6 +77,12 @@ public static AstStage Densify( return new AstDensifyStage(fieldPath, range, partitionByFieldPaths); } + public static AstStage Documents( + AstExpression documents) + { + return new AstDocumentsStage(documents); + } + public static AstStage Facet(IEnumerable facets) { return new AstFacetStage(facets); diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Visitors/AstNodeVisitor.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Visitors/AstNodeVisitor.cs index d9d3e98298a..04486e84174 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Visitors/AstNodeVisitor.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Visitors/AstNodeVisitor.cs @@ -299,6 +299,11 @@ public virtual AstNode VisitDerivativeOrIntegralWindowExpression(AstDerivativeOr return node.Update(node.Operator, VisitAndConvert(node.Arg), node.Unit, node.Window); } + public virtual AstNode VisitDocumentsStage(AstDocumentsStage node) + { + return node.Update(VisitAndConvert(node.Documents)); + } + public virtual AstNode VisitElemMatchFilterOperation(AstElemMatchFilterOperation node) { return node.Update(VisitAndConvert(node.Filter)); diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/ExtensionMethods/ExpressionExtensions.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/ExtensionMethods/ExpressionExtensions.cs index c4d43590ab0..1634ce60c99 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/ExtensionMethods/ExpressionExtensions.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/ExtensionMethods/ExpressionExtensions.cs @@ -13,7 +13,6 @@ * limitations under the License. */ -using System.Linq; using System.Linq.Expressions; using MongoDB.Bson.Serialization; @@ -24,14 +23,15 @@ internal static class ExpressionExtensions public static (string CollectionName, IBsonSerializer DocumentSerializer) GetCollectionInfo(this Expression innerExpression, Expression containerExpression) { if (innerExpression is ConstantExpression constantExpression && - constantExpression.Value is IQueryable queryable && - queryable.Provider is MongoQueryProvider queryProvider) + constantExpression.Value is IMongoQueryable mongoQueryable && + mongoQueryable.Provider is IMongoQueryProvider mongoQueryProvider && + mongoQueryProvider.CollectionNamespace != null) { - return (queryProvider.CollectionNamespace.CollectionName, queryProvider.CollectionDocumentSerializer); + return (mongoQueryProvider.CollectionNamespace.CollectionName, mongoQueryProvider.PipelineInputSerializer); } - var message = $"Expression inner must be a MongoDB queryable representing a collection: {innerExpression} in {containerExpression}."; - throw new ExpressionNotSupportedException(message); + var message = $"inner expression is not an IMongoQueryable representing a collection"; + throw new ExpressionNotSupportedException(innerExpression, containerExpression, because: message); } public static TValue GetConstantValue(this Expression expression, Expression containingExpression) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/GroupExpressionStageDefinitions.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/GroupExpressionStageDefinitions.cs deleted file mode 100644 index 2d4790fa3e2..00000000000 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/GroupExpressionStageDefinitions.cs +++ /dev/null @@ -1,190 +0,0 @@ -/* 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; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Threading; -using System.Threading.Tasks; -using MongoDB.Bson.Serialization; -using MongoDB.Driver.Core.Misc; -using MongoDB.Driver.Linq.Linq3Implementation.Ast.Optimizers; -using MongoDB.Driver.Linq.Linq3Implementation.Misc; -using MongoDB.Driver.Linq.Linq3Implementation.Reflection; -using MongoDB.Driver.Linq.Linq3Implementation.Translators; -using MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToPipelineTranslators; - -namespace MongoDB.Driver.Linq.Linq3Implementation -{ - internal sealed class GroupExpressionStageDefinitions - { - private readonly Expression> _idExpression; - private readonly Expression, TOutput>> _groupExpression; - private readonly GroupStageDefinition _groupStage; - private readonly ProjectStageDefinition _projectStage; - - public GroupExpressionStageDefinitions( - Expression> idExpression, - Expression, TOutput>> groupExpression) - { - _idExpression = Ensure.IsNotNull(idExpression, nameof(idExpression)); - _groupExpression = Ensure.IsNotNull(groupExpression, nameof(groupExpression)); - - _groupStage = new GroupStageDefinition(idExpression, groupExpression); - _projectStage = new ProjectStageDefinition(_groupStage); - } - - public Expression> IdExpression => _idExpression; - public Expression, TOutput>> GroupExpression => _groupExpression; - public PipelineStageDefinition> GroupStage => _groupStage; - public PipelineStageDefinition, TOutput> ProjectStage => _projectStage; - - private class GroupStageDefinition : PipelineStageDefinition> - { - private readonly Expression> _idExpression; - private readonly Expression, TOutput>> _groupExpression; - private RenderedPipelineStageDefinition _renderedProjectStage = null; - - public GroupStageDefinition( - Expression> idExpression, - Expression, TOutput>> groupExpression) - { - _idExpression = idExpression; - _groupExpression = groupExpression; - } - - public override string OperatorName => "$group"; - public RenderedPipelineStageDefinition RenderedProjectStage => _renderedProjectStage; - - public override RenderedPipelineStageDefinition> Render( - IBsonSerializer inputSerializer, - IBsonSerializerRegistry serializerRegistry, - LinqProvider linqProvider) - { - if (linqProvider != LinqProvider.V3) - { - throw new InvalidOperationException("GroupExpressionStageDefinitions can only be used with LINQ3."); - } - - var expression = CreateExpression(inputSerializer); - expression = PartialEvaluator.EvaluatePartially(expression); - var context = TranslationContext.Create(expression, inputSerializer); - var unoptimizedPipeline = ExpressionToPipelineTranslator.Translate(context, expression); - var pipeline = AstPipelineOptimizer.Optimize(unoptimizedPipeline); - - var groupStageDocument = pipeline.Stages[0].Render().AsBsonDocument; - var renderedGroupStage = new RenderedPipelineStageDefinition>("$group", groupStageDocument, new DummyIGroupingSerializer()); - - var projectStageDocument = pipeline.Stages[1].Render().AsBsonDocument; - _renderedProjectStage = new RenderedPipelineStageDefinition("$project", projectStageDocument, (IBsonSerializer)pipeline.OutputSerializer); - - return renderedGroupStage; - } - - private Expression CreateExpression(IBsonSerializer inputSerializer) - { - var provider = new PseudoQueryProvider(inputSerializer); - var pseudoSource = new PseudoSource(provider); - - var groupByExpression = Expression.Call( - QueryableMethod.GroupByWithKeySelector.MakeGenericMethod(typeof(TInput), typeof(TKey)), - Expression.Constant(pseudoSource), - _idExpression); - - var selectExpression = Expression.Call( - QueryableMethod.Select.MakeGenericMethod(typeof(IGrouping), typeof(TOutput)), - groupByExpression, - _groupExpression); - - return selectExpression; - } - - private class PseudoQueryProvider : IMongoQueryProvider - { - private readonly IBsonSerializer _inputSerializer; - - public PseudoQueryProvider(IBsonSerializer inputSerializer) - { - _inputSerializer = inputSerializer; - } - - public CollectionNamespace CollectionNamespace => throw new NotImplementedException(); - - public IBsonSerializer CollectionDocumentSerializer => _inputSerializer; - - public IQueryable CreateQuery(Expression expression) => throw new NotImplementedException(); - public IQueryable CreateQuery(Expression expression) => throw new NotImplementedException(); - public object Execute(Expression expression) => throw new NotImplementedException(); - public TResult Execute(Expression expression) => throw new NotImplementedException(); - public Task ExecuteAsync(Expression expression, CancellationToken cancellationToken = default) => throw new NotImplementedException(); - public QueryableExecutionModel GetExecutionModel(Expression expression) => throw new NotImplementedException(); - } - - private class PseudoSource : IQueryable - { - private readonly Expression _expression; - private readonly IQueryProvider _provider; - - public PseudoSource(IQueryProvider provider) - { - _provider = provider; - _expression = Expression.Constant(this); - } - - public Type ElementType => typeof(TInput); - - public Expression Expression => _expression; - public IQueryProvider Provider => _provider; - - public IEnumerator GetEnumerator() => throw new NotImplementedException(); - IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); - } - - private class DummyIGroupingSerializer : IBsonSerializer> - { - public Type ValueType => typeof(IGrouping); - - public IGrouping Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) => throw new NotImplementedException(); - public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, IGrouping value) => throw new NotImplementedException(); - public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value) => throw new NotImplementedException(); - object IBsonSerializer.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) => throw new NotImplementedException(); - } - } - - private class ProjectStageDefinition : PipelineStageDefinition, TOutput> - { - private readonly GroupStageDefinition _groupStageDefinition; - - public ProjectStageDefinition(GroupStageDefinition groupStageDefinition) - { - _groupStageDefinition = groupStageDefinition; - } - - public override string OperatorName => "$project"; - - public override RenderedPipelineStageDefinition Render(IBsonSerializer> inputSerializer, IBsonSerializerRegistry serializerRegistry, LinqProvider linqProvider) - { - var renderedProjectStage = _groupStageDefinition.RenderedProjectStage; - if (renderedProjectStage == null) - { - throw new InvalidOperationException("GroupStageDefinition.Render must be called before ProjectStageDefinition.Render."); - } - return renderedProjectStage; - } - } - } -} diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/GroupingWithOutputExpressionStageDefinitions.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/GroupingWithOutputExpressionStageDefinitions.cs new file mode 100644 index 00000000000..daf979aa615 --- /dev/null +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/GroupingWithOutputExpressionStageDefinitions.cs @@ -0,0 +1,219 @@ +/* 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 MongoDB.Bson.Serialization; +using MongoDB.Driver.Linq; +using MongoDB.Driver.Linq.Linq3Implementation.Ast; +using MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions; +using MongoDB.Driver.Linq.Linq3Implementation.Ast.Optimizers; +using MongoDB.Driver.Linq.Linq3Implementation.Ast.Stages; +using MongoDB.Driver.Linq.Linq3Implementation.Misc; +using MongoDB.Driver.Linq.Linq3Implementation.Serializers; +using MongoDB.Driver.Linq.Linq3Implementation.Translators; +using MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators; + +namespace MongoDB.Driver.Linq.Linq3Implementation +{ + internal abstract class GroupingWithOutputExpressionStageDefinition : PipelineStageDefinition + { + protected readonly Expression> _output; + + public GroupingWithOutputExpressionStageDefinition(Expression> output) + { + _output = output; + } + + public override RenderedPipelineStageDefinition Render(IBsonSerializer inputSerializer, IBsonSerializerRegistry serializerRegistry, LinqProvider linqProvider) + { + if (linqProvider != LinqProvider.V3) + { + throw new InvalidOperationException($"{GetType().Name} is only intended for use with LINQ3."); + } + + var groupingStage = RenderGroupingStage(inputSerializer, serializerRegistry, out var groupingSerializer); + var projectStage = RenderProjectStage(groupingSerializer, serializerRegistry, out var outputSerializer); + var optimizedStages = OptimizeGroupingStages(groupingStage, projectStage, inputSerializer, outputSerializer); + var renderedStages = optimizedStages.Select(x => x.Render().AsBsonDocument); + + return new RenderedPipelineStageDefinition(OperatorName, renderedStages, outputSerializer); + } + + protected abstract AstStage RenderGroupingStage(IBsonSerializer inputSerializer, IBsonSerializerRegistry serializerRegistry, out IBsonSerializer groupingOutputSerializer); + + private AstStage RenderProjectStage(IBsonSerializer inputSerializer, IBsonSerializerRegistry serializerRegistry, out IBsonSerializer outputSerializer) + { + var partiallyEvaluatedOutput = (Expression>)PartialEvaluator.EvaluatePartially(_output); + var context = TranslationContext.Create(partiallyEvaluatedOutput, inputSerializer); + var outputTranslation = ExpressionToAggregationExpressionTranslator.TranslateLambdaBody(context, partiallyEvaluatedOutput, inputSerializer, asRoot: true); + var (projectStage, projectSerializer) = ProjectionHelper.CreateProjectStage(outputTranslation); + outputSerializer = (IBsonSerializer)projectSerializer; + return projectStage; + } + + private IReadOnlyList OptimizeGroupingStages(AstStage groupingStage, AstStage projectStage, IBsonSerializer inputSerializer, IBsonSerializer outputSerializer) + { + var pipeline = AstPipeline.Empty(inputSerializer).AddStages(outputSerializer, groupingStage, projectStage); + var optimizedPipeline = AstPipelineOptimizer.Optimize(pipeline); + return optimizedPipeline.Stages; + } + } + + internal sealed class BucketWithOutputExpressionStageDefinition : GroupingWithOutputExpressionStageDefinition, TOutput> + { + private readonly IReadOnlyList _boundaries; + private readonly Expression> _groupBy; + private readonly AggregateBucketOptions _options; + private readonly ExpressionTranslationOptions _translationOptions; + + public BucketWithOutputExpressionStageDefinition( + Expression> groupBy, + IEnumerable boundaries, + Expression, TOutput>> output, + AggregateBucketOptions options, + ExpressionTranslationOptions translationOptions) + : base(output) + { + _groupBy = groupBy; + _boundaries = boundaries.ToArray(); + _options = options; + _translationOptions = translationOptions; + } + + public override string OperatorName => "$bucket"; + + public override RenderedPipelineStageDefinition Render(IBsonSerializer inputSerializer, IBsonSerializerRegistry serializerRegistry, LinqProvider linqProvider) + { + if (linqProvider == LinqProvider.V2) + { + var linq2Stage = PipelineStageDefinitionBuilder.Bucket( + new ExpressionAggregateExpressionDefinition(_groupBy, _translationOptions), + _boundaries, + new ExpressionBucketOutputProjection(x => default(TValue), _output, _translationOptions), + _options); + return linq2Stage.Render(inputSerializer, serializerRegistry, linqProvider); + } + else + { + return base.Render(inputSerializer, serializerRegistry, linqProvider); + } + } + + protected override AstStage RenderGroupingStage(IBsonSerializer inputSerializer, IBsonSerializerRegistry serializerRegistry, out IBsonSerializer> groupingOutputSerializer) + { + var partiallyEvaluatedGroupBy = (Expression>)PartialEvaluator.EvaluatePartially(_groupBy); + var context = TranslationContext.Create(partiallyEvaluatedGroupBy, inputSerializer); + var groupByTranslation = ExpressionToAggregationExpressionTranslator.TranslateLambdaBody(context, partiallyEvaluatedGroupBy, inputSerializer, asRoot: true); + + var valueSerializer = (IBsonSerializer)groupByTranslation.Serializer; + var serializedBoundaries = SerializationHelper.SerializeValues(valueSerializer, _boundaries); + var serializedDefault = _options != null && _options.DefaultBucket.HasValue ? SerializationHelper.SerializeValue(valueSerializer, _options.DefaultBucket.Value) : null; + var pushElements = AstExpression.AccumulatorField("_elements", AstUnaryAccumulatorOperator.Push, AstExpression.Var("ROOT", isCurrent: true)); + groupingOutputSerializer = IGroupingSerializer.Create(valueSerializer, inputSerializer); + + return AstStage.Bucket( + groupByTranslation.Ast, + serializedBoundaries, + serializedDefault, + new[] { pushElements }); + } + } + + internal sealed class BucketAutoWithOutputExpressionStageDefinition : GroupingWithOutputExpressionStageDefinition, TInput>, TOutput> + { + private readonly int _buckets; + private readonly Expression> _groupBy; + private readonly AggregateBucketAutoOptions _options; + + public BucketAutoWithOutputExpressionStageDefinition( + Expression> groupBy, + int buckets, + Expression, TInput>, TOutput>> output, + AggregateBucketAutoOptions options) + : base(output) + { + _groupBy = groupBy; + _buckets = buckets; + _options = options; + } + + public override string OperatorName => "$bucketAuto"; + + protected override AstStage RenderGroupingStage(IBsonSerializer inputSerializer, IBsonSerializerRegistry serializerRegistry, out IBsonSerializer, TInput>> groupingOutputSerializer) + { + var partiallyEvaluatedGroupBy = (Expression>)PartialEvaluator.EvaluatePartially(_groupBy); + var context = TranslationContext.Create(partiallyEvaluatedGroupBy, inputSerializer); + var groupByTranslation = ExpressionToAggregationExpressionTranslator.TranslateLambdaBody(context, partiallyEvaluatedGroupBy, inputSerializer, asRoot: true); + + var valueSerializer = (IBsonSerializer)groupByTranslation.Serializer; + var keySerializer = AggregateBucketAutoResultIdSerializer.Create(valueSerializer); + var serializedGranularity = _options != null && _options.Granularity.HasValue ? _options.Granularity.Value.Value : null; + var pushElements = AstExpression.AccumulatorField("_elements", AstUnaryAccumulatorOperator.Push, AstExpression.Var("ROOT", isCurrent: true)); + groupingOutputSerializer = IGroupingSerializer.Create(keySerializer, inputSerializer); + + return AstStage.BucketAuto( + groupByTranslation.Ast, + _buckets, + serializedGranularity, + new[] { pushElements }); + } + } + + internal sealed class GroupWithOutputExpressionStageDefinition : GroupingWithOutputExpressionStageDefinition, TOutput> + { + private readonly Expression> _groupBy; + private readonly ExpressionTranslationOptions _translationOptions; + + public GroupWithOutputExpressionStageDefinition( + Expression> groupBy, + Expression, TOutput>> output, + ExpressionTranslationOptions translationOptions = null) + : base(output) + { + _groupBy = groupBy; + _translationOptions = translationOptions; + } + + public override string OperatorName => "$group"; + + public override RenderedPipelineStageDefinition Render(IBsonSerializer inputSerializer, IBsonSerializerRegistry serializerRegistry, LinqProvider linqProvider) + { + if (linqProvider == LinqProvider.V2) + { + var linq2Stage = PipelineStageDefinitionBuilder.Group(new GroupExpressionProjection(_groupBy, _output, _translationOptions)); + return linq2Stage.Render(inputSerializer, serializerRegistry, linqProvider); + } + else + { + return base.Render(inputSerializer, serializerRegistry, linqProvider); + } + } + + protected override AstStage RenderGroupingStage(IBsonSerializer inputSerializer, IBsonSerializerRegistry serializerRegistry, out IBsonSerializer> groupingOutputSerializer) + { + var partiallyEvaluatedGroupBy = (Expression>)PartialEvaluator.EvaluatePartially(_groupBy); + var context = TranslationContext.Create(partiallyEvaluatedGroupBy, inputSerializer); + var groupByTranslation = ExpressionToAggregationExpressionTranslator.TranslateLambdaBody(context, partiallyEvaluatedGroupBy, inputSerializer, asRoot: true); + var pushElements = AstExpression.AccumulatorField("_elements", AstUnaryAccumulatorOperator.Push, AstExpression.Var("ROOT", isCurrent: true)); + var groupBySerializer = (IBsonSerializer)groupByTranslation.Serializer; + groupingOutputSerializer = IGroupingSerializer.Create(groupBySerializer, inputSerializer); + + return AstStage.Group(groupByTranslation.Ast, pushElements); + } + } +} diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/LinqProviderAdapterV3.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/LinqProviderAdapterV3.cs index de6b6d6de47..1c98fbf091a 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/LinqProviderAdapterV3.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/LinqProviderAdapterV3.cs @@ -38,6 +38,15 @@ internal override IMongoQueryable AsQueryable( return new MongoQuery(provider); } + internal override IMongoQueryable AsQueryable( + IMongoDatabase database, + IClientSessionHandle session, + AggregateOptions options) + { + var provider = new MongoQueryProvider(database, session, options); + return new MongoQuery(provider); + } + public override string ToString() => "V3"; internal override BsonValue TranslateExpressionToAggregateExpression( @@ -62,8 +71,7 @@ internal override RenderedProjectionDefinition TranslateExpressionToBuc IBsonSerializerRegistry serializerRegistry, ExpressionTranslationOptions translationOptions) { - // TODO: implement using LINQ3 instead of falling back to LINQ2 - return LinqProviderAdapter.V2.TranslateExpressionToBucketOutputProjection(valueExpression, outputExpression, documentSerializer, serializerRegistry, translationOptions); + throw new InvalidOperationException("TranslateExpressionToBucketOutputProjection can only be used with LINQ2."); } internal override RenderedFieldDefinition TranslateExpressionToField( @@ -120,7 +128,7 @@ internal override BsonDocument TranslateExpressionToFilter( { expression = (Expression>)PartialEvaluator.EvaluatePartially(expression); var context = TranslationContext.Create(expression, documentSerializer); - var filter = ExpressionToFilterTranslator.TranslateLambda(context, expression, documentSerializer); + var filter = ExpressionToFilterTranslator.TranslateLambda(context, expression, documentSerializer, asRoot: true); filter = AstSimplifier.SimplifyAndConvert(filter); return filter.Render().AsBsonDocument; @@ -131,8 +139,7 @@ internal override RenderedProjectionDefinition TranslateExpressionT IBsonSerializer sourceSerializer, IBsonSerializerRegistry serializerRegistry) { - // TODO: implement using LINQ3 instead of falling back to LINQ2 - return LinqProviderAdapter.V2.TranslateExpressionToFindProjection(expression, sourceSerializer, serializerRegistry); + return TranslateExpressionToProjection(expression, sourceSerializer, serializerRegistry, translationOptions: null); } internal override RenderedProjectionDefinition TranslateExpressionToGroupProjection( diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/ArraySerializerHelper.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/ArraySerializerHelper.cs index 744e0b62ae0..80eb292c57d 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/ArraySerializerHelper.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/ArraySerializerHelper.cs @@ -14,6 +14,7 @@ */ using System; +using System.Linq.Expressions; using MongoDB.Bson.Serialization; namespace MongoDB.Driver.Linq.Linq3Implementation.Misc @@ -38,5 +39,24 @@ public static IBsonSerializer GetItemSerializer(IBsonSerializer serializer) throw new InvalidOperationException($"{serializer.GetType().FullName} must implement IBsonArraySerializer to be used with LINQ."); } } + + public static IBsonSerializer GetItemSerializer(Expression expression, IBsonSerializer serializer) + { + if (serializer is IBsonArraySerializer arraySerializer) + { + if (arraySerializer.TryGetItemSerializationInfo(out var itemSerializationInfo)) + { + return itemSerializationInfo.Serializer; + } + else + { + throw new ExpressionNotSupportedException(expression, because: $"{serializer.GetType().FullName}.TryGetItemSerializationInfo returned false"); + } + } + else + { + throw new ExpressionNotSupportedException(expression, because: $"{serializer.GetType().FullName} must implement IBsonArraySerializer to be used with LINQ"); + } + } } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/ConvertHelper.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/ConvertHelper.cs index 2721927d0aa..6cfec336a72 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/ConvertHelper.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/ConvertHelper.cs @@ -82,7 +82,7 @@ public static Expression RemoveConvertToMongoQueryable(Expression expression) { var convertExpression = (UnaryExpression)expression; var convertToType = convertExpression.Type; - if (convertToType.IsGenericType() && + if (convertToType.IsGenericType && convertToType.GetGenericTypeDefinition() == typeof(IMongoQueryable<>)) { return convertExpression.Operand; @@ -99,7 +99,9 @@ public static Expression RemoveConvertToEnumUnderlyingType(Expression expression var convertExpression = (UnaryExpression)expression; var sourceType = convertExpression.Operand.Type; var targetType = convertExpression.Type; - if (sourceType.IsEnum() && targetType == Enum.GetUnderlyingType(sourceType)) + + if (sourceType.IsEnumOrNullableEnum(out _, out var underlyingType) && + targetType.IsSameAsOrNullableOf(underlyingType)) { return convertExpression.Operand; } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/DocumentSerializerHelper.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/DocumentSerializerHelper.cs index 90e28a3f2c0..37692e69e8c 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/DocumentSerializerHelper.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/DocumentSerializerHelper.cs @@ -20,7 +20,7 @@ namespace MongoDB.Driver.Linq.Linq3Implementation.Misc { internal static class DocumentSerializerHelper { - public static FieldInfo GetFieldInfo(IBsonSerializer serializer, string memberName) + public static MemberSerializationInfo GetMemberSerializationInfo(IBsonSerializer serializer, string memberName) { if (!(serializer is IBsonDocumentSerializer documentSerializer)) { @@ -32,10 +32,10 @@ public static FieldInfo GetFieldInfo(IBsonSerializer serializer, string memberNa throw new InvalidOperationException($"Serializer for {serializer.ValueType} does not have a member named {memberName}."); } - return new FieldInfo(serializationInfo.ElementName, serializationInfo.Serializer); + return new MemberSerializationInfo(serializationInfo.ElementName, serializationInfo.Serializer); } - public static bool HasFieldInfo(IBsonSerializer serializer, string memberName) + public static bool HasMemberSerializationInfo(IBsonSerializer serializer, string memberName) { return serializer is IBsonDocumentSerializer documentSerializer && diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/FieldInfo.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/MemberSerializationInfo.cs similarity index 90% rename from src/MongoDB.Driver/Linq/Linq3Implementation/Misc/FieldInfo.cs rename to src/MongoDB.Driver/Linq/Linq3Implementation/Misc/MemberSerializationInfo.cs index 9c56b38ecf2..637a7b4e0ab 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/FieldInfo.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/MemberSerializationInfo.cs @@ -18,14 +18,14 @@ namespace MongoDB.Driver.Linq.Linq3Implementation.Misc { - internal class FieldInfo + internal class MemberSerializationInfo { // private fields private readonly string _elementName; private readonly IBsonSerializer _serializer; // constructors - public FieldInfo(string elementName, IBsonSerializer serializer) + public MemberSerializationInfo(string elementName, IBsonSerializer serializer) { _elementName = Ensure.IsNotNullOrEmpty(elementName, nameof(elementName)); _serializer = Ensure.IsNotNull(serializer, nameof(serializer)); diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/TypeExtensions.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/TypeExtensions.cs index c641a1a5445..9d0acc0448a 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/TypeExtensions.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/TypeExtensions.cs @@ -32,10 +32,14 @@ public static Type GetIEnumerableGenericInterface(this Type enumerableType) public static bool Implements(this Type type, Type @interface) { - Type interfaceDefinition = null; - if (@interface.IsGenericType()) + if (type == @interface) { - interfaceDefinition = @interface.GetGenericTypeDefinition(); + return true; + } + + if (type.IsGenericType && type.GetGenericTypeDefinition() == @interface) + { + return true; } foreach (var implementedInterface in type.GetInterfaces()) @@ -45,7 +49,7 @@ public static bool Implements(this Type type, Type @interface) return true; } - if (implementedInterface.IsGenericType() && implementedInterface.GetGenericTypeDefinition() == interfaceDefinition) + if (implementedInterface.IsGenericType && implementedInterface.GetGenericTypeDefinition() == @interface) { return true; } @@ -61,7 +65,7 @@ public static bool Is(this Type type, Type comparand) return true; } - if (type.IsGenericType() && comparand.IsGenericTypeDefinition()) + if (type.IsGenericType && comparand.IsGenericTypeDefinition) { if (type.GetGenericTypeDefinition() == comparand) { @@ -72,26 +76,89 @@ public static bool Is(this Type type, Type comparand) return false; } - public static bool IsEnum(this Type type) + public static bool IsEnum(this Type type, out Type underlyingType) + { + if (type.IsEnum) + { + underlyingType = Enum.GetUnderlyingType(type); + return true; + } + else + { + underlyingType = null; + return false; + } + } + + public static bool IsEnum(this Type type, out Type enumType, out Type underlyingType) + { + if (type.IsEnum) + { + enumType = type; + underlyingType = Enum.GetUnderlyingType(type); + return true; + } + else + { + enumType = null; + underlyingType = null; + return false; + } + } + + public static bool IsEnumOrNullableEnum(this Type type, out Type enumType, out Type underlyingType) { - return type.IsEnum; + return + type.IsEnum(out enumType, out underlyingType) || + type.IsNullableEnum(out enumType, out underlyingType); } - public static bool IsGenericType(this Type type) + public static bool IsNullable(this Type type) { - return type.IsGenericType; + return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); } - public static bool IsGenericTypeDefinition(this Type type) + public static bool IsNullable(this Type type, out Type valueType) { - return type.IsGenericType; + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) + { + valueType = type.GetGenericArguments()[0]; + return true; + } + else + { + valueType = null; + return false; + } + } + + public static bool IsNullableEnum(this Type type) + { + return type.IsNullable(out var valueType) && valueType.IsEnum; + } + + public static bool IsNullableEnum(this Type type, out Type enumType, out Type underlyingType) + { + enumType = null; + underlyingType = null; + return type.IsNullable(out var valueType) && valueType.IsEnum(out enumType, out underlyingType); + } + + public static bool IsNullableOf(this Type type, Type valueType) + { + return type.IsNullable(out var nullableValueType) && nullableValueType == valueType; + } + + public static bool IsSameAsOrNullableOf(this Type type, Type valueType) + { + return type == valueType || type.IsNullableOf(valueType); } public static bool TryGetIDictionaryGenericInterface(this Type type, out Type idictionaryGenericInterface) { foreach (var interfaceType in type.GetInterfaces()) { - if (interfaceType.IsGenericType() && interfaceType.GetGenericTypeDefinition() == typeof(IDictionary<,>)) + if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IDictionary<,>)) { idictionaryGenericInterface = interfaceType; return true; @@ -104,9 +171,15 @@ public static bool TryGetIDictionaryGenericInterface(this Type type, out Type id public static bool TryGetIEnumerableGenericInterface(this Type type, out Type ienumerableGenericInterface) { + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>)) + { + ienumerableGenericInterface = type; + return true; + } + foreach (var interfaceType in type.GetInterfaces()) { - if (interfaceType.IsGenericType() && interfaceType.GetGenericTypeDefinition() == typeof(IEnumerable<>)) + if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IEnumerable<>)) { ienumerableGenericInterface = interfaceType; return true; diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/MongoQueryProvider.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/MongoQueryProvider.cs index ba5985d24d5..b90003aa6bf 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/MongoQueryProvider.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/MongoQueryProvider.cs @@ -18,9 +18,11 @@ using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; +using MongoDB.Bson; using MongoDB.Bson.Serialization; -using MongoDB.Driver.Linq; +using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToExecutableQueryTranslators; +using MongoDB.Driver.Support; namespace MongoDB.Driver.Linq.Linq3Implementation { @@ -40,9 +42,9 @@ protected MongoQueryProvider( } // public properties - public abstract IBsonSerializer CollectionDocumentSerializer { get; } public abstract CollectionNamespace CollectionNamespace { get; } public AggregateOptions Options => _options; + public abstract IBsonSerializer PipelineInputSerializer { get; } public IClientSessionHandle Session => _session; // public methods @@ -52,12 +54,15 @@ protected MongoQueryProvider( public abstract object Execute(Expression expression); public abstract TResult Execute(Expression expression); public abstract Task ExecuteAsync(Expression expression, CancellationToken cancellationToken); + public abstract BsonDocument[] GetMostRecentPipelineStages(); } internal sealed class MongoQueryProvider : MongoQueryProvider { // private fields private readonly IMongoCollection _collection; + private readonly IMongoDatabase _database; + private ExecutableQuery _mostRecentExecutableQuery; // constructors public MongoQueryProvider( @@ -66,18 +71,30 @@ public MongoQueryProvider( AggregateOptions options) : base(session, options) { - _collection = collection; + _collection = Ensure.IsNotNull(collection, nameof(collection)); + } + + public MongoQueryProvider( + IMongoDatabase database, + IClientSessionHandle session, + AggregateOptions options) + : base(session, options) + { + _database = Ensure.IsNotNull(database, nameof(database)); } // public properties public IMongoCollection Collection => _collection; - public override CollectionNamespace CollectionNamespace => _collection.CollectionNamespace; - public override IBsonSerializer CollectionDocumentSerializer => _collection.DocumentSerializer; + public override CollectionNamespace CollectionNamespace => _collection == null ? null : _collection.CollectionNamespace; + public IMongoDatabase Database => _database; + public override IBsonSerializer PipelineInputSerializer => _collection == null ? NoPipelineInputSerializer.Instance : _collection.DocumentSerializer; // public methods public override IQueryable CreateQuery(Expression expression) { - throw new NotImplementedException(); + var outputType = expression.Type.GetSequenceElementType(); + var queryType = typeof(MongoQuery<,>).MakeGenericType(typeof(TDocument), outputType); + return (IQueryable)Activator.CreateInstance(queryType, new object[] { this, expression }); } public override IQueryable CreateQuery(Expression expression) @@ -98,13 +115,22 @@ public override object Execute(Expression expression) public override TResult Execute(Expression expression) { var executableQuery = ExpressionToExecutableQueryTranslator.TranslateScalar(this, expression); + _mostRecentExecutableQuery = executableQuery; return executableQuery.Execute(_session, CancellationToken.None); } public override Task ExecuteAsync(Expression expression, CancellationToken cancellationToken) { var executableQuery = ExpressionToExecutableQueryTranslator.TranslateScalar(this, expression); + _mostRecentExecutableQuery = executableQuery; return executableQuery.ExecuteAsync(_session, cancellationToken); } + + public override BsonDocument[] GetMostRecentPipelineStages() + { + var pipeline = _mostRecentExecutableQuery.Pipeline; + var renderedPipeline = (BsonArray)pipeline.Render(); + return renderedPipeline.Cast().ToArray(); + } } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/DateTimeMethod.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/DateTimeMethod.cs index 3b6c194dadb..4e677ffe5f3 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/DateTimeMethod.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/DateTimeMethod.cs @@ -53,9 +53,13 @@ internal static class DateTimeMethod private static readonly MethodInfo __subtractWithTimeSpanAndTimezone; private static readonly MethodInfo __subtractWithUnit; private static readonly MethodInfo __subtractWithUnitAndTimezone; + private static readonly MethodInfo __toStringWithFormat; + private static readonly MethodInfo __toStringWithFormatAndTimezone; private static readonly MethodInfo __truncate; private static readonly MethodInfo __truncateWithBinSize; private static readonly MethodInfo __truncateWithBinSizeAndTimezone; + private static readonly MethodInfo __week; + private static readonly MethodInfo __weekWithTimezone; // static constructor static DateTimeMethod() @@ -92,9 +96,13 @@ static DateTimeMethod() __subtractWithTimeSpanAndTimezone = ReflectionInfo.Method((DateTime @this, TimeSpan value, string timezone) => @this.Subtract(value, timezone)); __subtractWithUnit = ReflectionInfo.Method((DateTime @this, long value, DateTimeUnit unit) => @this.Subtract(value, unit)); __subtractWithUnitAndTimezone = ReflectionInfo.Method((DateTime @this, long value, DateTimeUnit unit, string timezone) => @this.Subtract(value, unit, timezone)); + __toStringWithFormat = ReflectionInfo.Method((DateTime @this, string format) => @this.ToString(format)); + __toStringWithFormatAndTimezone = ReflectionInfo.Method((DateTime @this, string format, string timezone) => @this.ToString(format, timezone)); __truncate = ReflectionInfo.Method((DateTime @this, DateTimeUnit unit) => @this.Truncate(unit)); __truncateWithBinSize = ReflectionInfo.Method((DateTime @this, DateTimeUnit unit, long binSize) => @this.Truncate(unit, binSize)); __truncateWithBinSizeAndTimezone = ReflectionInfo.Method((DateTime @this, DateTimeUnit unit, long binSize, string timezone) => @this.Truncate(unit, binSize, timezone)); + __week = ReflectionInfo.Method((DateTime @this) => @this.Week()); + __weekWithTimezone = ReflectionInfo.Method((DateTime @this, string timezone) => @this.Week(timezone)); } // public properties @@ -130,8 +138,12 @@ static DateTimeMethod() public static MethodInfo SubtractWithTimeSpanAndTimezone => __subtractWithTimeSpanAndTimezone; public static MethodInfo SubtractWithUnit => __subtractWithUnit; public static MethodInfo SubtractWithUnitAndTimezone => __subtractWithUnitAndTimezone; + public static MethodInfo ToStringWithFormat => __toStringWithFormat; + public static MethodInfo ToStringWithFormatAndTimezone => __toStringWithFormatAndTimezone; public static MethodInfo Truncate => __truncate; public static MethodInfo TruncateWithBinSize => __truncateWithBinSize; public static MethodInfo TruncateWithBinSizeAndTimezone => __truncateWithBinSizeAndTimezone; + public static MethodInfo Week => __week; + public static MethodInfo WeekWithTimezone => __weekWithTimezone; } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MathMethod.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MathMethod.cs index ee24de134e7..29433c23c58 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MathMethod.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MathMethod.cs @@ -28,8 +28,17 @@ internal static class MathMethod private static readonly MethodInfo __absInt64; private static readonly MethodInfo __absSByte; private static readonly MethodInfo __absSingle; + private static readonly MethodInfo __acos; + private static readonly MethodInfo __acosh = null; // null when target framework does not have this method + private static readonly MethodInfo __asin; + private static readonly MethodInfo __asinh = null; // null when target framework does not have this method + private static readonly MethodInfo __atan; + private static readonly MethodInfo __atan2; + private static readonly MethodInfo __atanh = null; // null when target framework does not have this method private static readonly MethodInfo __ceilingWithDecimal; private static readonly MethodInfo __ceilingWithDouble; + private static readonly MethodInfo __cos; + private static readonly MethodInfo __cosh; private static readonly MethodInfo __exp; private static readonly MethodInfo __floorWithDecimal; private static readonly MethodInfo __floorWithDouble; @@ -37,7 +46,11 @@ internal static class MathMethod private static readonly MethodInfo __logWithNewBase; private static readonly MethodInfo __log10; private static readonly MethodInfo __pow; + private static readonly MethodInfo __sin; + private static readonly MethodInfo __sinh; private static readonly MethodInfo __sqrt; + private static readonly MethodInfo __tan; + private static readonly MethodInfo __tanh; private static readonly MethodInfo __truncateDecimal; private static readonly MethodInfo __truncateDouble; @@ -51,8 +64,23 @@ static MathMethod() __absInt64 = ReflectionInfo.Method((long value) => Math.Abs(value)); __absSByte = ReflectionInfo.Method((sbyte value) => Math.Abs(value)); __absSingle = ReflectionInfo.Method((float value) => Math.Abs(value)); + __acos = ReflectionInfo.Method((double d) => Math.Acos(d)); +#if NETSTANDARD2_1_OR_GREATER + __acosh = ReflectionInfo.Method((double d) => Math.Acosh(d)); +#endif + __asin = ReflectionInfo.Method((double d) => Math.Asin(d)); +#if NETSTANDARD2_1_OR_GREATER + __asinh = ReflectionInfo.Method((double d) => Math.Asinh(d)); +#endif + __atan = ReflectionInfo.Method((double d) => Math.Atan(d)); + __atan2 = ReflectionInfo.Method((double x, double y) => Math.Atan2(x, y)); +#if NETSTANDARD2_1_OR_GREATER + __atanh = ReflectionInfo.Method((double d) => Math.Atanh(d)); +#endif __ceilingWithDecimal = ReflectionInfo.Method((decimal d) => Math.Ceiling(d)); __ceilingWithDouble = ReflectionInfo.Method((double a) => Math.Ceiling(a)); + __cos = ReflectionInfo.Method((double d) => Math.Cos(d)); + __cosh = ReflectionInfo.Method((double a) => Math.Cosh(a)); __exp = ReflectionInfo.Method((double d) => Math.Exp(d)); __floorWithDecimal = ReflectionInfo.Method((decimal d) => Math.Floor(d)); __floorWithDouble = ReflectionInfo.Method((double d) => Math.Floor(d)); @@ -60,7 +88,11 @@ static MathMethod() __logWithNewBase = ReflectionInfo.Method((double a, double newBase) => Math.Log(a, newBase)); __log10 = ReflectionInfo.Method((double d) => Math.Log10(d)); __pow = ReflectionInfo.Method((double x, double y) => Math.Pow(x, y)); + __sin = ReflectionInfo.Method((double a) => Math.Sin(a)); + __sinh = ReflectionInfo.Method((double a) => Math.Sinh(a)); __sqrt = ReflectionInfo.Method((double d) => Math.Sqrt(d)); + __tan = ReflectionInfo.Method((double a) => Math.Tan(a)); + __tanh = ReflectionInfo.Method((double a) => Math.Tanh(a)); __truncateDecimal = ReflectionInfo.Method((decimal d) => Math.Truncate(d)); __truncateDouble = ReflectionInfo.Method((double d) => Math.Truncate(d)); } @@ -73,8 +105,17 @@ static MathMethod() public static MethodInfo AbsInt64 => __absInt64; public static MethodInfo AbsSByte => __absSByte; public static MethodInfo AbsSingle => __absSingle; + public static MethodInfo Acos => __acos; + public static MethodInfo Acosh => __acosh; + public static MethodInfo Asin => __asin; + public static MethodInfo Asinh => __asinh; + public static MethodInfo Atan => __atan; + public static MethodInfo Atan2 => __atan2; + public static MethodInfo Atanh => __atanh; public static MethodInfo CeilingWithDecimal => __ceilingWithDecimal; public static MethodInfo CeilingWithDouble => __ceilingWithDouble; + public static MethodInfo Cos => __cos; + public static MethodInfo Cosh => __cosh; public static MethodInfo Exp => __exp; public static MethodInfo FloorWithDecimal => __floorWithDecimal; public static MethodInfo FloorWithDouble => __floorWithDouble; @@ -82,7 +123,11 @@ static MathMethod() public static MethodInfo LogWithNewBase => __logWithNewBase; public static MethodInfo Log10 => __log10; public static MethodInfo Pow => __pow; + public static MethodInfo Sin => __sin; + public static MethodInfo Sinh => __sinh; public static MethodInfo Sqrt => __sqrt; + public static MethodInfo Tan => __tan; + public static MethodInfo Tanh => __tanh; public static MethodInfo TruncateDecimal => __truncateDecimal; public static MethodInfo TruncateDouble => __truncateDouble; } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MongoDBMathMethod.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MongoDBMathMethod.cs new file mode 100644 index 00000000000..e39c3989e2f --- /dev/null +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MongoDBMathMethod.cs @@ -0,0 +1,37 @@ +/* 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.Reflection; + +namespace MongoDB.Driver.Linq.Linq3Implementation.Reflection +{ + internal static class MongoDBMathMethod + { + // private static fields + private static readonly MethodInfo __degreesToRadians; + private static readonly MethodInfo __radiansToDegrees; + + // static constructor + static MongoDBMathMethod() + { + __degreesToRadians = ReflectionInfo.Method((double degrees) => MongoDBMath.DegreesToRadians(degrees)); + __radiansToDegrees = ReflectionInfo.Method((double radians) => MongoDBMath.RadiansToDegrees(radians)); + } + + // public properties + public static MethodInfo DegreesToRadians => __degreesToRadians; + public static MethodInfo RadiansToDegrees => __radiansToDegrees; + } +} diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MongoQueryableMethod.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MongoQueryableMethod.cs index d093f35ffd2..074b183b844 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MongoQueryableMethod.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MongoQueryableMethod.cs @@ -14,6 +14,7 @@ */ using System; +using System.Collections.Generic; using System.Linq.Expressions; using System.Reflection; using System.Threading; @@ -50,6 +51,8 @@ internal static class MongoQueryableMethod private static readonly MethodInfo __countAsync; private static readonly MethodInfo __countWithPredicateAsync; private static readonly MethodInfo __densifyWithArrayPartitionByFields; + private static readonly MethodInfo __documents; + private static readonly MethodInfo __documentsWithSerializer; private static readonly MethodInfo __firstAsync; private static readonly MethodInfo __firstOrDefaultAsync; private static readonly MethodInfo __firstOrDefaultWithPredicateAsync; @@ -65,6 +68,7 @@ internal static class MongoQueryableMethod private static readonly MethodInfo __singleOrDefaultAsync; private static readonly MethodInfo __singleOrDefaultWithPredicateAsync; private static readonly MethodInfo __singleWithPredicateAsync; + private static readonly MethodInfo __skipWithLong; private static readonly MethodInfo __standardDeviationPopulationDecimal; private static readonly MethodInfo __standardDeviationPopulationDecimalAsync; private static readonly MethodInfo __standardDeviationPopulationDecimalWithSelector; @@ -165,6 +169,7 @@ internal static class MongoQueryableMethod private static readonly MethodInfo __sumNullableSingleWithSelectorAsync; private static readonly MethodInfo __sumSingleAsync; private static readonly MethodInfo __sumSingleWithSelectorAsync; + private static readonly MethodInfo __takeWithLong; // static constructor static MongoQueryableMethod() @@ -195,6 +200,8 @@ static MongoQueryableMethod() __countAsync = ReflectionInfo.Method((IMongoQueryable source, CancellationToken cancellationToken) => source.CountAsync(cancellationToken)); __countWithPredicateAsync = ReflectionInfo.Method((IMongoQueryable source, Expression> predicate, CancellationToken cancellationToken) => source.CountAsync(predicate, cancellationToken)); __densifyWithArrayPartitionByFields = ReflectionInfo.Method((IMongoQueryable source, Expression> field, DensifyRange range, Expression>[] partitionByFields) => source.Densify(field, range, partitionByFields)); + __documents = ReflectionInfo.Method((IMongoQueryable source, object[] documents) => source.Documents(documents)); + __documentsWithSerializer = ReflectionInfo.Method((IMongoQueryable source, IEnumerable documents, IBsonSerializer serializer) => source.Documents(documents, serializer)); __firstAsync = ReflectionInfo.Method((IMongoQueryable source, CancellationToken cancellationToken) => source.FirstAsync(cancellationToken)); __firstOrDefaultAsync = ReflectionInfo.Method((IMongoQueryable source, CancellationToken cancellationToken) => source.FirstOrDefaultAsync(cancellationToken)); __firstOrDefaultWithPredicateAsync = ReflectionInfo.Method((IMongoQueryable source, Expression> predicate, CancellationToken cancellationToken) => source.FirstOrDefaultAsync(predicate, cancellationToken)); @@ -210,6 +217,7 @@ static MongoQueryableMethod() __singleOrDefaultAsync = ReflectionInfo.Method((IMongoQueryable source, CancellationToken cancellationToken) => source.SingleOrDefaultAsync(cancellationToken)); __singleOrDefaultWithPredicateAsync = ReflectionInfo.Method((IMongoQueryable source, Expression> predicate, CancellationToken cancellationToken) => source.SingleOrDefaultAsync(predicate, cancellationToken)); __singleWithPredicateAsync = ReflectionInfo.Method((IMongoQueryable source, Expression> predicate, CancellationToken cancellationToken) => source.SingleAsync(predicate, cancellationToken)); + __skipWithLong = ReflectionInfo.Method((IMongoQueryable source, long count) => source.Skip(count)); __standardDeviationPopulationDecimal = ReflectionInfo.Method((IMongoQueryable source) => source.StandardDeviationPopulation()); __standardDeviationPopulationDecimalAsync = ReflectionInfo.Method((IMongoQueryable source, CancellationToken cancellationToken) => source.StandardDeviationPopulationAsync(cancellationToken)); __standardDeviationPopulationDecimalWithSelector = ReflectionInfo.Method((IMongoQueryable source, Expression> selector) => source.StandardDeviationPopulation(selector)); @@ -310,6 +318,7 @@ static MongoQueryableMethod() __sumNullableSingleWithSelectorAsync = ReflectionInfo.Method((IMongoQueryable source, Expression> selector, CancellationToken cancellationToken) => source.SumAsync(selector, cancellationToken)); __sumSingleAsync = ReflectionInfo.Method((IMongoQueryable source, CancellationToken cancellationToken) => source.SumAsync(cancellationToken)); __sumSingleWithSelectorAsync = ReflectionInfo.Method((IMongoQueryable source, Expression> selector, CancellationToken cancellationToken) => source.SumAsync(selector, cancellationToken)); + __takeWithLong = ReflectionInfo.Method((IMongoQueryable source, long count) => source.Take(count)); } // public properties @@ -339,6 +348,8 @@ static MongoQueryableMethod() public static MethodInfo CountAsync => __countAsync; public static MethodInfo CountWithPredicateAsync => __countWithPredicateAsync; public static MethodInfo DensifyWithArrayPartitionByFields => __densifyWithArrayPartitionByFields; + public static MethodInfo Documents => __documents; + public static MethodInfo DocumentsWithSerializer => __documentsWithSerializer; public static MethodInfo FirstAsync => __firstAsync; public static MethodInfo FirstOrDefaultAsync => __firstOrDefaultAsync; public static MethodInfo FirstOrDefaultWithPredicateAsync => __firstOrDefaultWithPredicateAsync; @@ -354,6 +365,7 @@ static MongoQueryableMethod() public static MethodInfo SingleOrDefaultAsync => __singleOrDefaultAsync; public static MethodInfo SingleOrDefaultWithPredicateAsync => __singleOrDefaultWithPredicateAsync; public static MethodInfo SingleWithPredicateAsync => __singleWithPredicateAsync; + public static MethodInfo SkipWithLong => __skipWithLong; public static MethodInfo StandardDeviationPopulationDecimal => __standardDeviationPopulationDecimal; public static MethodInfo StandardDeviationPopulationDecimalAsync => __standardDeviationPopulationDecimalAsync; public static MethodInfo StandardDeviationPopulationDecimalWithSelector => __standardDeviationPopulationDecimalWithSelector; @@ -454,5 +466,6 @@ static MongoQueryableMethod() public static MethodInfo SumNullableSingleWithSelectorAsync => __sumNullableSingleWithSelectorAsync; public static MethodInfo SumSingleAsync => __sumSingleAsync; public static MethodInfo SumSingleWithSelectorAsync => __sumSingleWithSelectorAsync; + public static MethodInfo TakeWithLong => __takeWithLong; } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/NullableDateTimeMethod.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/NullableDateTimeMethod.cs new file mode 100644 index 00000000000..e1b80e6ba1d --- /dev/null +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/NullableDateTimeMethod.cs @@ -0,0 +1,35 @@ +/* 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.Reflection; + +namespace MongoDB.Driver.Linq.Linq3Implementation.Reflection +{ + internal static class NullableDateTimeMethod + { + // private static fields + private static readonly MethodInfo __toStringWithFormatAndTimezoneAndOnNull; + + // static constructor + static NullableDateTimeMethod() + { + __toStringWithFormatAndTimezoneAndOnNull = ReflectionInfo.Method((DateTime? @this, string format, string timezone, string onNull) => @this.ToString(format, timezone, onNull)); + } + + // public properties + public static MethodInfo ToStringWithFormatAndTimezoneAndOnNull => __toStringWithFormatAndTimezoneAndOnNull; + } +} diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Serializers/EnumUnderlyingTypeSerializer.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Serializers/EnumUnderlyingTypeSerializer.cs index 4ceb9ac8ecd..a862b574a43 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Serializers/EnumUnderlyingTypeSerializer.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Serializers/EnumUnderlyingTypeSerializer.cs @@ -14,6 +14,7 @@ */ using System; +using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Core.Misc; @@ -21,7 +22,12 @@ namespace MongoDB.Driver.Linq.Linq3Implementation.Serializers { - internal class EnumUnderlyingTypeSerializer : StructSerializerBase + internal interface IEnumUnderlyingTypeSerializer + { + IBsonSerializer EnumSerializer { get; } + } + + internal class EnumUnderlyingTypeSerializer : StructSerializerBase, IEnumUnderlyingTypeSerializer where TEnum : Enum where TEnumUnderlyingType : struct { @@ -41,6 +47,9 @@ public EnumUnderlyingTypeSerializer(IBsonSerializer enumSerializer) // public properties public IBsonSerializer EnumSerializer => _enumSerializer; + // explicitly implemented properties + IBsonSerializer IEnumUnderlyingTypeSerializer.EnumSerializer => EnumSerializer; + // public methods public override TEnumUnderlyingType Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) { @@ -48,6 +57,17 @@ public override TEnumUnderlyingType Deserialize(BsonDeserializationContext conte return (TEnumUnderlyingType)(object)enumValue; } + /// + public override bool Equals(object obj) + { + return + obj is EnumUnderlyingTypeSerializer other && + _enumSerializer.Equals(other._enumSerializer); + } + + /// + public override int GetHashCode() => _enumSerializer.GetHashCode(); + public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, TEnumUnderlyingType value) { var enumValue = (TEnum)(object)value; @@ -60,9 +80,9 @@ internal static class EnumUnderlyingTypeSerializer public static IBsonSerializer Create(IBsonSerializer enumSerializer) { var enumType = enumSerializer.ValueType; - var enumUnderlyingType = enumType.GetEnumUnderlyingType(); - var serializerType = typeof(EnumUnderlyingTypeSerializer<,>).MakeGenericType(enumType, enumUnderlyingType); - return (IBsonSerializer)Activator.CreateInstance(serializerType, enumSerializer); + var underlyingType = Enum.GetUnderlyingType(enumType); + var enumUnderlyingTypeSerializerType = typeof(EnumUnderlyingTypeSerializer<,>).MakeGenericType(enumType, underlyingType); + return (IBsonSerializer)Activator.CreateInstance(enumUnderlyingTypeSerializerType, enumSerializer); } } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Serializers/IGroupingSerializer.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Serializers/IGroupingSerializer.cs index d8f41451044..fe13ed0a33b 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Serializers/IGroupingSerializer.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Serializers/IGroupingSerializer.cs @@ -107,5 +107,10 @@ public static IBsonSerializer Create(IBsonSerializer keySerializer, IBsonSeriali var serializerType = typeof(IGroupingSerializer<,>).MakeGenericType(keyType, elementType); return (IBsonSerializer)Activator.CreateInstance(serializerType, keySerializer, elementSerializer); } + + public static IBsonSerializer> Create(IBsonSerializer keySerializer, IBsonSerializer elementSerializer) + { + return new IGroupingSerializer(keySerializer, elementSerializer); + } } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Serializers/KnownSerializers/KnownSerializerFinder.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Serializers/KnownSerializers/KnownSerializerFinder.cs index d86dd76214a..a898fb17216 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Serializers/KnownSerializers/KnownSerializerFinder.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Serializers/KnownSerializers/KnownSerializerFinder.cs @@ -68,34 +68,39 @@ public override Expression Visit(Expression node) var result = base.Visit(node); _registry.Add(node, _currentKnownSerializersNode); - _currentKnownSerializersNode = _currentKnownSerializersNode.Parent; + + var parent = _currentKnownSerializersNode.Parent; + if (ShouldPropagateKnownSerializersToParent(parent)) + { + parent.AddKnownSerializersFromChild(_currentKnownSerializersNode); + } + _currentKnownSerializersNode = parent; + return result; } - protected override Expression VisitBinary(BinaryExpression node) + protected override Expression VisitConditional(ConditionalExpression node) { - var result = base.VisitBinary(node); + var result = base.VisitConditional(node); - if (result is BinaryExpression binaryExpression) + if (_currentKnownSerializersNode.KnownSerializers.TryGetValue(node.Type, out var resultSerializers) && + resultSerializers.Count > 1) { - if (BinaryExpressionToAggregationExpressionTranslator.IsEnumComparisonExpression(binaryExpression)) - { - var leftExpression = ConvertHelper.RemoveConvertToEnumUnderlyingType(binaryExpression.Left); - var rightExpression = ConvertHelper.RemoveConvertToEnumUnderlyingType(binaryExpression.Right); + var ifTrueSerializer = _registry.GetSerializerAtThisLevel(node.IfTrue); + var ifFalseSerializer = _registry.GetSerializerAtThisLevel(node.IfFalse); - if (leftExpression is ConstantExpression leftConstantExpression) - { - var rightExpressionSerializer = _registry.GetSerializer(rightExpression); - var leftExpressionSerializer = EnumUnderlyingTypeSerializer.Create(rightExpressionSerializer); - _registry.AddKnownSerializer(leftExpression, leftExpressionSerializer, allowPropagation: false); - } + if (ifTrueSerializer != null && ifFalseSerializer != null && !ifTrueSerializer.Equals(ifFalseSerializer)) + { + throw new ExpressionNotSupportedException(node, because: "IfTrue and IfFalse expressions have different serializers"); + } - if (rightExpression is ConstantExpression rightConstantExpression) - { - var leftExpressionSerializer = _registry.GetSerializer(leftExpression); - var rightExpressionSerializer = EnumUnderlyingTypeSerializer.Create(leftExpressionSerializer); - _registry.AddKnownSerializer(rightExpression, rightExpressionSerializer, allowPropagation: false); - } + if (ifTrueSerializer != null) + { + _currentKnownSerializersNode.SetKnownSerializerForType(node.Type, ifTrueSerializer); + } + else if (ifFalseSerializer != null) + { + _currentKnownSerializersNode.SetKnownSerializerForType(node.Type, ifFalseSerializer); } } @@ -202,5 +207,20 @@ protected override Expression VisitParameter(ParameterExpression node) return result; } + + private bool ShouldPropagateKnownSerializersToParent(KnownSerializersNode parent) + { + if (parent == null) + { + return false; + } + + return parent.Expression.NodeType switch + { + ExpressionType.MemberInit => false, + ExpressionType.New => false, + _ => true + }; + } } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Serializers/KnownSerializers/KnownSerializersNode.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Serializers/KnownSerializers/KnownSerializersNode.cs index 8e1ff9f3b9b..15be366d647 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Serializers/KnownSerializers/KnownSerializersNode.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Serializers/KnownSerializers/KnownSerializersNode.cs @@ -27,6 +27,7 @@ internal class KnownSerializersNode // private fields private readonly Expression _expression; private readonly Dictionary> _knownSerializers = new Dictionary>(); + private IBsonSerializer _nodeSerializer; // a serializer used only for this node (not propagated upwards) private readonly KnownSerializersNode _parent; // constructors @@ -42,7 +43,16 @@ public KnownSerializersNode(Expression expression, KnownSerializersNode parent) public KnownSerializersNode Parent => _parent; // public methods - public void AddKnownSerializer(Type type, IBsonSerializer serializer, bool allowPropagation = true) + public void AddKnownSerializersFromChild(KnownSerializersNode child) + { + foreach (var type in child.KnownSerializers.Keys) + foreach (var serializer in child.KnownSerializers[type]) + { + AddKnownSerializer(type, serializer); + } + } + + public void AddKnownSerializer(Type type, IBsonSerializer serializer) { if (!_knownSerializers.TryGetValue(type, out var set)) { @@ -51,15 +61,35 @@ public void AddKnownSerializer(Type type, IBsonSerializer serializer, bool allow } set.Add(serializer); + } + + public void SetKnownSerializerForType(Type type, IBsonSerializer serializer) + { + if (serializer.ValueType != type) + { + throw new ArgumentException($"Serializer value type {serializer.ValueType} does not match expected type {type}."); + } + + _knownSerializers[type] = new HashSet { serializer }; + } - if (allowPropagation && ShouldPropagateKnownSerializerToParent()) + public void SetNodeSerializer(IBsonSerializer serializer) + { + if (serializer.ValueType != _expression.Type) { - _parent.AddKnownSerializer(type, serializer); + throw new ArgumentException($"Serializer value type {serializer.ValueType} does not match expression type {_expression.Type}."); } + + _nodeSerializer = serializer; } public HashSet GetPossibleSerializers(Type type) { + if (_nodeSerializer != null && _nodeSerializer.ValueType == type) + { + return new HashSet { _nodeSerializer }; + } + var possibleSerializers = GetPossibleSerializersAtThisLevel(type); if (possibleSerializers.Count > 0) { @@ -92,7 +122,7 @@ private HashSet GetPossibleSerializersAtThisLevel(Type type) foreach (var serializer in _knownSerializers.Values.SelectMany(hashset => hashset)) { var valueType = serializer.ValueType; - if (valueType == type || valueType.IsEnum() && Enum.GetUnderlyingType(valueType) == type) + if (valueType == type || valueType.IsEnum && Enum.GetUnderlyingType(valueType) == type) { possibleSerializers.Add(serializer); } @@ -115,20 +145,5 @@ private HashSet GetPossibleSerializersAtThisLevel(Type type) return possibleSerializers; } - - private bool ShouldPropagateKnownSerializerToParent() - { - if (_parent == null) - { - return false; - } - - return _parent.Expression.NodeType switch - { - ExpressionType.MemberInit => false, - ExpressionType.New => false, - _ => true - }; - } } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Serializers/KnownSerializers/KnownSerializersRegistry.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Serializers/KnownSerializers/KnownSerializersRegistry.cs index ade5531fb13..8e3c64b4774 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Serializers/KnownSerializers/KnownSerializersRegistry.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Serializers/KnownSerializers/KnownSerializersRegistry.cs @@ -42,11 +42,11 @@ public void Add(Expression expression, KnownSerializersNode knownSerializers) _registry.Add(expression, knownSerializers); } - public void AddKnownSerializer(Expression expression, IBsonSerializer knownSerializer, bool allowPropagation = true) + public void SetNodeSerializer(Expression expression, IBsonSerializer nodeSerializer) { - if (knownSerializer.ValueType != expression.Type) + if (nodeSerializer.ValueType != expression.Type) { - throw new ArgumentException($"Serializer value type {knownSerializer.ValueType} does not match expresion type {expression.Type}.", nameof(knownSerializer)); + throw new ArgumentException($"Serializer value type {nodeSerializer.ValueType} does not match expresion type {expression.Type}.", nameof(nodeSerializer)); } if (!_registry.TryGetValue(expression, out var knownSerializers)) @@ -54,7 +54,7 @@ public void AddKnownSerializer(Expression expression, IBsonSerializer knownSeria throw new InvalidOperationException("KnownSerializersNode does not exist yet for expression: {expression}."); } - knownSerializers.AddKnownSerializer(expression.Type, knownSerializer, allowPropagation); + knownSerializers.SetNodeSerializer(nodeSerializer); } public IBsonSerializer GetSerializer(Expression expression, IBsonSerializer defaultSerializer = null) @@ -74,6 +74,18 @@ public IBsonSerializer GetSerializer(Expression expression, Type type, IBsonSeri }; } + public IBsonSerializer GetSerializerAtThisLevel(Expression expression) + { + var expressionType = expression is LambdaExpression lambdaExpression ? lambdaExpression.ReturnType : expression.Type; + return GetSerializerAtThisLevel(expression, expressionType); + } + + public IBsonSerializer GetSerializerAtThisLevel(Expression expression, Type type) + { + var possibleSerializers = _registry.TryGetValue(expression, out var knownSerializers) ? knownSerializers.GetPossibleSerializers(type) : new HashSet(); + return possibleSerializers.Count == 1 ? possibleSerializers.Single() : null; + } + private IBsonSerializer LookupSerializer(Expression expression, Type type) { if (type.IsConstructedGenericType && diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/BinaryExpressionToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/BinaryExpressionToAggregationExpressionTranslator.cs index 70452e9a850..c7447dbe658 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/BinaryExpressionToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/BinaryExpressionToAggregationExpressionTranslator.cs @@ -15,10 +15,13 @@ 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; +using MongoDB.Driver.Linq.Linq3Implementation.ExtensionMethods; using MongoDB.Driver.Linq.Linq3Implementation.Misc; +using MongoDB.Driver.Linq.Linq3Implementation.Serializers; using MongoDB.Driver.Support; namespace MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators @@ -40,10 +43,9 @@ public static AggregationExpression Translate(TranslationContext context, Binary rightExpression = ConvertHelper.RemoveWideningConvert(rightExpression); } - if (IsEnumComparisonExpression(expression)) + if (IsEnumExpression(expression)) { - leftExpression = ConvertHelper.RemoveConvertToEnumUnderlyingType(leftExpression); - rightExpression = ConvertHelper.RemoveConvertToEnumUnderlyingType(rightExpression); + return TranslateEnumExpression(context, expression); } var leftTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, leftExpression); @@ -94,6 +96,16 @@ public static AggregationExpression Translate(TranslationContext context, Binary return new AggregationExpression(expression, ast, serializer); } + private static bool IsAddOrSubtractExpression(Expression expression) + { + return expression.NodeType switch + { + ExpressionType.Add => true, + ExpressionType.Subtract => true, + _ => false + }; + } + private static bool IsArithmeticExpression(BinaryExpression expression) { return expression.Type.IsNumeric() && IsArithmeticOperator(expression.NodeType); @@ -113,9 +125,9 @@ private static bool IsArithmeticOperator(ExpressionType nodeType) }; } - private static bool IsComparisonOperator(ExpressionType nodeType) + private static bool IsComparisonExpression(Expression expression) { - return nodeType switch + return expression.NodeType switch { ExpressionType.Equal => true, ExpressionType.GreaterThan => true, @@ -127,24 +139,31 @@ private static bool IsComparisonOperator(ExpressionType nodeType) }; } - internal static bool IsEnumComparisonExpression(BinaryExpression expression) + static bool IsConvertEnumToUnderlyingType(Expression expression) { - return - IsComparisonOperator(expression.NodeType) && - (IsConvertToEnumUnderlyingType(expression.Left) || IsConvertToEnumUnderlyingType(expression.Right)); - - static bool IsConvertToEnumUnderlyingType(Expression expression) + if (expression.NodeType == ExpressionType.Convert) { - if (expression.NodeType == ExpressionType.Convert) - { - var convertExpression = (UnaryExpression)expression; - var sourceType = convertExpression.Operand.Type; - var targetType = convertExpression.Type; - return sourceType.IsEnum() && targetType == Enum.GetUnderlyingType(sourceType); - } + var convertExpression = (UnaryExpression)expression; + var sourceType = convertExpression.Operand.Type; + var targetType = convertExpression.Type; - return false; + return + sourceType.IsEnumOrNullableEnum(out _, out var underlyingType) && + targetType.IsSameAsOrNullableOf(underlyingType); } + + return false; + } + + internal static bool IsEnumExpression(BinaryExpression expression) + { + return IsEnumOrConvertEnumToUnderlyingType(expression.Left) || IsEnumOrConvertEnumToUnderlyingType(expression.Right); + + } + + static bool IsEnumOrConvertEnumToUnderlyingType(Expression expression) + { + return expression.Type.IsEnum || IsConvertEnumToUnderlyingType(expression); } private static bool IsStringConcatenationExpression(BinaryExpression expression) @@ -155,5 +174,112 @@ private static bool IsStringConcatenationExpression(BinaryExpression expression) expression.Left.Type == typeof(string) && expression.Right.Type == typeof(string); } + + private static AstBinaryOperator ToBinaryOperator(ExpressionType nodeType) + { + return nodeType switch + { + ExpressionType.Equal => AstBinaryOperator.Eq, + ExpressionType.NotEqual => AstBinaryOperator.Ne, + ExpressionType.LessThan => AstBinaryOperator.Lt, + ExpressionType.LessThanOrEqual => AstBinaryOperator.Lte, + ExpressionType.GreaterThan => AstBinaryOperator.Gt, + ExpressionType.GreaterThanOrEqual => AstBinaryOperator.Gte, + ExpressionType.Subtract => AstBinaryOperator.Subtract, + _ => throw new Exception($"Unexpected expression type: {nodeType}.") + }; + } + + private static AggregationExpression TranslateEnumExpression(TranslationContext context, BinaryExpression expression) + { + var leftExpression = expression.Left; + var rightExpression = expression.Right; + + AggregationExpression leftTranslation; + AggregationExpression rightTranslation; + IBsonSerializer serializer; + + if (IsComparisonExpression(expression)) + { + if (leftExpression.NodeType == ExpressionType.Constant) + { + rightTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, rightExpression); + leftTranslation = TranslateEnumConstant(expression, leftExpression, rightTranslation.Serializer); + } + else if (rightExpression.NodeType == ExpressionType.Constant) + { + leftTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, leftExpression); + rightTranslation = TranslateEnumConstant(expression, rightExpression, leftTranslation.Serializer); + } + else + { + leftTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, leftExpression); + rightTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, rightExpression); + } + + if (!leftTranslation.Serializer.Equals(rightTranslation.Serializer)) + { + throw new ExpressionNotSupportedException(expression, because: "the two enums being compared are serialized using different serializers"); + } + + serializer = BooleanSerializer.Instance; + } + else if (IsAddOrSubtractExpression(expression)) + { + leftTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, leftExpression); + rightTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, rightExpression); + + if (IsEnumOrConvertEnumToUnderlyingType(leftExpression)) + { + serializer = leftTranslation.Serializer; + } + else + { + serializer = rightTranslation.Serializer; + } + + var representation = BsonType.Int32; // assume an integer representation unless we can determine otherwise + var valueSerializer = serializer; + if (valueSerializer is INullableSerializer nullableSerializer) + { + valueSerializer = nullableSerializer.ValueSerializer; + } + if (valueSerializer is IEnumUnderlyingTypeSerializer enumUnderlyingTypeSerializer && + enumUnderlyingTypeSerializer.EnumSerializer is IHasRepresentationSerializer withRepresentationSerializer) + { + representation = withRepresentationSerializer.Representation; + } + + if (representation != BsonType.Int32 && representation != BsonType.Int64) + { + throw new ExpressionNotSupportedException(expression, because: "arithmetic on enums is only allowed when the enum is represented as an integer"); + } + } + else + { + throw new ExpressionNotSupportedException(expression); + } + + AstExpression ast; + if (expression.NodeType == ExpressionType.Add) + { + ast = AstExpression.Add(leftTranslation.Ast, rightTranslation.Ast); + } + else + { + var binaryOperator = ToBinaryOperator(expression.NodeType); + ast = AstExpression.Binary(binaryOperator, leftTranslation.Ast, rightTranslation.Ast); + } + + return new AggregationExpression(expression, ast, serializer); + + static AggregationExpression TranslateEnumConstant(Expression expression, Expression constantExpression, IBsonSerializer serializer) + { + var value = constantExpression.GetConstantValue(expression); + var serializedValue = SerializationHelper.SerializeValue(serializer, value); + var ast = AstExpression.Constant(serializedValue); + return new AggregationExpression(constantExpression, ast, serializer); + } + } } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ConvertExpressionToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ConvertExpressionToAggregationExpressionTranslator.cs index 5991c8ff1b2..4d84e9a9b2c 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ConvertExpressionToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ConvertExpressionToAggregationExpressionTranslator.cs @@ -15,10 +15,12 @@ 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; using MongoDB.Driver.Linq.Linq3Implementation.Misc; +using MongoDB.Driver.Linq.Linq3Implementation.Serializers; namespace MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators { @@ -28,10 +30,25 @@ public static AggregationExpression Translate(TranslationContext context, UnaryE { if (expression.NodeType == ExpressionType.Convert) { + var expressionType = expression.Type; + if (expressionType == typeof(BsonValue)) + { + return TranslateConvertToBsonValue(context, expression, expression.Operand); + } + var operandExpression = expression.Operand; var operandTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, operandExpression); - var expressionType = expression.Type; + if (IsConvertEnumToUnderlyingType(expression)) + { + return TranslateConvertEnumToUnderlyingType(expression, operandTranslation); + } + + if (IsConvertUnderlyingTypeToEnum(expression)) + { + return TranslateConvertUnderlyingTypeToEnum(expression, operandTranslation); + } + if (expressionType.IsConstructedGenericType && expressionType.GetGenericTypeDefinition() == typeof(Nullable<>)) { var valueType = expressionType.GetGenericArguments()[0]; @@ -46,12 +63,132 @@ public static AggregationExpression Translate(TranslationContext context, UnaryE } } - var ast = AstExpression.Convert(operandTranslation.Ast, expressionType); - var serializer = context.KnownSerializersRegistry.GetSerializer(expression); + 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 + { + var to = expressionType.FullName switch + { + "MongoDB.Bson.ObjectId" => "objectId", + "System.Boolean" => "bool", + "System.DateTime" => "date", + "System.Decimal" => "decimal", + "System.Double" => "double", + "System.Int32" => "int", + "System.Int64" => "long", + "System.String" => "string", + _ => throw new ExpressionNotSupportedException(expression, because: $"conversion to {expressionType} is not supported") + }; + + ast = AstExpression.Convert(ast, to); + serializer = context.KnownSerializersRegistry.GetSerializer(expression); + } + return new AggregationExpression(expression, ast, serializer); } throw new ExpressionNotSupportedException(expression); } + + private static bool IsConvertEnumToUnderlyingType(UnaryExpression expression) + { + var sourceType = expression.Operand.Type; + var targetType = expression.Type; + + return + sourceType.IsEnumOrNullableEnum(out _, out var underlyingType) && + targetType.IsSameAsOrNullableOf(underlyingType); + } + + private static bool IsConvertUnderlyingTypeToEnum(UnaryExpression expression) + { + var sourceType = expression.Operand.Type; + var targetType = expression.Type; + + return + targetType.IsEnumOrNullableEnum(out _, out var underlyingType) && + sourceType.IsSameAsOrNullableOf(underlyingType); + } + + private static AggregationExpression TranslateConvertToBsonValue(TranslationContext context, UnaryExpression expression, Expression operand) + { + // 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); + } + + private static AggregationExpression TranslateConvertEnumToUnderlyingType(UnaryExpression expression, AggregationExpression operandTranslation) + { + 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; + } + + IBsonSerializer targetSerializer; + var enumUnderlyingTypeSerializer = EnumUnderlyingTypeSerializer.Create(enumSerializer); + if (targetType.IsNullable()) + { + targetSerializer = NullableSerializer.Create(enumUnderlyingTypeSerializer); + } + else + { + targetSerializer = enumUnderlyingTypeSerializer; + } + + return new AggregationExpression(expression, operandTranslation.Ast, targetSerializer); + } + + private static AggregationExpression TranslateConvertUnderlyingTypeToEnum(UnaryExpression expression, AggregationExpression operandTranslation) + { + var sourceType = expression.Operand.Type; + var targetType = expression.Type; + + IBsonSerializer enumUnderlyingTypeSerializer; + if (sourceType.IsNullable()) + { + var nullableSerializer = (INullableSerializer)operandTranslation.Serializer; + enumUnderlyingTypeSerializer = nullableSerializer.ValueSerializer; + } + else + { + enumUnderlyingTypeSerializer = operandTranslation.Serializer; + } + + IBsonSerializer targetSerializer; + var enumSerializer = ((IEnumUnderlyingTypeSerializer)enumUnderlyingTypeSerializer).EnumSerializer; + if (targetType.IsNullableEnum()) + { + targetSerializer = NullableSerializer.Create(enumSerializer); + } + else + { + targetSerializer = enumSerializer; + } + + return new AggregationExpression(expression, operandTranslation.Ast, targetSerializer); + } } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MemberExpressionToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MemberExpressionToAggregationExpressionTranslator.cs index ce75a035424..cfe8ec6a8c6 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MemberExpressionToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MemberExpressionToAggregationExpressionTranslator.cs @@ -21,7 +21,6 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Driver.Linq.Linq3Implementation.Ast; using MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions; using MongoDB.Driver.Linq.Linq3Implementation.Misc; using MongoDB.Driver.Linq.Linq3Implementation.Serializers; @@ -36,7 +35,7 @@ public static AggregationExpression Translate(TranslationContext context, Member var containerExpression = expression.Expression; var member = expression.Member; - if (member is PropertyInfo property) + if (member is PropertyInfo property && property.DeclaringType.IsNullable()) { switch (property.Name) { @@ -50,10 +49,10 @@ public static AggregationExpression Translate(TranslationContext context, Member if (containerTranslation.Serializer is IWrappedValueSerializer wrappedValueSerializer) { var unwrappedValueAst = AstExpression.GetField(containerTranslation.Ast, wrappedValueSerializer.FieldName); - containerTranslation = new AggregationExpression(expression, unwrappedValueAst, wrappedValueSerializer.ValueSerializer); + containerTranslation = new AggregationExpression(containerExpression, unwrappedValueAst, wrappedValueSerializer.ValueSerializer); } - if (!DocumentSerializerHelper.HasFieldInfo(containerTranslation.Serializer, member.Name)) + if (!DocumentSerializerHelper.HasMemberSerializationInfo(containerTranslation.Serializer, member.Name)) { if (member is PropertyInfo propertyInfo && propertyInfo.Name == "Length") { @@ -71,9 +70,9 @@ public static AggregationExpression Translate(TranslationContext context, Member } } - var fieldInfo = DocumentSerializerHelper.GetFieldInfo(containerTranslation.Serializer, member.Name); - var ast = AstExpression.GetField(containerTranslation.Ast, fieldInfo.ElementName); - return new AggregationExpression(expression, ast, fieldInfo.Serializer); + var serializationInfo = DocumentSerializerHelper.GetMemberSerializationInfo(containerTranslation.Serializer, member.Name); + var ast = AstExpression.GetField(containerTranslation.Ast, serializationInfo.ElementName); + return new AggregationExpression(expression, ast, serializationInfo.Serializer); } private static bool TryTranslateCollectionCountProperty(MemberExpression expression, AggregationExpression container, MemberInfo memberInfo, out AggregationExpression result) @@ -124,7 +123,6 @@ private static bool TryTranslateDateTimeProperty(MemberExpression expression, Ag case "Minute": datePart = AstDatePart.Minute; break; case "Month": datePart = AstDatePart.Month; break; case "Second": datePart = AstDatePart.Second; break; - case "Week": datePart = AstDatePart.Week; break; case "Year": datePart = AstDatePart.Year; break; default: return false; } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MemberInitExpressionToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MemberInitExpressionToAggregationExpressionTranslator.cs index a3ee6fcde1d..71d8b185f22 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MemberInitExpressionToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MemberInitExpressionToAggregationExpressionTranslator.cs @@ -13,6 +13,7 @@ * limitations under the License. */ +using System; using System.Collections.Generic; using System.Linq.Expressions; using System.Reflection; @@ -26,51 +27,97 @@ internal static class MemberInitExpressionToAggregationExpressionTranslator { public static AggregationExpression Translate(TranslationContext context, MemberInitExpression expression) { - var classSerializer = BsonSerializer.LookupSerializer(expression.Type); - if (classSerializer is IBsonDocumentSerializer documentSerializer) + var computedFields = new List(); + var classMap = CreateClassMap(expression.Type); + + var newExpression = expression.NewExpression; + var constructorParameters = newExpression.Constructor.GetParameters(); + var constructorArguments = newExpression.Arguments; + for (var i = 0; i < constructorParameters.Length; i++) { - var computedFields = new List(); + var constructorParameter = constructorParameters[i]; + var memberMap = FindMatchingMemberMap(expression, classMap, constructorParameter); - var newExpression = expression.NewExpression; - var constructorParameters = newExpression.Constructor.GetParameters(); - var constructorArguments = newExpression.Arguments; - for (var i = 0; i < constructorParameters.Length; i++) - { - var constructorParameter = constructorParameters[i]; - var argumentExpression = constructorArguments[i]; - var fieldName = GetFieldName(constructorParameter); - var argumentTanslation = ExpressionToAggregationExpressionTranslator.Translate(context, argumentExpression); - computedFields.Add(AstExpression.ComputedField(fieldName, argumentTanslation.Ast)); - } + var argumentExpression = constructorArguments[i]; + var argumentTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, argumentExpression); + computedFields.Add(AstExpression.ComputedField(memberMap.ElementName, argumentTranslation.Ast)); + + memberMap.SetSerializer(argumentTranslation.Serializer); + } - foreach (var binding in expression.Bindings) + foreach (var binding in expression.Bindings) + { + var memberAssignment = (MemberAssignment)binding; + var member = memberAssignment.Member; + var memberMap = FindMemberMap(expression, classMap, member.Name); + + var valueExpression = memberAssignment.Expression; + var valueTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, valueExpression); + computedFields.Add(AstExpression.ComputedField(memberMap.ElementName, valueTranslation.Ast)); + + memberMap.SetSerializer(valueTranslation.Serializer); + } + + var ast = AstExpression.ComputedDocument(computedFields); + + classMap.Freeze(); + var serializerType = typeof(BsonClassMapSerializer<>).MakeGenericType(expression.Type); + var serializer = (IBsonSerializer)Activator.CreateInstance(serializerType, classMap); + + return new AggregationExpression(expression, ast, serializer); + } + + private static BsonClassMap CreateClassMap(Type classType) + { + BsonClassMap baseClassMap = null; + if (classType.BaseType != null) + { + baseClassMap = CreateClassMap(classType.BaseType); + } + + var classMapType = typeof(BsonClassMap<>).MakeGenericType(classType); + var constructorInfo = classMapType.GetConstructor(new Type[] { typeof(BsonClassMap) }); + var classMap = (BsonClassMap)constructorInfo.Invoke(new object[] { baseClassMap }); + classMap.AutoMap(); + classMap.IdMemberMap?.SetElementName("_id"); // normally happens when Freeze is called but we need it sooner here + + return classMap; + } + + private static BsonMemberMap FindMatchingMemberMap(Expression expression, BsonClassMap classMap, ParameterInfo parameterInfo) + { + foreach (var memberMap in classMap.DeclaredMemberMaps) + { + if (memberMap.MemberType == parameterInfo.ParameterType && memberMap.MemberName.Equals(parameterInfo.Name, StringComparison.OrdinalIgnoreCase)) { - var memberAssignment = (MemberAssignment)binding; - var member = memberAssignment.Member; - if (!(documentSerializer.TryGetMemberSerializationInfo(member.Name, out var memberSerializationInfo))) - { - throw new ExpressionNotSupportedException(expression); - } - var elementName = memberSerializationInfo.ElementName; - var valueExpression = memberAssignment.Expression; - var valueTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, valueExpression); - computedFields.Add(AstExpression.ComputedField(elementName, valueTranslation.Ast)); + return memberMap; } + } - var ast = AstExpression.ComputedDocument(computedFields); - var serializer = context.KnownSerializersRegistry.GetSerializer(expression); - return new AggregationExpression(expression, ast, serializer); + if (classMap.BaseClassMap != null) + { + return FindMatchingMemberMap(expression, classMap.BaseClassMap, parameterInfo); } - throw new ExpressionNotSupportedException(expression); + throw new ExpressionNotSupportedException(expression, because: $"can't find matching property for constructor parameter : {parameterInfo.Name}"); } - private static string GetFieldName(ParameterInfo parameter) + private static BsonMemberMap FindMemberMap(Expression expression, BsonClassMap classMap, string memberName) { - // TODO: implement properly - var parameterName = parameter.Name; - var fieldName = parameterName.Substring(0, 1).ToUpper() + parameterName.Substring(1); - return fieldName; + foreach (var memberMap in classMap.DeclaredMemberMaps) + { + if (memberMap.MemberName == memberName) + { + return memberMap; + } + } + + if (classMap.BaseClassMap != null) + { + return FindMemberMap(expression, classMap.BaseClassMap, memberName); + } + + throw new ExpressionNotSupportedException(expression, because: $"can't find member map: {memberName}"); } } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs index 61a0eeffd0d..8dc2ec42dcd 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs @@ -73,10 +73,28 @@ public static AggregationExpression Translate(TranslationContext context, Method case "ToList": return ToListMethodToAggregationExpressionTranslator.Translate(context, expression); case "ToString": return ToStringMethodToAggregationExpressionTranslator.Translate(context, expression); case "Truncate": return TruncateMethodToAggregationExpressionTranslator.Translate(context, expression); + case "Week": return WeekMethodToAggregationExpressionTranslator.Translate(context, expression); case "Where": return WhereMethodToAggregationExpressionTranslator.Translate(context, expression); case "Union": return UnionMethodToAggregationExpressionTranslator.Translate(context, expression); case "Zip": return ZipMethodToAggregationExpressionTranslator.Translate(context, expression); + case "Acos": + case "Acosh": + case "Asin": + case "Asinh": + case "Atan": + case "Atan2": + case "Atanh": + case "Cos": + case "Cosh": + case "DegreesToRadians": + case "RadiansToDegrees": + case "Sin": + case "Sinh": + case "Tan": + case "Tanh": + return TrigMethodToAggregationExpressionTranslator.Translate(context, expression); + case "AddDays": case "AddHours": case "AddMilliseconds": diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ToStringMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ToStringMethodToAggregationExpressionTranslator.cs index 45492586fd8..df9df6212e6 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ToStringMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ToStringMethodToAggregationExpressionTranslator.cs @@ -13,64 +13,110 @@ * limitations under the License. */ -using System; +using System.Linq; using System.Linq.Expressions; +using System.Reflection; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions; +using MongoDB.Driver.Linq.Linq3Implementation.Misc; +using MongoDB.Driver.Linq.Linq3Implementation.Reflection; namespace MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators.MethodTranslators { internal static class ToStringMethodToAggregationExpressionTranslator { + private static readonly MethodInfo[] __dateTimeToStringMethods = new[] + { + DateTimeMethod.ToStringWithFormat, + DateTimeMethod.ToStringWithFormatAndTimezone, + NullableDateTimeMethod.ToStringWithFormatAndTimezoneAndOnNull, + }; + + private static readonly MethodInfo[] __dateTimeToStringMethodsWithTimezone = new[] + { + DateTimeMethod.ToStringWithFormatAndTimezone, + NullableDateTimeMethod.ToStringWithFormatAndTimezoneAndOnNull, + }; + + private static readonly MethodInfo[] __dateTimeToStringMethodsWithOnNull = new[] + { + NullableDateTimeMethod.ToStringWithFormatAndTimezoneAndOnNull, + }; + public static AggregationExpression Translate(TranslationContext context, MethodCallExpression expression) { - if (TryTranslateInstanceToStringWithNoArguments(context, expression, out var translation) || - TryTranslateDateTimeToStringWithFormat(context, expression, out translation)) + var method = expression.Method; + var arguments = expression.Arguments.ToArray(); + + if (IsInstanceToStringMethodWithNoArguments(method)) { - return translation; + return TranslateInstanceToStringMethodWithNoArguments(context, expression); + } + + if (method.IsOneOf(__dateTimeToStringMethods)) + { + return TranslateDateTimeToStringMethod(context, expression, method, arguments); } throw new ExpressionNotSupportedException(expression); } - private static bool TryTranslateDateTimeToStringWithFormat(TranslationContext context, MethodCallExpression expression, out AggregationExpression translation) + private static bool IsInstanceToStringMethodWithNoArguments(MethodInfo method) { - var method = expression.Method; - var arguments = expression.Arguments; + return + !method.IsStatic && + method.Name == "ToString" && + method.ReturnType == typeof(string) && + method.GetParameters().Length == 0; + } - if (method.DeclaringType != typeof(DateTime) || method.IsStatic || method.ReturnType != typeof(string) || method.Name != "ToString" || arguments.Count != 1 || arguments[0].Type != typeof(string)) - { - translation = null; - return false; - } + private static AggregationExpression TranslateDateTimeToStringMethod(TranslationContext context, MethodCallExpression expression, MethodInfo method, Expression[] arguments) + { - var dateTimeExpression = expression.Object; + var dateTimeExpression = method.IsStatic ? arguments[0] : expression.Object; var dateTimeTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, dateTimeExpression); - var formatExpression = arguments[0]; - var formatTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, formatExpression); - var ast = AstExpression.DateToString(dateTimeTranslation.Ast, formatTranslation.Ast); - translation = new AggregationExpression(expression, ast, new StringSerializer()); - return true; - } + var dateAst = dateTimeTranslation.Ast; - private static bool TryTranslateInstanceToStringWithNoArguments(TranslationContext context, MethodCallExpression expression, out AggregationExpression translation) - { - var method = expression.Method; - var arguments = expression.Arguments; - translation = null; + AstExpression formatAst = null; + var formatExpression = method.IsStatic ? arguments[1] : arguments[0]; + if (!(formatExpression is ConstantExpression constantExprssion) || constantExprssion.Value != null) + { + var formatTranslataion = ExpressionToAggregationExpressionTranslator.Translate(context, formatExpression); + formatAst = formatTranslataion.Ast; + } - if (method.IsStatic || method.ReturnType != typeof(string) || method.Name != "ToString" || arguments.Count != 0) + AstExpression timezoneAst = null; + if (method.IsOneOf(__dateTimeToStringMethodsWithTimezone)) { - return false; + var timezoneExpression = arguments[2]; + if (!(timezoneExpression is ConstantExpression constantExpression) || constantExpression.Value != null) + { + var timezoneTranslataion = ExpressionToAggregationExpressionTranslator.Translate(context, timezoneExpression); + timezoneAst = timezoneTranslataion.Ast; + } } - var objectExpression = expression.Object; + AstExpression onNullAst = null; + if (method.IsOneOf(__dateTimeToStringMethodsWithOnNull)) + { + var onNullExpression = arguments[3]; + var onNullTranslataion = ExpressionToAggregationExpressionTranslator.Translate(context, onNullExpression); + if (!(onNullExpression is ConstantExpression constantExpression) || constantExpression.Value != null) + { + onNullAst = onNullTranslataion.Ast; + } + } + + var ast = AstExpression.DateToString(dateAst, formatAst, timezoneAst, onNullAst); + return new AggregationExpression(expression, ast, StringSerializer.Instance); + } + private static AggregationExpression TranslateInstanceToStringMethodWithNoArguments(TranslationContext context, MethodCallExpression expression) + { + var objectExpression = expression.Object; var objectTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, objectExpression); var ast = AstExpression.ToString(objectTranslation.Ast); - - translation = new AggregationExpression(expression, ast, new StringSerializer()); - return true; + return new AggregationExpression(expression, ast, StringSerializer.Instance); } } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/TrigMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/TrigMethodToAggregationExpressionTranslator.cs new file mode 100644 index 00000000000..1fd0e028704 --- /dev/null +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/TrigMethodToAggregationExpressionTranslator.cs @@ -0,0 +1,122 @@ +/* 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 System.Linq.Expressions; +using System.Reflection; +using MongoDB.Bson.Serialization.Serializers; +using MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions; +using MongoDB.Driver.Linq.Linq3Implementation.Misc; +using MongoDB.Driver.Linq.Linq3Implementation.Reflection; + +namespace MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators.MethodTranslators +{ + internal static class TrigMethodToAggregationExpressionTranslator + { + private static readonly MethodInfo[] __binaryTrigMethods; + private static readonly MethodInfo[] __unaryTrigMethods; + + static TrigMethodToAggregationExpressionTranslator() + { + __binaryTrigMethods = new[] + { + MathMethod.Atan2 + }; + + __unaryTrigMethods = new[] + { + MathMethod.Acos, + MathMethod.Acosh, + MathMethod.Asin, + MathMethod.Asinh, + MathMethod.Atan, + MathMethod.Atanh, + MathMethod.Cos, + MathMethod.Cosh, + MathMethod.Sin, + MathMethod.Sinh, + MathMethod.Tan, + MathMethod.Tanh, + MongoDBMathMethod.DegreesToRadians, + MongoDBMathMethod.RadiansToDegrees + }; + } + + public static AggregationExpression Translate(TranslationContext context, MethodCallExpression expression) + { + var method = expression.Method; + var arguments = expression.Arguments; + + if (method.IsOneOf(__unaryTrigMethods)) + { + var argExpression = arguments.Single(); + var argTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, argExpression); + + var unaryOperator = ToUnaryOperator(method.Name); + AstExpression ast = AstExpression.Unary(unaryOperator, argTranslation.Ast); + + return new AggregationExpression(expression, ast, DoubleSerializer.Instance); + } + + if (method.IsOneOf(__binaryTrigMethods)) + { + var arg1Expression = arguments[0]; + var arg1Translation = ExpressionToAggregationExpressionTranslator.Translate(context, arg1Expression); + + var arg2Expression = arguments[1]; + var arg2Translation = ExpressionToAggregationExpressionTranslator.Translate(context, arg2Expression); + + var binaryOperator = ToBinaryOperator(method.Name); + AstExpression ast = AstExpression.Binary(binaryOperator, arg1Translation.Ast, arg2Translation.Ast); + + return new AggregationExpression(expression, ast, DoubleSerializer.Instance); + } + + throw new ExpressionNotSupportedException(expression); + } + + private static AstBinaryOperator ToBinaryOperator(string methodName) + { + return methodName switch + { + "Atan2" => AstBinaryOperator.Atan2, + _ => throw new ArgumentException($"Unexpected method name: {methodName}.", nameof(methodName)) + }; + } + + private static AstUnaryOperator ToUnaryOperator(string methodName) + { + return methodName switch + { + "Acos" => AstUnaryOperator.Acos, + "Acosh" => AstUnaryOperator.Acosh, + "Asin" => AstUnaryOperator.Asin, + "Asinh" => AstUnaryOperator.Asinh, + "Atan" => AstUnaryOperator.Atan, + "Atanh" => AstUnaryOperator.Atanh, + "Cos" => AstUnaryOperator.Cos, + "Cosh" => AstUnaryOperator.Cosh, + "DegreesToRadians" => AstUnaryOperator.DegreesToRadians, + "RadiansToDegrees" => AstUnaryOperator.RadiansToDegrees, + "Sin" => AstUnaryOperator.Sin, + "Sinh" => AstUnaryOperator.Sinh, + "Tan" => AstUnaryOperator.Tan, + "Tanh" => AstUnaryOperator.Tanh, + _ => throw new ArgumentException($"Unexpected method name: {methodName}.", nameof(methodName)) + }; + } + } +} diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/WeekMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/WeekMethodToAggregationExpressionTranslator.cs new file mode 100644 index 00000000000..d58c09812cd --- /dev/null +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/WeekMethodToAggregationExpressionTranslator.cs @@ -0,0 +1,49 @@ +/* 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.Linq.Expressions; +using MongoDB.Bson.Serialization.Serializers; +using MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions; +using MongoDB.Driver.Linq.Linq3Implementation.Misc; +using MongoDB.Driver.Linq.Linq3Implementation.Reflection; + +namespace MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators.MethodTranslators +{ + internal static class WeekMethodToAggregationExpressionTranslator + { + public static AggregationExpression Translate(TranslationContext context, MethodCallExpression expression) + { + var method = expression.Method; + var arguments = expression.Arguments; + + if (method.IsOneOf(DateTimeMethod.Week, DateTimeMethod.WeekWithTimezone)) + { + var dateExpression = arguments[0]; + var dateTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, dateExpression); + + AggregationExpression timezoneTranslation = null; + if (method.Is(DateTimeMethod.WeekWithTimezone)) + { + var timezoneExpression = arguments[1]; + timezoneTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, timezoneExpression); + } + + var ast = AstExpression.DatePart(AstDatePart.Week, dateTranslation.Ast, timezoneTranslation?.Ast); + return new AggregationExpression(expression, ast, Int32Serializer.Instance); + } + throw new ExpressionNotSupportedException(expression); + } + } +} diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/NewListExpressionToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/NewListExpressionToAggregationExpressionTranslator.cs index 7304a2e9a20..4acc4d8db78 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/NewListExpressionToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/NewListExpressionToAggregationExpressionTranslator.cs @@ -34,13 +34,14 @@ public static AggregationExpression Translate(TranslationContext context, NewExp { var argument = arguments[0]; var argumentType = argument.Type; - if (argumentType.IsConstructedGenericType && argumentType.GetGenericTypeDefinition() == typeof(IEnumerable<>)) + if (argumentType.IsConstructedGenericType && argumentType.GetGenericTypeDefinition().Implements(typeof(IEnumerable<>))) { - var argumentItemType = argumentType.GetGenericArguments()[0]; + var enumerableInterface = argumentType.GetIEnumerableGenericInterface(); + var argumentItemType = enumerableInterface.GetGenericArguments()[0]; if (argumentItemType == listItemType) { var collectionExpression = argument; - var collectionTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, collectionExpression); + var collectionTranslation = ExpressionToAggregationExpressionTranslator.TranslateEnumerable(context, collectionExpression); var listSerializerType = typeof(EnumerableInterfaceImplementerSerializer<,>).MakeGenericType(listType, listItemType); var listItemSerializer = ArraySerializerHelper.GetItemSerializer(collectionTranslation.Serializer); var listSerializer = (IBsonSerializer)Activator.CreateInstance(listSerializerType, listItemSerializer); diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/AllMethodToExecutableQueryTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/AllMethodToExecutableQueryTranslator.cs index e04eaf37af4..fc5f14f83c1 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/AllMethodToExecutableQueryTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/AllMethodToExecutableQueryTranslator.cs @@ -49,7 +49,7 @@ public static ExecutableQuery Translate(MongoQueryPr var pipeline = ExpressionToPipelineTranslator.Translate(context, sourceExpression); var predicateLambda = ExpressionHelper.UnquoteLambda(arguments[1]); - var predicateFilter = ExpressionToFilterTranslator.TranslateLambda(context, predicateLambda, parameterSerializer: pipeline.OutputSerializer); + var predicateFilter = ExpressionToFilterTranslator.TranslateLambda(context, predicateLambda, parameterSerializer: pipeline.OutputSerializer, asRoot: true); pipeline = pipeline.AddStages( __outputSerializer, @@ -60,8 +60,7 @@ public static ExecutableQuery Translate(MongoQueryPr AstProject.Set("_v", BsonNull.Value))); return ExecutableQuery.Create( - provider.Collection, - provider.Options, + provider, pipeline, __finalizer); } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/AnyMethodToExecutableQueryTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/AnyMethodToExecutableQueryTranslator.cs index 2a49143e30c..f6cd21c4820 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/AnyMethodToExecutableQueryTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/AnyMethodToExecutableQueryTranslator.cs @@ -71,7 +71,7 @@ public static ExecutableQuery Translate(MongoQueryPr if (method.IsOneOf(__anyWithPredicateMethods)) { var predicateLambda = ExpressionHelper.UnquoteLambda(arguments[1]); - var predicateFilter = ExpressionToFilterTranslator.TranslateLambda(context, predicateLambda, parameterSerializer: pipeline.OutputSerializer); + var predicateFilter = ExpressionToFilterTranslator.TranslateLambda(context, predicateLambda, parameterSerializer: pipeline.OutputSerializer, asRoot: true); pipeline = pipeline.AddStages( pipeline.OutputSerializer, @@ -86,8 +86,7 @@ public static ExecutableQuery Translate(MongoQueryPr AstProject.Set("_v", BsonNull.Value))); return ExecutableQuery.Create( - provider.Collection, - provider.Options, + provider, pipeline, __finalizer); } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/AverageMethodToExecutableQueryTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/AverageMethodToExecutableQueryTranslator.cs index a08e41147a8..7522fa50ed9 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/AverageMethodToExecutableQueryTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/AverageMethodToExecutableQueryTranslator.cs @@ -156,8 +156,7 @@ public static ExecutableQuery Translate(MongoQuer AstStage.Project(AstProject.ExcludeId())); return ExecutableQuery.Create( - provider.Collection, - provider.Options, + provider, pipeline, __finalizer); } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/ContainsMethodToExecutableQueryTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/ContainsMethodToExecutableQueryTranslator.cs index b211c9f7477..fcbc7e2c23a 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/ContainsMethodToExecutableQueryTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/ContainsMethodToExecutableQueryTranslator.cs @@ -79,8 +79,7 @@ public static ExecutableQuery Translate(MongoQueryPr AstProject.Set("_v", BsonNull.Value))); return ExecutableQuery.Create( - provider.Collection, - provider.Options, + provider, pipeline, __finalizer); } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/CountMethodToExecutableQueryTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/CountMethodToExecutableQueryTranslator.cs index 8e479e2d36b..c2f8a23d44d 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/CountMethodToExecutableQueryTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/CountMethodToExecutableQueryTranslator.cs @@ -67,7 +67,7 @@ public static ExecutableQuery Translate(MongoQueryPro if (method.IsOneOf(__countWithPredicateMethods)) { var predicateLambda = ExpressionHelper.UnquoteLambda(arguments[1]); - var predicateFilter = ExpressionToFilterTranslator.TranslateLambda(context, predicateLambda, parameterSerializer: pipeline.OutputSerializer); + var predicateFilter = ExpressionToFilterTranslator.TranslateLambda(context, predicateLambda, parameterSerializer: pipeline.OutputSerializer, asRoot: true); pipeline = pipeline.AddStages( pipeline.OutputSerializer, @@ -79,8 +79,7 @@ public static ExecutableQuery Translate(MongoQueryPro AstStage.Count("_v")); return ExecutableQuery.Create( - provider.Collection, - provider.Options, + provider, pipeline, _finalizer); } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/ElementAtMethodToExecutableQueryTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/ElementAtMethodToExecutableQueryTranslator.cs index 080d64a5f25..24d150e303a 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/ElementAtMethodToExecutableQueryTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/ElementAtMethodToExecutableQueryTranslator.cs @@ -48,8 +48,7 @@ public static ExecutableQuery Translate(MongoQuer AstStage.Limit(1)); return ExecutableQuery.Create( - provider.Collection, - provider.Options, + provider, pipeline, __finalizer); } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/ExecutableQuery.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/ExecutableQuery.cs index 6c9376000a7..31e078ab672 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/ExecutableQuery.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/ExecutableQuery.cs @@ -18,6 +18,7 @@ using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.Serialization; +using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Linq.Linq3Implementation.Ast; using MongoDB.Driver.Linq.Linq3Implementation.Ast.Optimizers; @@ -34,25 +35,25 @@ public static ExecutableQuery AsExecutableQuery Create( - IMongoCollection collection, - AggregateOptions options, + MongoQueryProvider provider, AstPipeline unoptimizedPipeline, IExecutableQueryFinalizer finalizer) { var pipeline = AstPipelineOptimizer.Optimize(unoptimizedPipeline); - return new ExecutableQuery(collection, options, unoptimizedPipeline, pipeline, finalizer); + return provider.Collection == null ? + new ExecutableQuery(provider.Database, provider.Options, unoptimizedPipeline, pipeline, finalizer) : + new ExecutableQuery(provider.Collection, provider.Options, unoptimizedPipeline, pipeline, finalizer); } } internal abstract class ExecutableQuery { + public abstract AstPipeline Pipeline { get; } + public abstract AstPipeline UnoptimizedPipeline { get; } } internal abstract class ExecutableQuery : ExecutableQuery { - public abstract AstPipeline Pipeline { get; } - public abstract AstPipeline UnoptimizedPipeline { get; } - public abstract TResult Execute(IClientSessionHandle session, CancellationToken cancellation); public abstract Task ExecuteAsync(IClientSessionHandle session, CancellationToken cancellation); } @@ -61,6 +62,7 @@ internal class ExecutableQuery : ExecutableQuery _collection; + private readonly IMongoDatabase _database; private readonly IExecutableQueryFinalizer _finalizer; private readonly AggregateOptions _options; private readonly AstPipeline _pipeline; @@ -73,8 +75,28 @@ public ExecutableQuery( AstPipeline unoptimizedPipeline, AstPipeline pipeline, IExecutableQueryFinalizer finalizer) + : this(options, unoptimizedPipeline, pipeline, finalizer) + { + _collection = Ensure.IsNotNull(collection, nameof(collection)); + } + + public ExecutableQuery( + IMongoDatabase database, + AggregateOptions options, + AstPipeline unoptimizedPipeline, + AstPipeline pipeline, + IExecutableQueryFinalizer finalizer) + : this(options, unoptimizedPipeline, pipeline, finalizer) + { + _database = Ensure.IsNotNull(database, nameof(database)); + } + + private ExecutableQuery( + AggregateOptions options, + AstPipeline unoptimizedPipeline, + AstPipeline pipeline, + IExecutableQueryFinalizer finalizer) { - _collection = collection; _options = options; _unoptimizedPipeline = unoptimizedPipeline; _pipeline = pipeline; @@ -88,44 +110,47 @@ public ExecutableQuery( // public methods public override TResult Execute(IClientSessionHandle session, CancellationToken cancellationToken) { - var pipelineDefinition = CreatePipelineDefinition(); - IAsyncCursor cursor; - if (session == null) - { - cursor = _collection.Aggregate(pipelineDefinition, _options, cancellationToken); - } - else + var cursor = (_collection, session) switch { - cursor = _collection.Aggregate(session, pipelineDefinition, _options, cancellationToken); - } + (null, null) => _database.Aggregate(CreateDatabasePipelineDefinition(), _options, cancellationToken), + (null, _) => _database.Aggregate(session, CreateDatabasePipelineDefinition(), _options, cancellationToken), + (_, null) => _collection.Aggregate(CreateCollectionPipelineDefinition(), _options, cancellationToken), + (_, _) => _collection.Aggregate(session, CreateCollectionPipelineDefinition(), _options, cancellationToken) + }; + return _finalizer.Finalize(cursor, cancellationToken); } public override async Task ExecuteAsync(IClientSessionHandle session, CancellationToken cancellationToken) { - var pipelineDefinition = CreatePipelineDefinition(); - IAsyncCursor cursor; - if (session == null) - { - cursor = await _collection.AggregateAsync(pipelineDefinition, _options, cancellationToken).ConfigureAwait(false); - } - else + var cursor = (_collection, session) switch { - cursor = await _collection.AggregateAsync(session, pipelineDefinition, _options, cancellationToken).ConfigureAwait(false); - } + (null, null) => await _database.AggregateAsync(CreateDatabasePipelineDefinition(), _options, cancellationToken).ConfigureAwait(false), + (null, _) => await _database.AggregateAsync(session, CreateDatabasePipelineDefinition(), _options, cancellationToken).ConfigureAwait(false), + (_, null) => await _collection.AggregateAsync(CreateCollectionPipelineDefinition(), _options, cancellationToken).ConfigureAwait(false), + (_, _) => await _collection.AggregateAsync(session, CreateCollectionPipelineDefinition(), _options, cancellationToken).ConfigureAwait(false) + }; + return await _finalizer.FinalizeAsync(cursor, cancellationToken).ConfigureAwait(false); } public override string ToString() { - return $"{_collection.CollectionNamespace}.Aggregate({_pipeline})"; + var x = (object)_database?.DatabaseNamespace ?? _collection.CollectionNamespace; + return $"{(_collection == null ? _database.DatabaseNamespace : _collection.CollectionNamespace)}.Aggregate({_pipeline})"; } // private methods - private BsonDocumentStagePipelineDefinition CreatePipelineDefinition() + private BsonDocumentStagePipelineDefinition CreateCollectionPipelineDefinition() { var stages = _pipeline.Stages.Select(s => (BsonDocument)s.Render()); return new BsonDocumentStagePipelineDefinition(stages, (IBsonSerializer)_pipeline.OutputSerializer); } + + private BsonDocumentStagePipelineDefinition CreateDatabasePipelineDefinition() + { + var stages = _pipeline.Stages.Select(s => (BsonDocument)s.Render()); + return new BsonDocumentStagePipelineDefinition(stages, (IBsonSerializer)_pipeline.OutputSerializer); + } } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/ExpressionToExecutableQueryTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/ExpressionToExecutableQueryTranslator.cs index 0cf5e7b6c3a..f42bc3b4e75 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/ExpressionToExecutableQueryTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/ExpressionToExecutableQueryTranslator.cs @@ -16,9 +16,7 @@ using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; -using MongoDB.Bson.Serialization; using MongoDB.Driver.Linq.Linq3Implementation.Misc; -using MongoDB.Driver.Linq.Linq3Implementation.Serializers.KnownSerializers; using MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToPipelineTranslators; namespace MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToExecutableQueryTranslators @@ -30,12 +28,11 @@ public static ExecutableQuery> Translate.Instance); } @@ -44,7 +41,7 @@ public static ExecutableQuery TranslateScalar Translate(MongoQuer if (method.IsOneOf(__firstWithPredicateMethods)) { var predicateLambda = ExpressionHelper.UnquoteLambda(arguments[1]); - var predicateFilter = ExpressionToFilterTranslator.TranslateLambda(context, predicateLambda, parameterSerializer: pipeline.OutputSerializer); + var predicateFilter = ExpressionToFilterTranslator.TranslateLambda(context, predicateLambda, parameterSerializer: pipeline.OutputSerializer, asRoot: true); pipeline = pipeline.AddStages( pipeline.OutputSerializer, @@ -84,8 +84,7 @@ public static ExecutableQuery Translate(MongoQuer var finalizer = method.Name == "FirstOrDefault" ? __firstOrDefaultFinalizer : __firstFinalizer; return ExecutableQuery.Create( - provider.Collection, - provider.Options, + provider, pipeline, finalizer); } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/LastMethodToExecutableQueryTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/LastMethodToExecutableQueryTranslator.cs index 82c8a12d245..42b65bd2203 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/LastMethodToExecutableQueryTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/LastMethodToExecutableQueryTranslator.cs @@ -66,7 +66,7 @@ public static ExecutableQuery Translate(MongoQuer if (method.IsOneOf(__lastWithPredicateMethods)) { var predicateLambda = ExpressionHelper.UnquoteLambda(arguments[1]); - var predicateFilter = ExpressionToFilterTranslator.TranslateLambda(context, predicateLambda, parameterSerializer: pipeline.OutputSerializer); + var predicateFilter = ExpressionToFilterTranslator.TranslateLambda(context, predicateLambda, parameterSerializer: pipeline.OutputSerializer, asRoot: true); pipeline = pipeline.AddStages( pipeline.OutputSerializer, @@ -82,8 +82,7 @@ public static ExecutableQuery Translate(MongoQuer var finalizer = method.Name == "LastOrDefault" ? __singleOrDefaultFinalizer : __singleFinalizer; return ExecutableQuery.Create( - provider.Collection, - provider.Options, + provider, pipeline, finalizer); } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/LongCountMethodToExecutableQueryTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/LongCountMethodToExecutableQueryTranslator.cs index 1d5a8fca6f1..0de239b9e5c 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/LongCountMethodToExecutableQueryTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/LongCountMethodToExecutableQueryTranslator.cs @@ -67,7 +67,7 @@ public static ExecutableQuery Translate(MongoQueryPr if (method.IsOneOf(__longCountWithPredicateMethods)) { var predicateLambda = ExpressionHelper.UnquoteLambda(arguments[1]); - var predicateFilter = ExpressionToFilterTranslator.TranslateLambda(context, predicateLambda, parameterSerializer: pipeline.OutputSerializer); + var predicateFilter = ExpressionToFilterTranslator.TranslateLambda(context, predicateLambda, parameterSerializer: pipeline.OutputSerializer, asRoot: true); pipeline = pipeline.AddStages( pipeline.OutputSerializer, @@ -79,8 +79,7 @@ public static ExecutableQuery Translate(MongoQueryPr AstStage.Count("_v")); return ExecutableQuery.Create( - provider.Collection, - provider.Options, + provider, pipeline, _finalizer); } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/MaxMethodToExecutableQueryTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/MaxMethodToExecutableQueryTranslator.cs index a8c8679b0e5..99f5ab20975 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/MaxMethodToExecutableQueryTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/MaxMethodToExecutableQueryTranslator.cs @@ -97,8 +97,7 @@ public static ExecutableQuery Translate(MongoQuer AstStage.ReplaceRoot(AstExpression.GetField(root, "_max"))); return ExecutableQuery.Create( - provider.Collection, - provider.Options, + provider, pipeline, __finalizer); } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/MinMethodToExecutableQueryTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/MinMethodToExecutableQueryTranslator.cs index 14f54766c69..8b9ca579129 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/MinMethodToExecutableQueryTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/MinMethodToExecutableQueryTranslator.cs @@ -97,8 +97,7 @@ public static ExecutableQuery Translate(MongoQuer AstStage.ReplaceRoot(AstExpression.GetField(root, "_min"))); return ExecutableQuery.Create( - provider.Collection, - provider.Options, + provider, pipeline, __finalizer); } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/SingleMethodToExecutableQueryTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/SingleMethodToExecutableQueryTranslator.cs index f15dca212f8..f432b98435a 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/SingleMethodToExecutableQueryTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/SingleMethodToExecutableQueryTranslator.cs @@ -79,7 +79,7 @@ public static ExecutableQuery Translate(MongoQuer if (method.IsOneOf(__singleWithPredicateMethods)) { var predicateLambda = ExpressionHelper.UnquoteLambda(arguments[1]); - var predicateFilter = ExpressionToFilterTranslator.TranslateLambda(context, predicateLambda, parameterSerializer: pipeline.OutputSerializer); + var predicateFilter = ExpressionToFilterTranslator.TranslateLambda(context, predicateLambda, parameterSerializer: pipeline.OutputSerializer, asRoot: true); pipeline = pipeline.AddStages( pipeline.OutputSerializer, @@ -93,8 +93,7 @@ public static ExecutableQuery Translate(MongoQuer var finalizer = method.IsOneOf(__singleOrDefaultMethods) ? __singleOrDefaultFinalizer : __singleFinalizer; return ExecutableQuery.Create( - provider.Collection, - provider.Options, + provider, pipeline, finalizer); } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/StandardDeviationMethodToExecutableQueryTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/StandardDeviationMethodToExecutableQueryTranslator.cs index 095432802c6..843e38907f1 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/StandardDeviationMethodToExecutableQueryTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/StandardDeviationMethodToExecutableQueryTranslator.cs @@ -297,8 +297,7 @@ public static ExecutableQuery Translate(MongoQuer var finalizer = method.IsOneOf(__standardDeviationNullableMethods) ? __singleOrDefaultFinalizer : __singleFinalizer; return ExecutableQuery.Create( - provider.Collection, - provider.Options, + provider, pipeline, finalizer); } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/SumMethodToExecutableQueryTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/SumMethodToExecutableQueryTranslator.cs index 7c929864a27..ecd3d5094ce 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/SumMethodToExecutableQueryTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/SumMethodToExecutableQueryTranslator.cs @@ -146,8 +146,7 @@ public static ExecutableQuery Translate(MongoQuer AstStage.Project(AstProject.ExcludeId())); return ExecutableQuery.Create( - provider.Collection, - provider.Options, + provider, pipeline, __finalizer); } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ExpressionToFilterTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ExpressionToFilterTranslator.cs index 288e9b9dab4..b878bcb7fc8 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ExpressionToFilterTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ExpressionToFilterTranslator.cs @@ -13,11 +13,11 @@ * limitations under the License. */ +using System; using System.Linq; using System.Linq.Expressions; using MongoDB.Bson.Serialization; using MongoDB.Driver.Linq.Linq3Implementation.Ast.Filters; -using MongoDB.Driver.Linq.Linq3Implementation.Misc; using MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators; using MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToFilterTranslators.ExpressionTranslators; using MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToFilterTranslators.MethodTranslators; @@ -48,10 +48,23 @@ public static AstFilter Translate(TranslationContext context, Expression express return filter; } - public static AstFilter TranslateLambda(TranslationContext context, LambdaExpression lambdaExpression, IBsonSerializer parameterSerializer) + public static AstFilter TranslateLambda( + TranslationContext context, + LambdaExpression lambdaExpression, + IBsonSerializer parameterSerializer, + bool asRoot = false) { var parameterExpression = lambdaExpression.Parameters.Single(); - var parameterSymbol = context.CreateSymbol(parameterExpression, parameterSerializer, isCurrent: true); + if (parameterSerializer.ValueType != parameterExpression.Type) + { + throw new ArgumentException($"ValueType '{parameterSerializer.ValueType.FullName}' of parameterSerializer does not match parameter type '{parameterExpression.Type.FullName}'.", nameof(parameterSerializer)); + } + + var parameterSymbol = + asRoot ? + context.CreateSymbolWithVarName(parameterExpression, varName: "ROOT", parameterSerializer, isCurrent: true) : + context.CreateSymbol(parameterExpression, parameterSerializer, isCurrent: true); + var lambdaContext = context.WithSymbol(parameterSymbol); return Translate(lambdaContext, lambdaExpression.Body); } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ExpressionTranslators/MemberExpressionToFilterTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ExpressionTranslators/MemberExpressionToFilterTranslator.cs index 2caf0c0f492..eb9910eda3f 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ExpressionTranslators/MemberExpressionToFilterTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ExpressionTranslators/MemberExpressionToFilterTranslator.cs @@ -29,6 +29,15 @@ public static AstFilter Translate(TranslationContext context, MemberExpression e { var memberInfo = expression.Member; + if (memberInfo is FieldInfo fieldInfo) + { + if (fieldInfo.FieldType == typeof(bool)) + { + var field = ExpressionToFilterFieldTranslator.Translate(context, expression); + return AstFilter.Eq(field, true); + } + } + if (memberInfo is PropertyInfo propertyInfo) { if (propertyInfo.Is(NullableProperty.HasValue)) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ToFilterFieldTranslators/ArrayIndexExpressionToFilterFieldTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ToFilterFieldTranslators/ArrayIndexExpressionToFilterFieldTranslator.cs index 5e970092b72..57e81610094 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ToFilterFieldTranslators/ArrayIndexExpressionToFilterFieldTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ToFilterFieldTranslators/ArrayIndexExpressionToFilterFieldTranslator.cs @@ -14,6 +14,7 @@ */ using System.Linq.Expressions; +using MongoDB.Bson.Serialization; using MongoDB.Driver.Linq.Linq3Implementation.Ast.Filters; using MongoDB.Driver.Linq.Linq3Implementation.ExtensionMethods; using MongoDB.Driver.Linq.Linq3Implementation.Misc; @@ -27,25 +28,31 @@ public static AstFilterField Translate(TranslationContext context, BinaryExpress if (expression.NodeType == ExpressionType.ArrayIndex) { var arrayExpression = expression.Left; - var arrayField = ExpressionToFilterFieldTranslator.Translate(context, arrayExpression); var indexExpression = expression.Right; - var index = indexExpression.GetConstantValue(containingExpression: expression); - if (index < 0) + return Translate(context, expression, fieldExpression: arrayExpression, indexExpression); + } + + throw new ExpressionNotSupportedException(expression); + } + + public static AstFilterField Translate(TranslationContext context, Expression expression, Expression fieldExpression, Expression indexExpression) + { + var field = ExpressionToFilterFieldTranslator.TranslateEnumerable(context, fieldExpression); + var index = indexExpression.GetConstantValue(containingExpression: expression); + var itemSerializer = ArraySerializerHelper.GetItemSerializer(fieldExpression, field.Serializer); + + if (index < 0) + { + var reason = "negative indexes are not valid"; + if (index == -1) { - var reason = "negative indexes are not valid"; - if (index == -1) - { - reason += ". To use the positional operator $ use FirstMatchingElement instead of an index value of -1"; // closing period is added by exception - } - throw new ExpressionNotSupportedException(expression, because: reason); + reason += ". To use the positional operator $ use FirstMatchingElement instead of an index value of -1"; // closing period is added by exception } - - var itemSerializer = ArraySerializerHelper.GetItemSerializer(arrayField.Serializer); - return arrayField.SubField(index.ToString(), itemSerializer); + throw new ExpressionNotSupportedException(expression, because: reason); } - throw new ExpressionNotSupportedException(expression); + return field.SubField(index.ToString(), itemSerializer); } } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ToFilterFieldTranslators/ConvertExpressionToFilterFieldTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ToFilterFieldTranslators/ConvertExpressionToFilterFieldTranslator.cs index f99b0a63d4c..9e0f01d9010 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ToFilterFieldTranslators/ConvertExpressionToFilterFieldTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ToFilterFieldTranslators/ConvertExpressionToFilterFieldTranslator.cs @@ -28,96 +28,61 @@ internal static class ConvertExpressionToFilterFieldTranslator { public static AstFilterField Translate(TranslationContext context, UnaryExpression expression) { - if (expression.NodeType == ExpressionType.Convert) + if (expression.NodeType == ExpressionType.Convert || expression.NodeType == ExpressionType.TypeAs) { var field = ExpressionToFilterFieldTranslator.Translate(context, expression.Operand); - var fieldSerializer = field.Serializer; - var fieldType = fieldSerializer.ValueType; + var fieldType = field.Serializer.ValueType; var targetType = expression.Type; - if (fieldType.IsEnum()) + if (IsConvertEnumToUnderlyingType(fieldType, targetType)) { - var enumType = fieldType; - var enumUnderlyingType = enumType.GetEnumUnderlyingType(); - if (targetType == enumUnderlyingType) - { - var enumUnderlyingTypeSerializer = EnumUnderlyingTypeSerializer.Create(fieldSerializer); - return AstFilter.Field(field.Path, enumUnderlyingTypeSerializer); - } + return TranslateConvertEnumToUnderlyingType(field, targetType); } - if (IsNumericType(targetType)) + if (IsNumericConversion(fieldType, targetType)) { - IBsonSerializer targetTypeSerializer = expression.Type switch - { - Type t when t == typeof(byte) => new ByteSerializer(), - Type t when t == typeof(short) => new Int16Serializer(), - Type t when t == typeof(ushort) => new UInt16Serializer(), - Type t when t == typeof(int) => new Int32Serializer(), - Type t when t == typeof(uint) => new UInt32Serializer(), - Type t when t == typeof(long) => new Int64Serializer(), - Type t when t == typeof(ulong) => new UInt64Serializer(), - Type t when t == typeof(float) => new SingleSerializer(), - Type t when t == typeof(double) => new DoubleSerializer(), - Type t when t == typeof(decimal) => new DecimalSerializer(), - _ => throw new ExpressionNotSupportedException(expression) - }; - if (fieldSerializer is IRepresentationConfigurable representationConfigurableFieldSerializer && - targetTypeSerializer is IRepresentationConfigurable representationConfigurableTargetTypeSerializer) - { - var fieldRepresentation = representationConfigurableFieldSerializer.Representation; - if (fieldRepresentation == BsonType.String) - { - targetTypeSerializer = representationConfigurableTargetTypeSerializer.WithRepresentation(fieldRepresentation); - } - } - if (fieldSerializer is IRepresentationConverterConfigurable converterConfigurableFieldSerializer && - targetTypeSerializer is IRepresentationConverterConfigurable converterConfigurableTargetTypeSerializer) - { - targetTypeSerializer = converterConfigurableTargetTypeSerializer.WithConverter(converterConfigurableFieldSerializer.Converter); - } - return AstFilter.Field(field.Path, targetTypeSerializer); + return TranslateNumericConversion(field, targetType); } - if (targetType.IsConstructedGenericType && - targetType.GetGenericTypeDefinition() == typeof(Nullable<>)) + if (IsConvertToNullable(fieldType, targetType)) { - var nullableValueType = targetType.GetGenericArguments()[0]; - if (nullableValueType == fieldType) - { - var nullableSerializerType = typeof(NullableSerializer<>).MakeGenericType(nullableValueType); - var nullableSerializer = (IBsonSerializer)Activator.CreateInstance(nullableSerializerType, fieldSerializer); - return AstFilter.Field(field.Path, nullableSerializer); - } - - if (fieldType.IsConstructedGenericType && - fieldType.GetGenericTypeDefinition() == typeof(Nullable<>)) - { - var fieldValueType = fieldType.GetGenericArguments()[0]; - if (fieldValueType.IsEnum()) - { - var enumUnderlyingType = fieldValueType.GetEnumUnderlyingType(); - if (nullableValueType == enumUnderlyingType) - { - var fieldSerializerType = fieldSerializer.GetType(); - if (fieldSerializerType.IsConstructedGenericType && - fieldSerializerType.GetGenericTypeDefinition() == typeof(NullableSerializer<>)) - { - var enumSerializer = ((IChildSerializerConfigurable)fieldSerializer).ChildSerializer; - var enumUnderlyingTypeSerializer = EnumUnderlyingTypeSerializer.Create(enumSerializer); - var nullableSerializerType = typeof(NullableSerializer<>).MakeGenericType(nullableValueType); - var nullableSerializer = (IBsonSerializer)Activator.CreateInstance(nullableSerializerType, enumUnderlyingTypeSerializer); - return AstFilter.Field(field.Path, nullableSerializer); - } - } - } - } + return TranslateConvertToNullable(field); + } + + if (IsConvertToDerivedType(fieldType, targetType)) + { + return TranslateConvertToDerivedType(field, targetType); } } throw new ExpressionNotSupportedException(expression); } + private static bool IsConvertEnumToUnderlyingType(Type fieldType, Type targetType) + { + return + fieldType.IsEnumOrNullableEnum(out _, out var underlyingType) && + targetType.IsSameAsOrNullableOf(underlyingType); + } + + private static bool IsConvertToDerivedType(Type fieldType, Type targetType) + { + return targetType.IsSubclassOf(fieldType); + } + + private static bool IsConvertToNullable(Type fieldType, Type targetType) + { + return + targetType.IsConstructedGenericType && + targetType.GetGenericTypeDefinition() == typeof(Nullable<>) && + targetType.GetGenericArguments()[0] == fieldType; + } + + private static bool IsNumericConversion(Type fieldType, Type targetType) + { + return IsNumericType(fieldType) && IsNumericType(targetType); + } + private static bool IsNumericType(Type type) { switch (Type.GetTypeCode(type)) @@ -128,6 +93,7 @@ private static bool IsNumericType(Type type) case TypeCode.Int16: case TypeCode.Int32: case TypeCode.Int64: + case TypeCode.SByte: case TypeCode.Single: case TypeCode.UInt16: case TypeCode.UInt32: @@ -138,5 +104,81 @@ private static bool IsNumericType(Type type) return false; } } + + private static AstFilterField TranslateConvertEnumToUnderlyingType(AstFilterField field, Type targetType) + { + var fieldSerializer = field.Serializer; + var fieldType = fieldSerializer.ValueType; + + IBsonSerializer enumSerializer; + if (fieldType.IsNullable()) + { + var nullableSerializer = (INullableSerializer)fieldSerializer; + enumSerializer = nullableSerializer.ValueSerializer; + } + else + { + enumSerializer = fieldSerializer; + } + + IBsonSerializer targetSerializer; + var enumUnderlyingTypeSerializer = EnumUnderlyingTypeSerializer.Create(enumSerializer); + if (targetType.IsNullable()) + { + targetSerializer = NullableSerializer.Create(enumUnderlyingTypeSerializer); + } + else + { + targetSerializer = enumUnderlyingTypeSerializer; + } + + return AstFilter.Field(field.Path, targetSerializer); + } + + private static AstFilterField TranslateConvertToDerivedType(AstFilterField field, Type targetType) + { + var targetSerializer = BsonSerializer.LookupSerializer(targetType); + return AstFilter.Field(field.Path, targetSerializer); + } + + private static AstFilterField TranslateConvertToNullable(AstFilterField field) + { + var nullableSerializer = NullableSerializer.Create(field.Serializer); + return AstFilter.Field(field.Path, nullableSerializer); + } + + private static AstFilterField TranslateNumericConversion(AstFilterField field, Type targetType) + { + IBsonSerializer targetTypeSerializer = targetType switch + { + Type t when t == typeof(byte) => new ByteSerializer(), + Type t when t == typeof(sbyte) => new SByteSerializer(), + Type t when t == typeof(short) => new Int16Serializer(), + Type t when t == typeof(ushort) => new UInt16Serializer(), + Type t when t == typeof(int) => new Int32Serializer(), + Type t when t == typeof(uint) => new UInt32Serializer(), + Type t when t == typeof(long) => new Int64Serializer(), + Type t when t == typeof(ulong) => new UInt64Serializer(), + Type t when t == typeof(float) => new SingleSerializer(), + Type t when t == typeof(double) => new DoubleSerializer(), + Type t when t == typeof(decimal) => new DecimalSerializer(), + _ => throw new Exception($"Unexpected target type: {targetType}.") + }; + if (field.Serializer is IRepresentationConfigurable representationConfigurableFieldSerializer && + targetTypeSerializer is IRepresentationConfigurable representationConfigurableTargetTypeSerializer) + { + var fieldRepresentation = representationConfigurableFieldSerializer.Representation; + if (fieldRepresentation == BsonType.String) + { + targetTypeSerializer = representationConfigurableTargetTypeSerializer.WithRepresentation(fieldRepresentation); + } + } + if (field.Serializer is IRepresentationConverterConfigurable converterConfigurableFieldSerializer && + targetTypeSerializer is IRepresentationConverterConfigurable converterConfigurableTargetTypeSerializer) + { + targetTypeSerializer = converterConfigurableTargetTypeSerializer.WithConverter(converterConfigurableFieldSerializer.Converter); + } + return AstFilter.Field(field.Path, targetTypeSerializer); + } } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ToFilterFieldTranslators/ElementAtMethodToFilterFieldTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ToFilterFieldTranslators/ElementAtMethodToFilterFieldTranslator.cs index a0440177b0b..caab7af0051 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ToFilterFieldTranslators/ElementAtMethodToFilterFieldTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ToFilterFieldTranslators/ElementAtMethodToFilterFieldTranslator.cs @@ -14,9 +14,7 @@ */ using System.Linq.Expressions; -using MongoDB.Bson.Serialization; using MongoDB.Driver.Linq.Linq3Implementation.Ast.Filters; -using MongoDB.Driver.Linq.Linq3Implementation.ExtensionMethods; using MongoDB.Driver.Linq.Linq3Implementation.Misc; using MongoDB.Driver.Linq.Linq3Implementation.Reflection; @@ -32,27 +30,9 @@ public static AstFilterField Translate(TranslationContext context, MethodCallExp if (method.Is(EnumerableMethod.ElementAt)) { var sourceExpression = arguments[0]; - var field = ExpressionToFilterFieldTranslator.Translate(context, sourceExpression); - var indexExpression = arguments[1]; - var index = indexExpression.GetConstantValue(containingExpression: expression); - - if (index < 0) - { - var reason = "negative indexes are not valid"; - if (index == -1) - { - reason += ". To use the positional operator $ use FirstMatchingElement instead of an index value of -1"; // closing period is added by exception - } - throw new ExpressionNotSupportedException(expression, because: reason); - } - if (field.Serializer is IBsonArraySerializer arraySerializer && - arraySerializer.TryGetItemSerializationInfo(out var itemSerializationInfo)) - { - var itemSerializer = itemSerializationInfo.Serializer; - return field.SubField(index.ToString(), itemSerializer); - } + return ArrayIndexExpressionToFilterFieldTranslator.Translate(context, expression, fieldExpression: sourceExpression, indexExpression); } throw new ExpressionNotSupportedException(expression); diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ToFilterFieldTranslators/ExpressionToFilterFieldTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ToFilterFieldTranslators/ExpressionToFilterFieldTranslator.cs index 49aa53eae54..1a2307520b8 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ToFilterFieldTranslators/ExpressionToFilterFieldTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ToFilterFieldTranslators/ExpressionToFilterFieldTranslator.cs @@ -30,7 +30,10 @@ public static AstFilterField Translate(TranslationContext context, Expression ex case ExpressionType.MemberAccess: return MemberExpressionToFilterFieldTranslator.Translate(context, (MemberExpression)expression); case ExpressionType.Call: return MethodCallExpressionToFilterFieldTranslator.Translate(context, (MethodCallExpression)expression); case ExpressionType.Parameter: return ParameterExpressionToFilterFieldTranslator.Translate(context, (ParameterExpression)expression); - case ExpressionType.Convert: return ConvertExpressionToFilterFieldTranslator.Translate(context, (UnaryExpression)expression); + + case ExpressionType.Convert: + case ExpressionType.TypeAs: + return ConvertExpressionToFilterFieldTranslator.Translate(context, (UnaryExpression)expression); } throw new ExpressionNotSupportedException(expression); 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 43382307655..1de2a6e41af 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ToFilterFieldTranslators/GetItemMethodToFilterFieldTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ToFilterFieldTranslators/GetItemMethodToFilterFieldTranslator.cs @@ -21,7 +21,6 @@ using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Linq.Linq3Implementation.Ast.Filters; using MongoDB.Driver.Linq.Linq3Implementation.ExtensionMethods; -using MongoDB.Driver.Linq.Linq3Implementation.Misc; namespace MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToFilterTranslators.ToFilterFieldTranslators { @@ -42,40 +41,22 @@ public static AstFilterField Translate(TranslationContext context, MethodCallExp if (indexExpression.Type == typeof(int)) { - var index = indexExpression.GetConstantValue(containingExpression: expression); - return TranslateWithIntIndex(context, expression, method, fieldExpression, index); + return ArrayIndexExpressionToFilterFieldTranslator.Translate(context, expression, fieldExpression, indexExpression); } if (indexExpression.Type == typeof(string)) { - var key = indexExpression.GetConstantValue(containingExpression: expression); - return TranslateWithStringIndex(context, expression, method, fieldExpression, key); + return TranslateWithStringIndex(context, expression, method, fieldExpression, indexExpression); } } throw new ExpressionNotSupportedException(expression); } - private static AstFilterField TranslateWithIntIndex(TranslationContext context, MethodCallExpression expression, MethodInfo method, Expression fieldExpression, int index) - { - var field = ExpressionToFilterFieldTranslator.TranslateEnumerable(context, fieldExpression); - - if (field.Serializer is IBsonArraySerializer arraySerializer && - arraySerializer.TryGetItemSerializationInfo(out var itemSerializationInfo)) - { - var itemSerializer = itemSerializationInfo.Serializer; - if (method.ReturnType.IsAssignableFrom(itemSerializer.ValueType)) - { - return field.SubField(index.ToString(), itemSerializer); - } - } - - throw new ExpressionNotSupportedException(expression); - } - - private static AstFilterField TranslateWithStringIndex(TranslationContext context, MethodCallExpression expression, MethodInfo method, Expression fieldExpression, string key) + private static AstFilterField TranslateWithStringIndex(TranslationContext context, MethodCallExpression expression, MethodInfo method, Expression fieldExpression, Expression indexExpression) { var field = ExpressionToFilterFieldTranslator.Translate(context, fieldExpression); + var key = indexExpression.GetConstantValue(containingExpression: expression); if (typeof(BsonValue).IsAssignableFrom(field.Serializer.ValueType)) { diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ToFilterFieldTranslators/MethodCallExpressionToFilterFieldTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ToFilterFieldTranslators/MethodCallExpressionToFilterFieldTranslator.cs index df5a5be7088..41ff3cea6e6 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ToFilterFieldTranslators/MethodCallExpressionToFilterFieldTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ToFilterFieldTranslators/MethodCallExpressionToFilterFieldTranslator.cs @@ -30,6 +30,7 @@ public static AstFilterField Translate(TranslationContext context, MethodCallExp case "First": return FirstMethodToFilterFieldTranslator.Translate(context, expression); case "FirstMatchingElement": return FirstMatchingElementMethodToFilterFieldTranslator.Translate(context, expression); case "get_Item": return GetItemMethodToFilterFieldTranslator.Translate(context, expression); + case "Select": return SelectMethodToFilterFieldTranslator.Translate(context, expression); } throw new ExpressionNotSupportedException(expression); diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ToFilterFieldTranslators/SelectMethodToFilterFieldTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ToFilterFieldTranslators/SelectMethodToFilterFieldTranslator.cs new file mode 100644 index 00000000000..aea26a17285 --- /dev/null +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ToFilterFieldTranslators/SelectMethodToFilterFieldTranslator.cs @@ -0,0 +1,60 @@ +/* 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.Linq; +using System.Linq.Expressions; +using MongoDB.Bson.Serialization; +using MongoDB.Driver.Linq.Linq3Implementation.Ast.Filters; +using MongoDB.Driver.Linq.Linq3Implementation.Misc; +using MongoDB.Driver.Linq.Linq3Implementation.Reflection; +using MongoDB.Driver.Linq.Linq3Implementation.Serializers; + +namespace MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToFilterTranslators.ToFilterFieldTranslators +{ + internal static class SelectMethodToFilterFieldTranslator + { + public static AstFilterField Translate(TranslationContext context, MethodCallExpression expression) + { + var method = expression.Method; + var arguments = expression.Arguments; + + if (method.Is(EnumerableMethod.Select)) + { + var sourceExpression = arguments[0]; + var sourceField = ExpressionToFilterFieldTranslator.Translate(context, sourceExpression); + + var selectorExpression = arguments[1]; + if (selectorExpression is LambdaExpression lambdaExpression && + lambdaExpression.Body is MemberExpression memberExpression && + memberExpression.Expression == lambdaExpression.Parameters.Single()) + { + var itemSerializer = ArraySerializerHelper.GetItemSerializer(sourceField.Serializer); + if (itemSerializer is IBsonDocumentSerializer documentSerializer) + { + var memberName = memberExpression.Member.Name; + if (documentSerializer.TryGetMemberSerializationInfo(memberName, out var memberSerializationInfo)) + { + var subFieldName = memberSerializationInfo.ElementName; + var subFieldSerializer = IEnumerableSerializer.Create(memberSerializationInfo.Serializer); + return sourceField.SubField(subFieldName, subFieldSerializer); + } + } + } + } + + throw new ExpressionNotSupportedException(expression); + } + } +} diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/DocumentsMethodToPipelineTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/DocumentsMethodToPipelineTranslator.cs new file mode 100644 index 00000000000..03506a5d613 --- /dev/null +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/DocumentsMethodToPipelineTranslator.cs @@ -0,0 +1,65 @@ +/* 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.Collections; +using System.Linq.Expressions; +using MongoDB.Bson.Serialization; +using MongoDB.Driver.Linq.Linq3Implementation.Ast; +using MongoDB.Driver.Linq.Linq3Implementation.Ast.Stages; +using MongoDB.Driver.Linq.Linq3Implementation.ExtensionMethods; +using MongoDB.Driver.Linq.Linq3Implementation.Misc; +using MongoDB.Driver.Linq.Linq3Implementation.Reflection; + +namespace MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToPipelineTranslators +{ + internal static class DocumentsMethodToPipelineTranslator + { + // public static methods + public static AstPipeline Translate(TranslationContext context, MethodCallExpression expression) + { + var method = expression.Method; + var arguments = expression.Arguments; + + if (method.IsOneOf(MongoQueryableMethod.Documents, MongoQueryableMethod.DocumentsWithSerializer)) + { + var sourceExpression = ConvertHelper.RemoveConvertToMongoQueryable(arguments[0]); + var pipeline = ExpressionToPipelineTranslator.Translate(context, sourceExpression); + + var documentsExpression = arguments[1]; + var documents = documentsExpression.GetConstantValue(expression); + + IBsonSerializer documentSerializer; + if (method.Is(MongoQueryableMethod.DocumentsWithSerializer)) + { + var documentSerializerExpression = arguments[2]; + documentSerializer = documentSerializerExpression.GetConstantValue(expression); + } + else + { + var documentType = method.GetGenericArguments()[0]; + documentSerializer = BsonSerializer.LookupSerializer(documentType); + } + + var serializedDocuments = SerializationHelper.SerializeValues(documentSerializer, documents); + var documentsStage = AstStage.Documents(serializedDocuments); + pipeline = pipeline.AddStages(documentSerializer, documentsStage); + + return pipeline; + } + + throw new ExpressionNotSupportedException(expression); + } + } +} diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/ExpressionToPipelineTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/ExpressionToPipelineTranslator.cs index 8ec544f2da5..2857fc37c81 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/ExpressionToPipelineTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/ExpressionToPipelineTranslator.cs @@ -28,7 +28,7 @@ public static AstPipeline Translate(TranslationContext context, Expression expre { var query = (IQueryable)((ConstantExpression)expression).Value; var provider = (IMongoQueryProvider)query.Provider; - return AstPipeline.Empty(provider.CollectionDocumentSerializer); + return AstPipeline.Empty(provider.PipelineInputSerializer); } var methodCallExpression = (MethodCallExpression)expression; @@ -40,6 +40,8 @@ public static AstPipeline Translate(TranslationContext context, Expression expre return DensifyMethodToPipelineTranslator.Translate(context, methodCallExpression); case "Distinct": return DistinctMethodToPipelineTranslator.Translate(context, methodCallExpression); + case "Documents": + return DocumentsMethodToPipelineTranslator.Translate(context, methodCallExpression); case "GroupBy": return GroupByMethodToPipelineTranslator.Translate(context, methodCallExpression); case "GroupJoin": diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/OrderByMethodToPipelineTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/OrderByMethodToPipelineTranslator.cs index f1cc5403592..6e35cdee169 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/OrderByMethodToPipelineTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/OrderByMethodToPipelineTranslator.cs @@ -16,7 +16,6 @@ using System; using System.Linq; using System.Linq.Expressions; -using MongoDB.Bson.Serialization; using MongoDB.Driver.Linq.Linq3Implementation.Ast; using MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions; using MongoDB.Driver.Linq.Linq3Implementation.Ast.Stages; @@ -40,19 +39,19 @@ public static AstPipeline Translate(TranslationContext context, MethodCallExpres var pipeline = ExpressionToPipelineTranslator.Translate(context, sourceExpression); var keySelectorLambda = ExpressionHelper.UnquoteLambda(arguments[1]); - var sortField = CreateSortField(context, method.Name, keySelectorLambda, parameterSerializer: pipeline.OutputSerializer); + var keySelectorTranslation = ExpressionToAggregationExpressionTranslator.TranslateLambdaBody(context, keySelectorLambda, pipeline.OutputSerializer, asRoot: true); + + var newSortStages = CreateSortStages(method.Name, keySelectorTranslation); switch (method.Name) { case "OrderBy": case "OrderByDescending": - pipeline = pipeline.AddStages(pipeline.OutputSerializer, AstStage.Sort(sortField)); + pipeline = AppendSortStages(pipeline, newSortStages); break; case "ThenBy": case "ThenByDescending": - var oldSortStage = (AstSortStage)pipeline.Stages.Last(); - var newSortStage = oldSortStage.AddSortField(sortField); - pipeline = pipeline.ReplaceLastStage(pipeline.OutputSerializer, newSortStage); + pipeline = CombineSortStages(pipeline, newSortStages); break; } @@ -63,20 +62,200 @@ public static AstPipeline Translate(TranslationContext context, MethodCallExpres } // private static methods - private static AstSortField CreateSortField(TranslationContext context, string methodName, LambdaExpression keySelector, IBsonSerializer parameterSerializer) + private static AstPipeline AppendSortStages(AstPipeline pipeline, AstStage[] newSortStages) + { + return pipeline.AddStages(pipeline.OutputSerializer, newSortStages); + } + + private static AstPipeline CombineSortStages(AstPipeline pipeline, AstStage[] newSortStages) { - var fieldPath = keySelector.GetFieldPath(context, parameterSerializer); - switch (methodName) + var oldSortStages = FindOldSortStages(pipeline); + + // 1 stage => $sort + // 3 stages => $project/$sort/$replaceRoot + return (oldSortStages.Length, newSortStages.Length) switch + { + (1, 1) => Combine1And1(pipeline, oldSortStages, newSortStages), + (1, 3) => Combine1And3(pipeline, oldSortStages, newSortStages), + (3, 1) => Combine3And1(pipeline, oldSortStages, newSortStages), + (3, 3) => Combine3And3(pipeline, oldSortStages, newSortStages), + _ => throw new Exception("Unexpected number of old and new sort stages.") + }; + + static AstStage[] FindOldSortStages(AstPipeline pipeline) + { + var stages = pipeline.Stages; + var count = stages.Count; + + if (count >= 1) + { + if (stages.Last() is AstSortStage oldSortStage) + { + return new AstStage[] { oldSortStage }; + } + } + + if (count >= 3) + { + if (stages[count - 3] is AstProjectStage oldProjectStage && + stages[count - 2] is AstSortStage oldSortStage && + stages[count - 1] is AstReplaceRootStage oldReplaceRootStage) + { + return new AstStage[] { oldProjectStage, oldSortStage, oldReplaceRootStage }; + } + } + + throw new Exception("Unexpected failure to find old sort stages."); + } + + static AstPipeline Combine1And1(AstPipeline pipeline, AstStage[] oldSortStages, AstStage[] newSortStages) + { + // old: + // { $sort : { f1 : d1, ..., fj : dj } } + // new: + // { $sort : { fj+1 : dj+1 } } + // combined: + // { $sort : { f1 : d1, ..., fj : dj, fj+1 : dj+1 } } + + var oldSortStage = (AstSortStage)oldSortStages.Single(); + var newSortStage = (AstSortStage)newSortStages.Single(); + var combinedSortStage = oldSortStage.AddSortField(newSortStage.Fields.Single()); + + return pipeline.ReplaceLastStage(pipeline.OutputSerializer, combinedSortStage); + } + + static AstPipeline Combine1And3(AstPipeline pipeline, AstStage[] oldSortStages, AstStage[] newSortStages) { - case "OrderBy": - case "ThenBy": - return AstSort.Field(fieldPath, AstSortOrder.Ascending); - case "OrderByDescending": - case "ThenByDescending": - return AstSort.Field(fieldPath, AstSortOrder.Descending); - default: - throw new ArgumentException("Unexpected method name.", nameof(methodName)); + // old: + // { $sort : { f1 : d1, ..., fj : dj } } + // new: + // { $project : { _id : 0, _document : "$$ROOT", _key1 : expr1 } } + // { $sort : { _key1 : dj+1 } } + // { $replaceRoot : { newRoot : "$_document" } } + // combined: + // { $project : { _id : 0, _document : "$$ROOT", _key1 : expr1 } } + // { $sort : { '_document.f1' : d1, ..., '_document.fj' : dj, _key1 : dj+1 } } + // { $replaceRoot : { newRoot : "$_document" } } + + var oldSortStage = (AstSortStage)oldSortStages.Single(); + var newProjectStage = (AstProjectStage)newSortStages[0]; + var newSortStage = (AstSortStage)newSortStages[1]; + var newReplaceRootStage = (AstReplaceRootStage)newSortStages[2]; + var combinedSortStage = AstStage.Sort( + oldSortStage.Fields.Select(f => AstSort.Field("_document." + f.Path, f.Order)) + .Append(newSortStage.Fields.Single())); + + return pipeline.ReplaceStagesAtEnd(pipeline.OutputSerializer, numberOfStagesToReplace: 1, newProjectStage, combinedSortStage, newReplaceRootStage); + } + + static AstPipeline Combine3And1(AstPipeline pipeline, AstStage[] oldSortStages, AstStage[] newSortStages) + { + // old: + // { $project : { _id : 0, _document : "$$ROOT", _key1 : expr1, ..., _keyj : exprj } } + // { $sort : { f1 : d1, ..., fk : dk } } + // { $replaceRoot : { newRoot : "$_document" } } + // new: + // { $sort : { fk+1 : dk+1 } } + // combined: + // { $project : { _id : 0, _document : "$$ROOT", _key1 : expr1, ..., _keyj : exprj } } + // { $sort : { f1 : d1, ..., fk : dk, '_document.fk+1' : dk+1 } } + // { $replaceRoot : { newRoot : "$_document" } } + + var oldProjectStage = (AstProjectStage)oldSortStages[0]; + var oldSortStage = (AstSortStage)oldSortStages[1]; + var oldReplaceRootStage = oldSortStages[2]; + var newSortStage = (AstSortStage)newSortStages.Single(); + var combinedSortStage = AstStage.Sort( + oldSortStage.Fields + .Append(newSortStage.Fields.Select(f => AstSort.Field("_document." + f.Path, f.Order)).Single())); + + return pipeline.ReplaceStagesAtEnd(pipeline.OutputSerializer, numberOfStagesToReplace: 3, oldProjectStage, combinedSortStage, oldReplaceRootStage); } + + static AstPipeline Combine3And3(AstPipeline pipeline, AstStage[] oldSortStages, AstStage[] newSortStages) + { + // old: + // { $project : { _id : 0, _document : "$$ROOT", _key1 : oldExpr1, ..., _keyj : oldExprj } } + // { $sort : { f1 : d1, ..., fk : dk } } + // { $replaceRoot : { newRoot : "$_document" } } + // new: + // { $project : { _id : 0, _document : "$$ROOT", _key1 : newExpr } } + // { $sort : { _key1 : dnew } } + // { $replaceRoot : { newRoot : "$_document" } } + // combined: + // { $project : { _id : 0, _document : "$$ROOT", _key1 : oldExpr1, ..., _keyj : oldExprj, _keyj+1 : newExpr } } + // { $sort : { f1 : d1, ..., fk : dk, _keyj+1 : dnew } } + // { $replaceRoot : { newRoot : "$_document" } } + + var oldProjectStage = (AstProjectStage)oldSortStages[0]; + var oldSortStage = (AstSortStage)oldSortStages[1]; + var oldReplaceRootStage = (AstReplaceRootStage)oldSortStages[2]; + var newProjectStage = (AstProjectStage)newSortStages[0]; + var newSortStage = (AstSortStage)newSortStages[1]; + + var j = oldProjectStage.Specifications.Count - 2; + var newKey = $"_key{j + 1}"; + var newExpr = ((AstProjectStageSetFieldSpecification)newProjectStage.Specifications[2]).Value; + var dNew = newSortStage.Fields.Single().Order; + + var combinedProjectStage = AstStage.Project( + oldProjectStage.Specifications + .Append(AstProject.Set(newKey, newExpr))); + var combinedSortStage = AstStage.Sort( + oldSortStage.Fields + .Append(AstSort.Field(newKey, dNew))); + + return pipeline.ReplaceStagesAtEnd(pipeline.OutputSerializer, numberOfStagesToReplace: 3, combinedProjectStage, combinedSortStage, oldReplaceRootStage); + } + } + + private static AstStage[] CreateSortStages(string methodName, AggregationExpression keySelectorTranslation) + { + var sortOrder = ToSortOrder(methodName); + + if (TryConvertKeySelectorTranslationToFieldPath(keySelectorTranslation, out var path)) + { + var sortField = AstSort.Field(path, sortOrder); + var sortStage = AstStage.Sort(sortField); + return new[] { sortStage }; + } + else + { + var projectStage = AstStage.Project( + AstProject.Exclude("_id"), + AstProject.Set("_document", AstExpression.FieldPath("$$ROOT")), + AstProject.Set("_key1", keySelectorTranslation.Ast)); + var sortStage = AstStage.Sort(AstSort.Field("_key1", sortOrder)); + var replaceRootStage = AstStage.ReplaceRoot(AstExpression.FieldPath("$_document")); + return new[] { projectStage, sortStage, replaceRootStage }; + } + } + + private static AstSortOrder ToSortOrder(string methodName) + { + return methodName switch + { + "OrderBy" or "ThenBy" => AstSortOrder.Ascending, + "OrderByDescending" or "ThenByDescending" => AstSortOrder.Descending, + _ => throw new ArgumentException($"Unexpected method name: {methodName}.", nameof(methodName)) + }; + } + + private static bool TryConvertKeySelectorTranslationToFieldPath(AggregationExpression keySelectorTranslation, out string path) + { + if (keySelectorTranslation.Ast is AstGetFieldExpression getFieldExpression && + getFieldExpression.CanBeConvertedToFieldPath()) + { + path = getFieldExpression.ConvertToFieldPath(); + if (!path.StartsWith("$$")) + { + path = path.Substring(1); + return true; + } + } + + path = null; + return false; } } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/SkipMethodToPipelineTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/SkipMethodToPipelineTranslator.cs index 0449a1d0ef3..baf3662ac0c 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/SkipMethodToPipelineTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/SkipMethodToPipelineTranslator.cs @@ -30,13 +30,19 @@ public static AstPipeline Translate(TranslationContext context, MethodCallExpres var method = expression.Method; var arguments = expression.Arguments; - if (method.Is(QueryableMethod.Skip)) + if (method.IsOneOf(QueryableMethod.Skip, MongoQueryableMethod.SkipWithLong)) { var sourceExpression = arguments[0]; + if (method.Is(MongoQueryableMethod.SkipWithLong)) + { + sourceExpression = ConvertHelper.RemoveConvertToMongoQueryable(arguments[0]); + } var pipeline = ExpressionToPipelineTranslator.Translate(context, sourceExpression); var countExpression = arguments[1]; - var count = countExpression.GetConstantValue(containingExpression: expression); + var count = method.Is(MongoQueryableMethod.SkipWithLong) ? + countExpression.GetConstantValue(containingExpression: expression) : + countExpression.GetConstantValue(containingExpression: expression); pipeline = pipeline.AddStages( pipeline.OutputSerializer, diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/TakeMethodToPipelineTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/TakeMethodToPipelineTranslator.cs index 5ddc32c7185..1fd99416e92 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/TakeMethodToPipelineTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/TakeMethodToPipelineTranslator.cs @@ -30,13 +30,19 @@ public static AstPipeline Translate(TranslationContext context, MethodCallExpres var method = expression.Method; var arguments = expression.Arguments; - if (method.Is(QueryableMethod.Take)) + if (method.IsOneOf(QueryableMethod.Take, MongoQueryableMethod.TakeWithLong)) { var sourceExpression = arguments[0]; + if (method.Is(MongoQueryableMethod.TakeWithLong)) + { + sourceExpression = ConvertHelper.RemoveConvertToMongoQueryable(arguments[0]); + } var pipeline = ExpressionToPipelineTranslator.Translate(context, sourceExpression); var countExpression = arguments[1]; - var count = countExpression.GetConstantValue(containingExpression: expression); + var count = method.Is(MongoQueryableMethod.TakeWithLong) ? + countExpression.GetConstantValue(containingExpression: expression) : + countExpression.GetConstantValue(containingExpression: expression); pipeline = pipeline.AddStages( pipeline.OutputSerializer, diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/WhereMethodToPipelineTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/WhereMethodToPipelineTranslator.cs index 1003fa89ada..c59cfe7ad4a 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/WhereMethodToPipelineTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/WhereMethodToPipelineTranslator.cs @@ -36,7 +36,7 @@ public static AstPipeline Translate(TranslationContext context, MethodCallExpres var pipeline = ExpressionToPipelineTranslator.Translate(context, sourceExpression); var predicateLambda = ExpressionHelper.UnquoteLambda(arguments[1]); - var predicateFilter = ExpressionToFilterTranslator.TranslateLambda(context, predicateLambda, parameterSerializer: pipeline.OutputSerializer); + var predicateFilter = ExpressionToFilterTranslator.TranslateLambda(context, predicateLambda, parameterSerializer: pipeline.OutputSerializer, asRoot: true); pipeline = pipeline.AddStages( pipeline.OutputSerializer, diff --git a/src/MongoDB.Driver/Linq/LinqProviderAdapter.cs b/src/MongoDB.Driver/Linq/LinqProviderAdapter.cs index 603e1a185ea..1901f19f51b 100644 --- a/src/MongoDB.Driver/Linq/LinqProviderAdapter.cs +++ b/src/MongoDB.Driver/Linq/LinqProviderAdapter.cs @@ -35,6 +35,11 @@ internal abstract IMongoQueryable AsQueryable( IClientSessionHandle session, AggregateOptions options); + internal abstract IMongoQueryable AsQueryable( + IMongoDatabase database, + IClientSessionHandle session, + AggregateOptions options); + internal abstract BsonValue TranslateExpressionToAggregateExpression( Expression> expression, IBsonSerializer sourceSerializer, diff --git a/src/MongoDB.Driver/Linq/MongoDBMath.cs b/src/MongoDB.Driver/Linq/MongoDBMath.cs new file mode 100644 index 00000000000..e5bbe93abaf --- /dev/null +++ b/src/MongoDB.Driver/Linq/MongoDBMath.cs @@ -0,0 +1,45 @@ +/* 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; + +namespace MongoDB.Driver.Linq +{ + /// + /// Similar to the standard Math class but contains MongoDB specific methods. + /// + public static class MongoDBMath + { + /// + /// Converts degrees to radians. + /// + /// The degrees. + /// The radians. + public static double DegreesToRadians(double degrees) + { + return degrees * (Math.PI / 180.0); + } + + /// + /// Converts radians to degrees. + /// + /// The radians. + /// The degrees. + public static double RadiansToDegrees(double radians) + { + return radians / (Math.PI / 180.0); + } + } +} diff --git a/src/MongoDB.Driver/Linq/MongoQueryable.cs b/src/MongoDB.Driver/Linq/MongoQueryable.cs index 3e3af9519e4..f2dcdab9751 100644 --- a/src/MongoDB.Driver/Linq/MongoQueryable.cs +++ b/src/MongoDB.Driver/Linq/MongoQueryable.cs @@ -21,6 +21,8 @@ using System.Threading; using System.Threading.Tasks; using MongoDB.Bson.Serialization; +using MongoDB.Driver.Search; +using MongoDB.Driver.Core.Misc; namespace MongoDB.Driver.Linq { @@ -40,6 +42,8 @@ public static class MongoQueryable /// public static Task AnyAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, bool>(Queryable.Any, source), @@ -59,6 +63,9 @@ public static class MongoQueryable /// public static Task AnyAsync(this IMongoQueryable source, Expression> predicate, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(predicate, nameof(predicate)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, Expression>, bool>(Queryable.Any, source, predicate), @@ -81,13 +88,16 @@ public static IMongoQueryable AppendStage( PipelineStageDefinition stage, IBsonSerializer resultSerializer = null) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(stage, nameof(stage)); + return (IMongoQueryable)source.Provider.CreateQuery( Expression.Call( null, GetMethodInfo(AppendStage, source, stage, resultSerializer), Expression.Convert(source.Expression, typeof(IMongoQueryable)), Expression.Constant(stage), - Expression.Constant(resultSerializer))); + Expression.Constant(resultSerializer, typeof(IBsonSerializer)))); } /// @@ -98,6 +108,8 @@ public static IMongoQueryable AppendStage( /// The average of the values in the sequence. public static Task AverageAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, decimal>(Queryable.Average, source), @@ -113,6 +125,8 @@ public static IMongoQueryable AppendStage( /// The average of the values in the sequence. public static Task AverageAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, decimal?>(Queryable.Average, source), @@ -128,6 +142,8 @@ public static IMongoQueryable AppendStage( /// The average of the values in the sequence. public static Task AverageAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, double>(Queryable.Average, source), @@ -143,6 +159,8 @@ public static IMongoQueryable AppendStage( /// The average of the values in the sequence. public static Task AverageAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, double?>(Queryable.Average, source), @@ -158,6 +176,8 @@ public static IMongoQueryable AppendStage( /// The average of the values in the sequence. public static Task AverageAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, float>(Queryable.Average, source), @@ -173,6 +193,8 @@ public static IMongoQueryable AppendStage( /// The average of the values in the sequence. public static Task AverageAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, float?>(Queryable.Average, source), @@ -188,6 +210,8 @@ public static IMongoQueryable AppendStage( /// The average of the values in the sequence. public static Task AverageAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, double>(Queryable.Average, source), @@ -203,6 +227,8 @@ public static IMongoQueryable AppendStage( /// The average of the values in the sequence. public static Task AverageAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, double?>(Queryable.Average, source), @@ -218,6 +244,8 @@ public static IMongoQueryable AppendStage( /// The average of the values in the sequence. public static Task AverageAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, double>(Queryable.Average, source), @@ -233,6 +261,8 @@ public static IMongoQueryable AppendStage( /// The average of the values in the sequence. public static Task AverageAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, double?>(Queryable.Average, source), @@ -253,6 +283,9 @@ public static IMongoQueryable AppendStage( /// public static Task AverageAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, Expression>, decimal>(Queryable.Average, source, selector), @@ -274,6 +307,9 @@ public static IMongoQueryable AppendStage( /// public static Task AverageAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, Expression>, decimal?>(Queryable.Average, source, selector), @@ -295,6 +331,9 @@ public static IMongoQueryable AppendStage( /// public static Task AverageAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, Expression>, double>(Queryable.Average, source, selector), @@ -316,6 +355,9 @@ public static IMongoQueryable AppendStage( /// public static Task AverageAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, Expression>, double?>(Queryable.Average, source, selector), @@ -337,6 +379,9 @@ public static IMongoQueryable AppendStage( /// public static Task AverageAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, Expression>, float>(Queryable.Average, source, selector), @@ -358,6 +403,9 @@ public static IMongoQueryable AppendStage( /// public static Task AverageAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, Expression>, float?>(Queryable.Average, source, selector), @@ -379,6 +427,9 @@ public static IMongoQueryable AppendStage( /// public static Task AverageAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, Expression>, double>(Queryable.Average, source, selector), @@ -400,6 +451,9 @@ public static IMongoQueryable AppendStage( /// public static Task AverageAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, Expression>, double?>(Queryable.Average, source, selector), @@ -421,6 +475,9 @@ public static IMongoQueryable AppendStage( /// public static Task AverageAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, Expression>, double>(Queryable.Average, source, selector), @@ -442,6 +499,9 @@ public static IMongoQueryable AppendStage( /// public static Task AverageAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, Expression>, double?>(Queryable.Average, source, selector), @@ -461,6 +521,8 @@ public static IMongoQueryable AppendStage( /// public static Task CountAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, int>(Queryable.Count, source), @@ -480,6 +542,9 @@ public static IMongoQueryable AppendStage( /// public static Task CountAsync(this IMongoQueryable source, Expression> predicate, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(predicate, nameof(predicate)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, Expression>, int>(Queryable.Count, source, predicate), @@ -503,6 +568,10 @@ public static IMongoQueryable Densify( DensifyRange range, IEnumerable>> partitionByFields = null) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(field, nameof(field)); + Ensure.IsNotNull(range, nameof(range)); + return Densify(source, field, range, partitionByFields?.ToArray()); } @@ -521,6 +590,10 @@ public static IMongoQueryable Densify( DensifyRange range, params Expression>[] partitionByFields) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(field, nameof(field)); + Ensure.IsNotNull(range, nameof(range)); + Expression quotedPartitionByFields; if (partitionByFields?.Length > 0) { @@ -550,9 +623,58 @@ public static IMongoQueryable Densify( /// public static IMongoQueryable Distinct(this IMongoQueryable source) { + Ensure.IsNotNull(source, nameof(source)); + return (IMongoQueryable)Queryable.Distinct(source); } + /// + /// Injects a sequence of documents at the beginning of a pipeline. + /// + /// The type of the documents. + /// An IMongoQueryable with no other input. + /// The documents. + /// + /// An whose elements are the documents. + /// + public static IMongoQueryable Documents(this IMongoQueryable source, params TDocument[] documents) + { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(documents, nameof(documents)); + + return (IMongoQueryable)source.Provider.CreateQuery( + Expression.Call( + null, + GetMethodInfo(Documents, source, documents), + Expression.Convert(source.Expression, typeof(IMongoQueryable)), + Expression.Constant(documents, typeof(TDocument[])))); + } + + /// + /// Injects a sequence of documents at the beginning of a pipeline. + /// + /// The type of the documents. + /// An IMongoQueryable with no other input. + /// The documents. + /// The document serializer. + /// + /// An whose elements are the documents. + /// + public static IMongoQueryable Documents(this IMongoQueryable source, IEnumerable documents, IBsonSerializer documentSerializer) + { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(documents, nameof(documents)); + Ensure.IsNotNull(documentSerializer, nameof(documentSerializer)); + + return (IMongoQueryable)source.Provider.CreateQuery( + Expression.Call( + null, + GetMethodInfo(Documents, source, documents, documentSerializer), + Expression.Convert(source.Expression, typeof(IMongoQueryable)), + Expression.Constant(documents, typeof(IEnumerable)), + Expression.Constant(documentSerializer, typeof(IBsonSerializer)))); + } + /// /// Returns the first element of a sequence. /// @@ -564,6 +686,8 @@ public static IMongoQueryable Distinct(this IMongoQueryable public static Task FirstAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, TSource>(Queryable.First, source), @@ -583,6 +707,9 @@ public static IMongoQueryable Distinct(this IMongoQueryable public static Task FirstAsync(this IMongoQueryable source, Expression> predicate, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(predicate, nameof(predicate)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, Expression>, TSource>(Queryable.First, source, predicate), @@ -602,6 +729,8 @@ public static IMongoQueryable Distinct(this IMongoQueryable public static Task FirstOrDefaultAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, TSource>(Queryable.FirstOrDefault, source), @@ -621,6 +750,9 @@ public static IMongoQueryable Distinct(this IMongoQueryable public static Task FirstOrDefaultAsync(this IMongoQueryable source, Expression> predicate, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(predicate, nameof(predicate)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, Expression>, TSource>(Queryable.FirstOrDefault, source, predicate), @@ -643,6 +775,9 @@ public static IMongoQueryable Distinct(this IMongoQueryable public static IMongoQueryable> GroupBy(this IMongoQueryable source, Expression> keySelector) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(keySelector, nameof(keySelector)); + return (IMongoQueryable>)Queryable.GroupBy(source, keySelector); } @@ -662,6 +797,10 @@ public static IMongoQueryable> GroupBy(t /// public static IMongoQueryable GroupBy(this IMongoQueryable source, Expression> keySelector, Expression, TResult>> resultSelector) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(keySelector, nameof(keySelector)); + Ensure.IsNotNull(resultSelector, nameof(resultSelector)); + return (IMongoQueryable)Queryable.GroupBy(source, keySelector, resultSelector); } @@ -682,6 +821,12 @@ public static IMongoQueryable GroupBy(this IMon /// public static IMongoQueryable GroupJoin(this IMongoQueryable outer, IEnumerable inner, Expression> outerKeySelector, Expression> innerKeySelector, Expression, TResult>> resultSelector) { + Ensure.IsNotNull(outer, nameof(outer)); + Ensure.IsNotNull(inner, nameof(inner)); + Ensure.IsNotNull(outerKeySelector, nameof(outerKeySelector)); + Ensure.IsNotNull(innerKeySelector, nameof(innerKeySelector)); + Ensure.IsNotNull(resultSelector, nameof(resultSelector)); + return (IMongoQueryable)Queryable.GroupJoin(outer, inner, outerKeySelector, innerKeySelector, resultSelector); } @@ -702,6 +847,12 @@ public static IMongoQueryable GroupJoin( /// public static IMongoQueryable GroupJoin(this IMongoQueryable outer, IMongoCollection inner, Expression> outerKeySelector, Expression> innerKeySelector, Expression, TResult>> resultSelector) { + Ensure.IsNotNull(outer, nameof(outer)); + Ensure.IsNotNull(inner, nameof(inner)); + Ensure.IsNotNull(outerKeySelector, nameof(outerKeySelector)); + Ensure.IsNotNull(innerKeySelector, nameof(innerKeySelector)); + Ensure.IsNotNull(resultSelector, nameof(resultSelector)); + return GroupJoin(outer, inner.AsQueryable(), outerKeySelector, innerKeySelector, resultSelector); } @@ -722,6 +873,12 @@ public static IMongoQueryable GroupJoin( /// public static IMongoQueryable Join(this IMongoQueryable outer, IEnumerable inner, Expression> outerKeySelector, Expression> innerKeySelector, Expression> resultSelector) { + Ensure.IsNotNull(outer, nameof(outer)); + Ensure.IsNotNull(inner, nameof(inner)); + Ensure.IsNotNull(outerKeySelector, nameof(outerKeySelector)); + Ensure.IsNotNull(innerKeySelector, nameof(innerKeySelector)); + Ensure.IsNotNull(resultSelector, nameof(resultSelector)); + return (IMongoQueryable)Queryable.Join(outer, inner.AsQueryable(), outerKeySelector, innerKeySelector, resultSelector); } @@ -742,6 +899,12 @@ public static IMongoQueryable Join(this /// public static IMongoQueryable Join(this IMongoQueryable outer, IMongoCollection inner, Expression> outerKeySelector, Expression> innerKeySelector, Expression> resultSelector) { + Ensure.IsNotNull(outer, nameof(outer)); + Ensure.IsNotNull(inner, nameof(inner)); + Ensure.IsNotNull(outerKeySelector, nameof(outerKeySelector)); + Ensure.IsNotNull(innerKeySelector, nameof(innerKeySelector)); + Ensure.IsNotNull(resultSelector, nameof(resultSelector)); + return Join(outer, inner.AsQueryable(), outerKeySelector, innerKeySelector, resultSelector); } @@ -756,6 +919,8 @@ public static IMongoQueryable Join(this /// public static Task LongCountAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, long>(Queryable.LongCount, source), @@ -775,6 +940,9 @@ public static IMongoQueryable Join(this /// public static Task LongCountAsync(this IMongoQueryable source, Expression> predicate, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(predicate, nameof(predicate)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, Expression>, long>(Queryable.LongCount, source, predicate), @@ -794,6 +962,8 @@ public static IMongoQueryable Join(this /// public static Task MaxAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, TSource>(Queryable.Max, source), @@ -814,6 +984,9 @@ public static IMongoQueryable Join(this /// public static Task MaxAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, Expression>, TResult>(Queryable.Max, source, selector), @@ -833,6 +1006,8 @@ public static IMongoQueryable Join(this /// public static Task MinAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, TSource>(Queryable.Min, source), @@ -853,6 +1028,9 @@ public static IMongoQueryable Join(this /// public static Task MinAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, Expression>, TResult>(Queryable.Min, source, selector), @@ -871,6 +1049,8 @@ public static IMongoQueryable Join(this /// public static IMongoQueryable OfType(this IMongoQueryable source) { + Ensure.IsNotNull(source, nameof(source)); + return (IMongoQueryable)Queryable.OfType(source); } @@ -886,6 +1066,9 @@ public static IMongoQueryable OfType(this IMongoQueryable sour /// public static IOrderedMongoQueryable OrderBy(this IMongoQueryable source, Expression> keySelector) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(keySelector, nameof(keySelector)); + return (IOrderedMongoQueryable)Queryable.OrderBy(source, keySelector); } @@ -901,6 +1084,9 @@ public static IOrderedMongoQueryable OrderBy(this IMongo /// public static IOrderedMongoQueryable OrderByDescending(this IMongoQueryable source, Expression> keySelector) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(keySelector, nameof(keySelector)); + return (IOrderedMongoQueryable)Queryable.OrderByDescending(source, keySelector); } @@ -915,6 +1101,8 @@ public static IOrderedMongoQueryable OrderByDescending(t /// public static IMongoQueryable Sample(this IMongoQueryable source, long count) { + Ensure.IsNotNull(source, nameof(source)); + return (IMongoQueryable)source.Provider.CreateQuery( Expression.Call( null, @@ -923,6 +1111,53 @@ public static IMongoQueryable Sample(this IMongoQueryable + /// Appends a $search stage to the LINQ pipeline. + /// + /// The type of the elements of . + /// A sequence of values. + /// The search definition. + /// The highlight options. + /// The index name. + /// The count options. + /// + /// Flag that specifies whether to perform a full document lookup on the backend database + /// or return only stored source fields directly from Atlas Search. + /// + /// The queryable with a new stage appended. + public static IMongoQueryable Search( + this IMongoQueryable source, + SearchDefinition searchDefinition, + SearchHighlightOptions highlight = null, + string indexName = null, + SearchCountOptions count = null, + bool returnStoredSource = false) + { + return AppendStage( + source, + PipelineStageDefinitionBuilder.Search(searchDefinition, highlight, indexName, count, returnStoredSource)); + } + + /// + /// Appends a $searchMeta stage to the LINQ pipeline. + /// + /// The type of the elements of . + /// A sequence of values. + /// The search definition. + /// The index name. + /// The count options. + /// The queryable with a new stage appended. + public static IMongoQueryable SearchMeta( + this IMongoQueryable source, + SearchDefinition searchDefinition, + string indexName = null, + SearchCountOptions count = null) + { + return AppendStage( + source, + PipelineStageDefinitionBuilder.SearchMeta(searchDefinition, indexName, count)); + } + /// /// Projects each element of a sequence into a new form by incorporating the /// element's index. @@ -937,6 +1172,9 @@ public static IMongoQueryable Sample(this IMongoQueryable public static IMongoQueryable Select(this IMongoQueryable source, Expression> selector) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return (IMongoQueryable)Queryable.Select(source, selector); } @@ -952,6 +1190,9 @@ public static IMongoQueryable Select(this IMongoQuery /// public static IMongoQueryable SelectMany(this IMongoQueryable source, Expression>> selector) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return (IMongoQueryable)Queryable.SelectMany(source, selector); } @@ -971,6 +1212,10 @@ public static IMongoQueryable SelectMany(this IMongoQ /// public static IMongoQueryable SelectMany(this IMongoQueryable source, Expression>> collectionSelector, Expression> resultSelector) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(collectionSelector, nameof(collectionSelector)); + Ensure.IsNotNull(resultSelector, nameof(resultSelector)); + return (IMongoQueryable)Queryable.SelectMany(source, collectionSelector, resultSelector); } @@ -985,6 +1230,8 @@ public static IMongoQueryable SelectMany /// public static Task SingleAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, TSource>(Queryable.Single, source), @@ -1004,6 +1251,9 @@ public static IMongoQueryable SelectMany /// public static Task SingleAsync(this IMongoQueryable source, Expression> predicate, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(predicate, nameof(predicate)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, Expression>, TSource>(Queryable.Single, source, predicate), @@ -1023,6 +1273,8 @@ public static IMongoQueryable SelectMany /// public static Task SingleOrDefaultAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, TSource>(Queryable.SingleOrDefault, source), @@ -1042,6 +1294,9 @@ public static IMongoQueryable SelectMany /// public static Task SingleOrDefaultAsync(this IMongoQueryable source, Expression> predicate, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(predicate, nameof(predicate)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, Expression>, TSource>(Queryable.SingleOrDefault, source, predicate), @@ -1063,9 +1318,32 @@ public static IMongoQueryable SelectMany /// public static IMongoQueryable Skip(this IMongoQueryable source, int count) { + Ensure.IsNotNull(source, nameof(source)); + return (IMongoQueryable)Queryable.Skip(source, count); } + /// + /// Bypasses a specified number of elements in a sequence and then returns the + /// remaining elements. + /// + /// The type of the elements of source + /// An to return elements from. + /// The number of elements to skip before returning the remaining elements. + /// + /// An that contains elements that occur after the + /// specified index in the input sequence. + /// + public static IMongoQueryable Skip(this IMongoQueryable source, long count) + { + return (IMongoQueryable)source.Provider.CreateQuery( + Expression.Call( + null, + GetMethodInfo(Skip, source, count), + Expression.Convert(source.Expression, typeof(IMongoQueryable)), + Expression.Constant(count))); + } + /// /// Computes the population standard deviation of a sequence of values. /// @@ -1075,6 +1353,8 @@ public static IMongoQueryable Skip(this IMongoQueryable public static double StandardDeviationPopulation(this IMongoQueryable source) { + Ensure.IsNotNull(source, nameof(source)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source), @@ -1090,6 +1370,8 @@ public static double StandardDeviationPopulation(this IMongoQueryable sourc /// public static double? StandardDeviationPopulation(this IMongoQueryable source) { + Ensure.IsNotNull(source, nameof(source)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source), @@ -1105,6 +1387,8 @@ public static double StandardDeviationPopulation(this IMongoQueryable sourc /// public static double StandardDeviationPopulation(this IMongoQueryable source) { + Ensure.IsNotNull(source, nameof(source)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source), @@ -1120,6 +1404,8 @@ public static double StandardDeviationPopulation(this IMongoQueryable sour /// public static double? StandardDeviationPopulation(this IMongoQueryable source) { + Ensure.IsNotNull(source, nameof(source)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source), @@ -1135,6 +1421,8 @@ public static double StandardDeviationPopulation(this IMongoQueryable sour /// public static float StandardDeviationPopulation(this IMongoQueryable source) { + Ensure.IsNotNull(source, nameof(source)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source), @@ -1150,6 +1438,8 @@ public static float StandardDeviationPopulation(this IMongoQueryable sour /// public static float? StandardDeviationPopulation(this IMongoQueryable source) { + Ensure.IsNotNull(source, nameof(source)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source), @@ -1165,6 +1455,8 @@ public static float StandardDeviationPopulation(this IMongoQueryable sour /// public static double StandardDeviationPopulation(this IMongoQueryable source) { + Ensure.IsNotNull(source, nameof(source)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source), @@ -1180,6 +1472,8 @@ public static double StandardDeviationPopulation(this IMongoQueryable so /// public static double? StandardDeviationPopulation(this IMongoQueryable source) { + Ensure.IsNotNull(source, nameof(source)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source), @@ -1195,6 +1489,8 @@ public static double StandardDeviationPopulation(this IMongoQueryable so /// public static decimal StandardDeviationPopulation(this IMongoQueryable source) { + Ensure.IsNotNull(source, nameof(source)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source), @@ -1210,6 +1506,8 @@ public static decimal StandardDeviationPopulation(this IMongoQueryable /// public static decimal? StandardDeviationPopulation(this IMongoQueryable source) { + Ensure.IsNotNull(source, nameof(source)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source), @@ -1227,6 +1525,9 @@ public static decimal StandardDeviationPopulation(this IMongoQueryable /// public static double StandardDeviationPopulation(this IMongoQueryable source, Expression> selector) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source, selector), @@ -1245,6 +1546,9 @@ public static double StandardDeviationPopulation(this IMongoQueryable public static double? StandardDeviationPopulation(this IMongoQueryable source, Expression> selector) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source, selector), @@ -1263,6 +1567,9 @@ public static double StandardDeviationPopulation(this IMongoQueryable public static double StandardDeviationPopulation(this IMongoQueryable source, Expression> selector) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source, selector), @@ -1281,6 +1588,9 @@ public static double StandardDeviationPopulation(this IMongoQueryable public static double? StandardDeviationPopulation(this IMongoQueryable source, Expression> selector) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source, selector), @@ -1299,6 +1609,9 @@ public static double StandardDeviationPopulation(this IMongoQueryable public static float StandardDeviationPopulation(this IMongoQueryable source, Expression> selector) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source, selector), @@ -1317,6 +1630,9 @@ public static float StandardDeviationPopulation(this IMongoQueryable public static float? StandardDeviationPopulation(this IMongoQueryable source, Expression> selector) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source, selector), @@ -1335,6 +1651,9 @@ public static float StandardDeviationPopulation(this IMongoQueryable public static double StandardDeviationPopulation(this IMongoQueryable source, Expression> selector) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source, selector), @@ -1353,6 +1672,9 @@ public static double StandardDeviationPopulation(this IMongoQueryable public static double? StandardDeviationPopulation(this IMongoQueryable source, Expression> selector) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source, selector), @@ -1371,6 +1693,9 @@ public static double StandardDeviationPopulation(this IMongoQueryable public static decimal StandardDeviationPopulation(this IMongoQueryable source, Expression> selector) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source, selector), @@ -1389,6 +1714,9 @@ public static decimal StandardDeviationPopulation(this IMongoQueryable< /// public static decimal? StandardDeviationPopulation(this IMongoQueryable source, Expression> selector) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source, selector), @@ -1406,6 +1734,8 @@ public static decimal StandardDeviationPopulation(this IMongoQueryable< /// public static Task StandardDeviationPopulationAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source), @@ -1423,6 +1753,8 @@ public static decimal StandardDeviationPopulation(this IMongoQueryable< /// public static Task StandardDeviationPopulationAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source), @@ -1440,6 +1772,8 @@ public static decimal StandardDeviationPopulation(this IMongoQueryable< /// public static Task StandardDeviationPopulationAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source), @@ -1457,6 +1791,8 @@ public static decimal StandardDeviationPopulation(this IMongoQueryable< /// public static Task StandardDeviationPopulationAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source), @@ -1474,6 +1810,8 @@ public static decimal StandardDeviationPopulation(this IMongoQueryable< /// public static Task StandardDeviationPopulationAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source), @@ -1491,6 +1829,8 @@ public static decimal StandardDeviationPopulation(this IMongoQueryable< /// public static Task StandardDeviationPopulationAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source), @@ -1508,6 +1848,8 @@ public static decimal StandardDeviationPopulation(this IMongoQueryable< /// public static Task StandardDeviationPopulationAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source), @@ -1525,6 +1867,8 @@ public static decimal StandardDeviationPopulation(this IMongoQueryable< /// public static Task StandardDeviationPopulationAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source), @@ -1542,6 +1886,8 @@ public static decimal StandardDeviationPopulation(this IMongoQueryable< /// public static Task StandardDeviationPopulationAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source), @@ -1559,6 +1905,8 @@ public static decimal StandardDeviationPopulation(this IMongoQueryable< /// public static Task StandardDeviationPopulationAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source), @@ -1578,6 +1926,9 @@ public static decimal StandardDeviationPopulation(this IMongoQueryable< /// public static Task StandardDeviationPopulationAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source, selector), @@ -1598,6 +1949,9 @@ public static decimal StandardDeviationPopulation(this IMongoQueryable< /// public static Task StandardDeviationPopulationAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source, selector), @@ -1618,6 +1972,9 @@ public static decimal StandardDeviationPopulation(this IMongoQueryable< /// public static Task StandardDeviationPopulationAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source, selector), @@ -1638,6 +1995,9 @@ public static decimal StandardDeviationPopulation(this IMongoQueryable< /// public static Task StandardDeviationPopulationAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source, selector), @@ -1658,6 +2018,9 @@ public static decimal StandardDeviationPopulation(this IMongoQueryable< /// public static Task StandardDeviationPopulationAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source, selector), @@ -1678,6 +2041,9 @@ public static decimal StandardDeviationPopulation(this IMongoQueryable< /// public static Task StandardDeviationPopulationAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source, selector), @@ -1698,6 +2064,9 @@ public static decimal StandardDeviationPopulation(this IMongoQueryable< /// public static Task StandardDeviationPopulationAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source, selector), @@ -1718,6 +2087,9 @@ public static decimal StandardDeviationPopulation(this IMongoQueryable< /// public static Task StandardDeviationPopulationAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source, selector), @@ -1738,6 +2110,9 @@ public static decimal StandardDeviationPopulation(this IMongoQueryable< /// public static Task StandardDeviationPopulationAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source, selector), @@ -1758,6 +2133,9 @@ public static decimal StandardDeviationPopulation(this IMongoQueryable< /// public static Task StandardDeviationPopulationAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationPopulation, source, selector), @@ -1775,6 +2153,8 @@ public static decimal StandardDeviationPopulation(this IMongoQueryable< /// public static double StandardDeviationSample(this IMongoQueryable source) { + Ensure.IsNotNull(source, nameof(source)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationSample, source), @@ -1790,6 +2170,8 @@ public static double StandardDeviationSample(this IMongoQueryable source) /// public static double? StandardDeviationSample(this IMongoQueryable source) { + Ensure.IsNotNull(source, nameof(source)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationSample, source), @@ -1805,6 +2187,8 @@ public static double StandardDeviationSample(this IMongoQueryable source) /// public static double StandardDeviationSample(this IMongoQueryable source) { + Ensure.IsNotNull(source, nameof(source)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationSample, source), @@ -1820,6 +2204,8 @@ public static double StandardDeviationSample(this IMongoQueryable source) /// public static double? StandardDeviationSample(this IMongoQueryable source) { + Ensure.IsNotNull(source, nameof(source)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationSample, source), @@ -1835,6 +2221,8 @@ public static double StandardDeviationSample(this IMongoQueryable source) /// public static float StandardDeviationSample(this IMongoQueryable source) { + Ensure.IsNotNull(source, nameof(source)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationSample, source), @@ -1850,6 +2238,8 @@ public static float StandardDeviationSample(this IMongoQueryable source) /// public static float? StandardDeviationSample(this IMongoQueryable source) { + Ensure.IsNotNull(source, nameof(source)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationSample, source), @@ -1865,6 +2255,8 @@ public static float StandardDeviationSample(this IMongoQueryable source) /// public static double StandardDeviationSample(this IMongoQueryable source) { + Ensure.IsNotNull(source, nameof(source)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationSample, source), @@ -1880,6 +2272,8 @@ public static double StandardDeviationSample(this IMongoQueryable source /// public static double? StandardDeviationSample(this IMongoQueryable source) { + Ensure.IsNotNull(source, nameof(source)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationSample, source), @@ -1895,6 +2289,8 @@ public static double StandardDeviationSample(this IMongoQueryable source /// public static decimal StandardDeviationSample(this IMongoQueryable source) { + Ensure.IsNotNull(source, nameof(source)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationSample, source), @@ -1910,6 +2306,8 @@ public static decimal StandardDeviationSample(this IMongoQueryable sour /// public static decimal? StandardDeviationSample(this IMongoQueryable source) { + Ensure.IsNotNull(source, nameof(source)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationSample, source), @@ -1927,6 +2325,9 @@ public static decimal StandardDeviationSample(this IMongoQueryable sour /// public static double StandardDeviationSample(this IMongoQueryable source, Expression> selector) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationSample, source, selector), @@ -1945,6 +2346,9 @@ public static double StandardDeviationSample(this IMongoQueryable public static double? StandardDeviationSample(this IMongoQueryable source, Expression> selector) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationSample, source, selector), @@ -1963,6 +2367,9 @@ public static double StandardDeviationSample(this IMongoQueryable public static double StandardDeviationSample(this IMongoQueryable source, Expression> selector) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationSample, source, selector), @@ -1981,6 +2388,9 @@ public static double StandardDeviationSample(this IMongoQueryable public static double? StandardDeviationSample(this IMongoQueryable source, Expression> selector) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationSample, source, selector), @@ -1999,6 +2409,9 @@ public static double StandardDeviationSample(this IMongoQueryable public static float StandardDeviationSample(this IMongoQueryable source, Expression> selector) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationSample, source, selector), @@ -2017,6 +2430,9 @@ public static float StandardDeviationSample(this IMongoQueryable public static float? StandardDeviationSample(this IMongoQueryable source, Expression> selector) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationSample, source, selector), @@ -2035,6 +2451,9 @@ public static float StandardDeviationSample(this IMongoQueryable public static double StandardDeviationSample(this IMongoQueryable source, Expression> selector) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationSample, source, selector), @@ -2053,6 +2472,9 @@ public static double StandardDeviationSample(this IMongoQueryable public static double? StandardDeviationSample(this IMongoQueryable source, Expression> selector) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationSample, source, selector), @@ -2071,6 +2493,9 @@ public static double StandardDeviationSample(this IMongoQueryable public static decimal StandardDeviationSample(this IMongoQueryable source, Expression> selector) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationSample, source, selector), @@ -2089,6 +2514,9 @@ public static decimal StandardDeviationSample(this IMongoQueryable public static decimal? StandardDeviationSample(this IMongoQueryable source, Expression> selector) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return source.Provider.Execute( Expression.Call( GetMethodInfo(StandardDeviationSample, source, selector), @@ -2106,6 +2534,8 @@ public static decimal StandardDeviationSample(this IMongoQueryable public static Task StandardDeviationSampleAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationSample, source), @@ -2123,6 +2553,8 @@ public static decimal StandardDeviationSample(this IMongoQueryable public static Task StandardDeviationSampleAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationSample, source), @@ -2140,6 +2572,8 @@ public static decimal StandardDeviationSample(this IMongoQueryable public static Task StandardDeviationSampleAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationSample, source), @@ -2157,6 +2591,8 @@ public static decimal StandardDeviationSample(this IMongoQueryable public static Task StandardDeviationSampleAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationSample, source), @@ -2174,6 +2610,8 @@ public static decimal StandardDeviationSample(this IMongoQueryable public static Task StandardDeviationSampleAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationSample, source), @@ -2191,6 +2629,8 @@ public static decimal StandardDeviationSample(this IMongoQueryable public static Task StandardDeviationSampleAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationSample, source), @@ -2208,6 +2648,8 @@ public static decimal StandardDeviationSample(this IMongoQueryable public static Task StandardDeviationSampleAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationSample, source), @@ -2225,6 +2667,8 @@ public static decimal StandardDeviationSample(this IMongoQueryable public static Task StandardDeviationSampleAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationSample, source), @@ -2242,6 +2686,8 @@ public static decimal StandardDeviationSample(this IMongoQueryable public static Task StandardDeviationSampleAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationSample, source), @@ -2259,6 +2705,8 @@ public static decimal StandardDeviationSample(this IMongoQueryable public static Task StandardDeviationSampleAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationSample, source), @@ -2278,6 +2726,9 @@ public static decimal StandardDeviationSample(this IMongoQueryable public static Task StandardDeviationSampleAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationSample, source, selector), @@ -2298,6 +2749,9 @@ public static decimal StandardDeviationSample(this IMongoQueryable public static Task StandardDeviationSampleAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationSample, source, selector), @@ -2318,6 +2772,9 @@ public static decimal StandardDeviationSample(this IMongoQueryable public static Task StandardDeviationSampleAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationSample, source, selector), @@ -2338,6 +2795,9 @@ public static decimal StandardDeviationSample(this IMongoQueryable public static Task StandardDeviationSampleAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationSample, source, selector), @@ -2358,6 +2818,9 @@ public static decimal StandardDeviationSample(this IMongoQueryable public static Task StandardDeviationSampleAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationSample, source, selector), @@ -2378,6 +2841,9 @@ public static decimal StandardDeviationSample(this IMongoQueryable public static Task StandardDeviationSampleAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationSample, source, selector), @@ -2398,6 +2864,9 @@ public static decimal StandardDeviationSample(this IMongoQueryable public static Task StandardDeviationSampleAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationSample, source, selector), @@ -2418,6 +2887,9 @@ public static decimal StandardDeviationSample(this IMongoQueryable public static Task StandardDeviationSampleAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationSample, source, selector), @@ -2438,6 +2910,9 @@ public static decimal StandardDeviationSample(this IMongoQueryable public static Task StandardDeviationSampleAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationSample, source, selector), @@ -2458,6 +2933,9 @@ public static decimal StandardDeviationSample(this IMongoQueryable public static Task StandardDeviationSampleAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo(StandardDeviationSample, source, selector), @@ -2474,6 +2952,8 @@ public static decimal StandardDeviationSample(this IMongoQueryableThe sum of the values in the sequence. public static Task SumAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, decimal>(Queryable.Sum, source), @@ -2489,6 +2969,8 @@ public static decimal StandardDeviationSample(this IMongoQueryableThe sum of the values in the sequence. public static Task SumAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, decimal?>(Queryable.Sum, source), @@ -2504,6 +2986,8 @@ public static decimal StandardDeviationSample(this IMongoQueryableThe sum of the values in the sequence. public static Task SumAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, double>(Queryable.Sum, source), @@ -2519,6 +3003,8 @@ public static decimal StandardDeviationSample(this IMongoQueryableThe sum of the values in the sequence. public static Task SumAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, double?>(Queryable.Sum, source), @@ -2534,6 +3020,8 @@ public static decimal StandardDeviationSample(this IMongoQueryableThe sum of the values in the sequence. public static Task SumAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, float>(Queryable.Sum, source), @@ -2549,6 +3037,8 @@ public static decimal StandardDeviationSample(this IMongoQueryableThe sum of the values in the sequence. public static Task SumAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, float?>(Queryable.Sum, source), @@ -2564,6 +3054,8 @@ public static decimal StandardDeviationSample(this IMongoQueryableThe sum of the values in the sequence. public static Task SumAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, int>(Queryable.Sum, source), @@ -2579,6 +3071,8 @@ public static decimal StandardDeviationSample(this IMongoQueryableThe sum of the values in the sequence. public static Task SumAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, int?>(Queryable.Sum, source), @@ -2594,6 +3088,8 @@ public static decimal StandardDeviationSample(this IMongoQueryableThe sum of the values in the sequence. public static Task SumAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, long>(Queryable.Sum, source), @@ -2609,6 +3105,8 @@ public static decimal StandardDeviationSample(this IMongoQueryableThe sum of the values in the sequence. public static Task SumAsync(this IMongoQueryable source, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, long?>(Queryable.Sum, source), @@ -2629,6 +3127,9 @@ public static decimal StandardDeviationSample(this IMongoQueryable public static Task SumAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, Expression>, decimal>(Queryable.Sum, source, selector), @@ -2650,6 +3151,9 @@ public static decimal StandardDeviationSample(this IMongoQueryable public static Task SumAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, Expression>, decimal?>(Queryable.Sum, source, selector), @@ -2671,6 +3175,9 @@ public static decimal StandardDeviationSample(this IMongoQueryable public static Task SumAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, Expression>, double>(Queryable.Sum, source, selector), @@ -2692,6 +3199,9 @@ public static decimal StandardDeviationSample(this IMongoQueryable public static Task SumAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, Expression>, double?>(Queryable.Sum, source, selector), @@ -2713,6 +3223,9 @@ public static decimal StandardDeviationSample(this IMongoQueryable public static Task SumAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, Expression>, float>(Queryable.Sum, source, selector), @@ -2734,6 +3247,9 @@ public static decimal StandardDeviationSample(this IMongoQueryable public static Task SumAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, Expression>, float?>(Queryable.Sum, source, selector), @@ -2755,6 +3271,9 @@ public static decimal StandardDeviationSample(this IMongoQueryable public static Task SumAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, Expression>, int>(Queryable.Sum, source, selector), @@ -2776,6 +3295,9 @@ public static decimal StandardDeviationSample(this IMongoQueryable public static Task SumAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, Expression>, int?>(Queryable.Sum, source, selector), @@ -2797,6 +3319,9 @@ public static decimal StandardDeviationSample(this IMongoQueryable public static Task SumAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, Expression>, long>(Queryable.Sum, source, selector), @@ -2818,6 +3343,9 @@ public static decimal StandardDeviationSample(this IMongoQueryable public static Task SumAsync(this IMongoQueryable source, Expression> selector, CancellationToken cancellationToken = default(CancellationToken)) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(selector, nameof(selector)); + return ((IMongoQueryProvider)source.Provider).ExecuteAsync( Expression.Call( GetMethodInfo, Expression>, long?>(Queryable.Sum, source, selector), @@ -2838,9 +3366,31 @@ public static decimal StandardDeviationSample(this IMongoQueryable public static IMongoQueryable Take(this IMongoQueryable source, int count) { + Ensure.IsNotNull(source, nameof(source)); + return (IMongoQueryable)Queryable.Take(source, count); } + /// + /// Returns a specified number of contiguous elements from the start of a sequence. + /// + /// The type of the elements of . + /// The sequence to return elements from. + /// The number of elements to return. + /// + /// An that contains the specified number of elements + /// from the start of source. + /// + public static IMongoQueryable Take(this IMongoQueryable source, long count) + { + return (IMongoQueryable)source.Provider.CreateQuery( + Expression.Call( + null, + GetMethodInfo(Take, source, count), + Expression.Convert(source.Expression, typeof(IMongoQueryable)), + Expression.Constant(count))); + } + /// /// Performs a subsequent ordering of the elements in a sequence in ascending /// order according to a key. @@ -2854,6 +3404,9 @@ public static IMongoQueryable Take(this IMongoQueryable public static IOrderedMongoQueryable ThenBy(this IOrderedMongoQueryable source, Expression> keySelector) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(keySelector, nameof(keySelector)); + return (IOrderedMongoQueryable)Queryable.ThenBy(source, keySelector); } @@ -2870,6 +3423,9 @@ public static IOrderedMongoQueryable ThenBy(this IOrdere /// public static IOrderedMongoQueryable ThenByDescending(this IOrderedMongoQueryable source, Expression> keySelector) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(keySelector, nameof(keySelector)); + return (IOrderedMongoQueryable)Queryable.ThenByDescending(source, keySelector); } @@ -2885,6 +3441,9 @@ public static IOrderedMongoQueryable ThenByDescending(th /// public static IMongoQueryable Where(this IMongoQueryable source, Expression> predicate) { + Ensure.IsNotNull(source, nameof(source)); + Ensure.IsNotNull(predicate, nameof(predicate)); + return (IMongoQueryable)Queryable.Where(source, predicate); } diff --git a/src/MongoDB.Driver/Linq/NullableDateTimeExtensions.cs b/src/MongoDB.Driver/Linq/NullableDateTimeExtensions.cs new file mode 100644 index 00000000000..e97853f5de8 --- /dev/null +++ b/src/MongoDB.Driver/Linq/NullableDateTimeExtensions.cs @@ -0,0 +1,39 @@ + +/* Copyright 2016-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; + +namespace MongoDB.Driver.Linq +{ + /// + /// This static class holds methods that can be used to express MongoDB specific operations in LINQ queries. + /// + public static class NullableDateTimeExtensions + { + /// + /// Converts a NullableDateTime value to a string. + /// + /// The NullableDateTime value. + /// The format string (optional, can be null). + /// The timezone to use in the returned string (optional, can be null). + /// The string to return if the NullableDateTime value is null. + /// The NullableDateTime value converted to a string. + public static string ToString(this DateTime? @this, string format, string timezone, string onNull) + { + throw new InvalidOperationException("This DateTime.ToString method is only intended to be used in LINQ queries."); + } + } +} diff --git a/src/MongoDB.Driver/MongoClientSettings.cs b/src/MongoDB.Driver/MongoClientSettings.cs index 2d746a27962..56bb31c3cd5 100644 --- a/src/MongoDB.Driver/MongoClientSettings.cs +++ b/src/MongoDB.Driver/MongoClientSettings.cs @@ -53,6 +53,7 @@ public class MongoClientSettings : IEquatable, IInheritable private LinqProvider _linqProvider; private bool _loadBalanced; private TimeSpan _localThreshold; + private LoggingSettings _loggingSettings; private int _maxConnecting; private TimeSpan _maxConnectionIdleTime; private TimeSpan _maxConnectionLifeTime; @@ -109,7 +110,7 @@ public MongoClientSettings() _heartbeatInterval = ServerSettings.DefaultHeartbeatInterval; _heartbeatTimeout = ServerSettings.DefaultHeartbeatTimeout; _ipv6 = false; - _linqProvider = LinqProvider.V2; + _linqProvider = LinqProvider.V3; _loadBalanced = false; _localThreshold = MongoDefaults.LocalThreshold; _maxConnecting = MongoInternalDefaults.ConnectionPool.MaxConnecting; @@ -448,6 +449,19 @@ public TimeSpan LocalThreshold } } + /// + /// Gets or sets the logging settings + /// + public LoggingSettings LoggingSettings + { + get { return _loggingSettings; } + set + { + if (_isFrozen) { throw new InvalidOperationException("MongoClientSettings is frozen."); } + _loggingSettings = value; + } + } + /// /// Gets or sets the maximum concurrently connecting connections. /// @@ -614,6 +628,7 @@ public ConnectionStringScheme Scheme /// /// Gets or set the name of the SDAM log file. Null turns logging off. stdout will log to console. /// + [Obsolete("Use LoggerFactory instead.")] public string SdamLogFilename { get { return _sdamLogFilename; } @@ -943,7 +958,7 @@ public static MongoClientSettings FromUrl(MongoUrl url) clientSettings.HeartbeatInterval = url.HeartbeatInterval; clientSettings.HeartbeatTimeout = url.HeartbeatTimeout; clientSettings.IPv6 = url.IPv6; - clientSettings.LinqProvider = LinqProvider.V2; + clientSettings.LinqProvider = LinqProvider.V3; clientSettings.LoadBalanced = url.LoadBalanced; clientSettings.LocalThreshold = url.LocalThreshold; clientSettings.MaxConnecting = url.MaxConnecting; @@ -1002,6 +1017,7 @@ public MongoClientSettings Clone() clone._linqProvider = _linqProvider; clone._loadBalanced = _loadBalanced; clone._localThreshold = _localThreshold; + clone._loggingSettings = _loggingSettings; clone._maxConnecting = _maxConnecting; clone._maxConnectionIdleTime = _maxConnectionIdleTime; clone._maxConnectionLifeTime = _maxConnectionLifeTime; @@ -1070,6 +1086,7 @@ public override bool Equals(object obj) _linqProvider == rhs._linqProvider && _loadBalanced == rhs._loadBalanced && _localThreshold == rhs._localThreshold && + _loggingSettings == rhs._loggingSettings && _maxConnecting == rhs._maxConnecting && _maxConnectionIdleTime == rhs._maxConnectionIdleTime && _maxConnectionLifeTime == rhs._maxConnectionLifeTime && @@ -1077,7 +1094,7 @@ public override bool Equals(object obj) _minConnectionPoolSize == rhs._minConnectionPoolSize && object.Equals(_readEncoding, rhs._readEncoding) && object.Equals(_readConcern, rhs._readConcern) && - _readPreference == rhs._readPreference && + object.Equals(_readPreference, rhs._readPreference) && _replicaSetName == rhs._replicaSetName && _retryReads == rhs._retryReads && _retryWrites == rhs._retryWrites && @@ -1092,7 +1109,7 @@ public override bool Equals(object obj) _useTls == rhs._useTls && _waitQueueSize == rhs._waitQueueSize && _waitQueueTimeout == rhs._waitQueueTimeout && - _writeConcern == rhs._writeConcern && + object.Equals(_writeConcern, rhs._writeConcern) && object.Equals(_writeEncoding, rhs._writeEncoding); } @@ -1291,6 +1308,7 @@ internal ClusterKey ToClusterKey() _ipv6, _loadBalanced, _localThreshold, + _loggingSettings, _maxConnecting, _maxConnectionIdleTime, _maxConnectionLifeTime, diff --git a/src/MongoDB.Driver/MongoDB.Driver.csproj b/src/MongoDB.Driver/MongoDB.Driver.csproj index ffab1ec5418..913e1baf8ff 100644 --- a/src/MongoDB.Driver/MongoDB.Driver.csproj +++ b/src/MongoDB.Driver/MongoDB.Driver.csproj @@ -1,58 +1,19 @@ - - true - - netstandard2.0;netstandard2.1;net472 - netstandard2.0;netstandard2.1 - 9 - true - ..\..\MongoDB.ruleset MongoDB.Driver MongoDB.Driver - MongoDB Inc. - Copyright © 2010-present MongoDB Inc. Official MongoDB supported driver for MongoDB. See https://www.mongodb.com/docs/drivers/csharp/ for more details. - MongoDB Inc. - packageIcon.png - true Official .NET driver for MongoDB. - https://www.mongodb.com/docs/drivers/csharp/ - License.txt - mongodb;mongo;nosql - en-US - true - snupkg - true - - - - 0.0.0-local - - - - TRACE - - - - - true - - - - - - all - runtime; build; native; contentfiles; analyzers - + + @@ -60,9 +21,4 @@ - - - - - diff --git a/src/MongoDB.Driver/MongoUtils.cs b/src/MongoDB.Driver/MongoUtils.cs index 60cff0bcb9a..43717b4dfc9 100644 --- a/src/MongoDB.Driver/MongoUtils.cs +++ b/src/MongoDB.Driver/MongoUtils.cs @@ -14,8 +14,6 @@ */ using System; -using System.Runtime.InteropServices; -using System.Security; using System.Security.Cryptography; using System.Text; using MongoDB.Bson; @@ -61,5 +59,8 @@ public static string ToCamelCase(string value) { return value.Length == 0 ? "" : value.Substring(0, 1).ToLower() + value.Substring(1); } + + internal static string ToCamelCase(this TEnum @enum) where TEnum : Enum => + ToCamelCase(@enum.ToString()); } } diff --git a/src/MongoDB.Driver/NoPipelineInput.cs b/src/MongoDB.Driver/NoPipelineInput.cs index 787fe340cbf..2285d989a91 100644 --- a/src/MongoDB.Driver/NoPipelineInput.cs +++ b/src/MongoDB.Driver/NoPipelineInput.cs @@ -31,7 +31,7 @@ private NoPipelineInput() /// /// The serializer for NoPipelineInput. /// - internal sealed class NoPipelineInputSerializer : IBsonSerializer + internal sealed class NoPipelineInputSerializer : IBsonSerializer, IBsonDocumentSerializer { #region static // private static fields @@ -70,5 +70,12 @@ public void Serialize(BsonSerializationContext context, BsonSerializationArgs ar { throw new NotSupportedException(); } + + /// + public bool TryGetMemberSerializationInfo(string memberName, out BsonSerializationInfo serializationInfo) + { + serializationInfo = null; + return false; + } } } diff --git a/src/MongoDB.Driver/OfTypeMongoCollection.cs b/src/MongoDB.Driver/OfTypeMongoCollection.cs index 214c121e48c..672f86a9f79 100644 --- a/src/MongoDB.Driver/OfTypeMongoCollection.cs +++ b/src/MongoDB.Driver/OfTypeMongoCollection.cs @@ -13,6 +13,7 @@ * limitations under the License. */ +using MongoDB.Bson; using MongoDB.Bson.Serialization; namespace MongoDB.Driver @@ -61,11 +62,43 @@ protected override UpdateDefinition AdjustUpdateDefinition(Upd if (isUpsert) { var discriminatorConvention = BsonSerializer.LookupDiscriminatorConvention(typeof(TDerivedDocument)); + var discriminatorConventionElementName = discriminatorConvention.ElementName; var discriminatorValue = discriminatorConvention.GetDiscriminator(typeof(TRootDocument), typeof(TDerivedDocument)); - var builder = new UpdateDefinitionBuilder(); - var setOnInsertDiscriminator = builder.SetOnInsert(discriminatorConvention.ElementName, discriminatorValue); - result = builder.Combine(result, setOnInsertDiscriminator); + if (result is PipelineUpdateDefinition pipeline) + { + var setOnInsertStage = new BsonDocument() + { + { + "$set", + new BsonDocument + { + { + discriminatorConventionElementName, // target field + new BsonDocument // condition + { + { + "$cond", + new BsonArray + { + new BsonDocument("$eq", new BsonArray { new BsonDocument("$type", "$_id"), "missing" }), // if "_id" is missed + discriminatorValue, // then set targetField to discriminatorValue + $"${discriminatorConventionElementName}" // else set targetField from the value in the document + } + } + } + } + } + } + }; + result = pipeline.Pipeline.AppendStage(setOnInsertStage); + } + else + { + var builder = new UpdateDefinitionBuilder(); + var setOnInsertDiscriminator = builder.SetOnInsert(discriminatorConventionElementName, discriminatorValue); + result = builder.Combine(result, setOnInsertDiscriminator); + } } return result; diff --git a/src/MongoDB.Driver/PipelineDefinition.cs b/src/MongoDB.Driver/PipelineDefinition.cs index 103d2ceac21..2d26c40fb22 100644 --- a/src/MongoDB.Driver/PipelineDefinition.cs +++ b/src/MongoDB.Driver/PipelineDefinition.cs @@ -86,7 +86,7 @@ public abstract class PipelineDefinition /// A public virtual RenderedPipelineDefinition Render(IBsonSerializer inputSerializer, IBsonSerializerRegistry serializerRegistry) { - return Render(inputSerializer, serializerRegistry, LinqProvider.V2); + return Render(inputSerializer, serializerRegistry, LinqProvider.V3); } /// @@ -101,7 +101,7 @@ public virtual RenderedPipelineDefinition Render(IBsonSerializer public override string ToString() { - return ToString(LinqProvider.V2); + return ToString(LinqProvider.V3); } /// @@ -128,7 +128,7 @@ public string ToString(LinqProvider linqProvider) /// public string ToString(IBsonSerializer inputSerializer, IBsonSerializerRegistry serializerRegistry) { - return ToString(inputSerializer, serializerRegistry, LinqProvider.V2); + return ToString(inputSerializer, serializerRegistry, LinqProvider.V3); } /// @@ -358,9 +358,12 @@ public override RenderedPipelineDefinition Render(IBsonSerializer 0) + foreach (var document in renderedStage.Documents) { - pipeline.Add(renderedStage.Document); + if (document.ElementCount > 0) + { + pipeline.Add(document); + } } } diff --git a/src/MongoDB.Driver/PipelineDefinitionBuilder.cs b/src/MongoDB.Driver/PipelineDefinitionBuilder.cs index 782e14c37f0..a78b3f98af1 100644 --- a/src/MongoDB.Driver/PipelineDefinitionBuilder.cs +++ b/src/MongoDB.Driver/PipelineDefinitionBuilder.cs @@ -21,6 +21,7 @@ using MongoDB.Bson.Serialization; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Linq; +using MongoDB.Driver.Search; namespace MongoDB.Driver { @@ -245,7 +246,7 @@ public static PipelineDefinition> Buck } /// - /// Appends a $bucketAuto stage to the pipeline. + /// Appends a $bucketAuto stage to the pipeline (this overload can only be used with LINQ3). /// /// The type of the input documents. /// The type of the intermediate documents. @@ -264,7 +265,7 @@ public static PipelineDefinition BucketAuto pipeline, Expression> groupBy, int buckets, - Expression, TOutput>> output, + Expression, TIntermediate>, TOutput>> output, AggregateBucketAutoOptions options = null, ExpressionTranslationOptions translationOptions = null) { @@ -272,6 +273,34 @@ public static PipelineDefinition BucketAuto + /// Appends a $bucketAuto stage to the pipeline (this method can only be used with LINQ2). + /// + /// The type of the input documents. + /// The type of the intermediate documents. + /// The type of the value. + /// The type of the output documents. + /// The pipeline. + /// The group by expression. + /// The number of buckets. + /// The output projection. + /// The options (optional). + /// The translation options. + /// + /// The fluent aggregate interface. + /// + public static PipelineDefinition BucketAutoForLinq2( + this PipelineDefinition pipeline, + Expression> groupBy, + int buckets, + Expression, TOutput>> output, // the IGrouping for BucketAuto has been wrong all along, only fixing it for LINQ3 + AggregateBucketAutoOptions options = null, + ExpressionTranslationOptions translationOptions = null) + { + Ensure.IsNotNull(pipeline, nameof(pipeline)); + return pipeline.AppendStage(PipelineStageDefinitionBuilder.BucketAutoForLinq2(groupBy, buckets, output, options, translationOptions)); + } + /// /// Appends a $changeStream stage to the pipeline. /// Normally you would prefer to use the Watch method of . @@ -397,6 +426,44 @@ public static PipelineDefinition Densify( return pipeline.AppendStage(PipelineStageDefinitionBuilder.Densify(field, range, partitionByFields)); } + /// + /// Appends a $documents stage to the pipeline. + /// + /// The type of the documents. + /// The pipeline. + /// The documents. + /// The document serializer. + /// + /// A new pipeline with an additional stage. + /// + public static PipelineDefinition Documents( + this PipelineDefinition pipeline, + AggregateExpressionDefinition> documents, + IBsonSerializer documentSerializer = null) + { + Ensure.IsNotNull(pipeline, nameof(pipeline)); + return pipeline.AppendStage(PipelineStageDefinitionBuilder.Documents(documents, documentSerializer)); + } + + /// + /// Appends a $documents stage to the pipeline. + /// + /// The type of the documents. + /// The pipeline. + /// The documents. + /// The document serializer. + /// + /// A new pipeline with an additional stage. + /// + public static PipelineDefinition Documents( + this PipelineDefinition pipeline, + IEnumerable documents, + IBsonSerializer documentSerializer = null) + { + Ensure.IsNotNull(pipeline, nameof(pipeline)); + return pipeline.AppendStage(PipelineStageDefinitionBuilder.Documents(documents, documentSerializer)); + } + /// /// Appends a $facet stage to the pipeline. /// @@ -717,32 +784,6 @@ public static PipelineDefinition Group - /// Appends a group stage to the pipeline (this method can only be used with LINQ3). - /// - /// The type of the input documents. - /// The type of the intermediate documents. - /// The type of the key. - /// The type of the output documents. - /// The pipeline. - /// The id. - /// The group projection. - /// The translation options. - /// - /// The fluent aggregate interface. - /// - /// This method can only be used with LINQ3 but that can't be verified until Render is called. - public static PipelineDefinition GroupForLinq3( - this PipelineDefinition pipeline, - Expression> id, - Expression, TOutput>> group, - ExpressionTranslationOptions translationOptions = null) - { - Ensure.IsNotNull(pipeline, nameof(pipeline)); - var (groupStage, projectStage) = PipelineStageDefinitionBuilder.GroupForLinq3(id, group, translationOptions); - return pipeline.AppendStage(groupStage).AppendStage(projectStage); - } - /// /// Appends a $limit stage to the pipeline. /// @@ -755,7 +796,7 @@ public static PipelineDefinition GroupForLinq3 public static PipelineDefinition Limit( this PipelineDefinition pipeline, - int limit) + long limit) { Ensure.IsNotNull(pipeline, nameof(pipeline)); return pipeline.AppendStage(PipelineStageDefinitionBuilder.Limit(limit)); @@ -1124,6 +1165,57 @@ public static PipelineDefinition ReplaceWith + /// Appends a $search stage to the pipeline. + /// + /// The type of the input documents. + /// The type of the output documents. + /// The pipeline. + /// The search definition. + /// The highlight options. + /// The index name. + /// The count options. + /// + /// Flag that specifies whether to perform a full document lookup on the backend database + /// or return only stored source fields directly from Atlas Search. + /// + /// + /// A new pipeline with an additional stage. + /// + public static PipelineDefinition Search( + this PipelineDefinition pipeline, + SearchDefinition searchDefinition, + SearchHighlightOptions highlight = null, + string indexName = null, + SearchCountOptions count = null, + bool returnStoredSource = false) + { + Ensure.IsNotNull(pipeline, nameof(pipeline)); + return pipeline.AppendStage(PipelineStageDefinitionBuilder.Search(searchDefinition, highlight, indexName, count, returnStoredSource)); + } + + /// + /// Appends a $searchMeta stage to the pipeline. + /// + /// The type of the input documents. + /// The type of the output documents. + /// The pipeline. + /// The search definition. + /// The index name. + /// The count options. + /// + /// A new pipeline with an additional stage. + /// + public static PipelineDefinition SearchMeta( + this PipelineDefinition pipeline, + SearchDefinition query, + string indexName = null, + SearchCountOptions count = null) + { + Ensure.IsNotNull(pipeline, nameof(pipeline)); + return pipeline.AppendStage(PipelineStageDefinitionBuilder.SearchMeta(query, indexName, count)); + } + /// /// Create a $setWindowFields stage. /// @@ -1261,7 +1353,7 @@ public static PipelineDefinition SetWindowFields public static PipelineDefinition Skip( this PipelineDefinition pipeline, - int skip) + long skip) { Ensure.IsNotNull(pipeline, nameof(pipeline)); return pipeline.AppendStage(PipelineStageDefinitionBuilder.Skip(skip)); @@ -1468,7 +1560,7 @@ public override RenderedPipelineDefinition Render(IBsonSerializer(documents, outputSerializer); } @@ -1544,7 +1636,7 @@ public override RenderedPipelineDefinition Render(IBsonSerializer(documents, outputSerializer); } diff --git a/src/MongoDB.Driver/PipelineStageDefinition.cs b/src/MongoDB.Driver/PipelineStageDefinition.cs index 9c5015d7b76..f1f7388a1e2 100644 --- a/src/MongoDB.Driver/PipelineStageDefinition.cs +++ b/src/MongoDB.Driver/PipelineStageDefinition.cs @@ -14,6 +14,8 @@ */ using System; +using System.Collections.Generic; +using System.Linq; using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Driver.Core.Misc; @@ -39,6 +41,11 @@ public interface IRenderedPipelineStageDefinition /// BsonDocument Document { get; } + /// + /// Gets the documents (usually one but could be more). + /// + IReadOnlyList Documents { get; } + /// /// Gets the output serializer. /// @@ -52,7 +59,7 @@ public interface IRenderedPipelineStageDefinition public class RenderedPipelineStageDefinition : IRenderedPipelineStageDefinition { private string _operatorName; - private BsonDocument _document; + private IReadOnlyList _documents; private IBsonSerializer _outputSerializer; /// @@ -62,16 +69,33 @@ public class RenderedPipelineStageDefinition : IRenderedPipelineStageDe /// The document. /// The output serializer. public RenderedPipelineStageDefinition(string operatorName, BsonDocument document, IBsonSerializer outputSerializer) + : this(operatorName, new List { document }, outputSerializer) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Name of the pipeline operator. + /// The documents. + /// The output serializer. + public RenderedPipelineStageDefinition(string operatorName, IEnumerable documents, IBsonSerializer outputSerializer) { _operatorName = Ensure.IsNotNull(operatorName, nameof(operatorName)); - _document = Ensure.IsNotNull(document, nameof(document)); + _documents = Ensure.IsNotNull(documents, nameof(documents)).ToArray(); _outputSerializer = Ensure.IsNotNull(outputSerializer, nameof(outputSerializer)); } /// public BsonDocument Document { - get { return _document; } + get { return _documents.Single(); } + } + + /// + public IReadOnlyList Documents + { + get { return _documents; } } /// @@ -188,7 +212,7 @@ public Type OutputType /// A public virtual RenderedPipelineStageDefinition Render(IBsonSerializer inputSerializer, IBsonSerializerRegistry serializerRegistry) { - return Render(inputSerializer, serializerRegistry, LinqProvider.V2); + return Render(inputSerializer, serializerRegistry, LinqProvider.V3); } /// @@ -218,7 +242,7 @@ public override string ToString() /// public string ToString(IBsonSerializer inputSerializer, IBsonSerializerRegistry serializerRegistry) { - return ToString(inputSerializer, serializerRegistry, LinqProvider.V2); + return ToString(inputSerializer, serializerRegistry, LinqProvider.V3); } /// @@ -233,7 +257,15 @@ public string ToString(IBsonSerializer inputSerializer, IBsonSerializerR public string ToString(IBsonSerializer inputSerializer, IBsonSerializerRegistry serializerRegistry, LinqProvider linqProvider) { var renderedStage = Render(inputSerializer, serializerRegistry, linqProvider); - return renderedStage.Document.ToJson(); + var documents = renderedStage.Documents; + if (documents.Count == 1) + { + return documents[0].ToJson(); + } + else + { + return new BsonArray(documents).ToJson(); + } } string IPipelineStageDefinition.ToString(IBsonSerializer inputSerializer, IBsonSerializerRegistry serializerRegistry) @@ -283,7 +315,7 @@ public static implicit operator PipelineStageDefinition(string /// IRenderedPipelineStageDefinition IPipelineStageDefinition.Render(IBsonSerializer inputSerializer, IBsonSerializerRegistry serializerRegistry) { - return Render((IBsonSerializer)inputSerializer, serializerRegistry, LinqProvider.V2); + return Render((IBsonSerializer)inputSerializer, serializerRegistry, LinqProvider.V3); } /// diff --git a/src/MongoDB.Driver/PipelineStageDefinitionBuilder.cs b/src/MongoDB.Driver/PipelineStageDefinitionBuilder.cs index 4e5c1338149..49d1b126939 100644 --- a/src/MongoDB.Driver/PipelineStageDefinitionBuilder.cs +++ b/src/MongoDB.Driver/PipelineStageDefinitionBuilder.cs @@ -22,8 +22,10 @@ using MongoDB.Bson.Serialization; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Linq; +using MongoDB.Driver.Linq.Linq3Implementation; using MongoDB.Driver.Linq.Linq3Implementation.Serializers; using MongoDB.Driver.Linq.Linq3Implementation.Translators; +using MongoDB.Driver.Search; namespace MongoDB.Driver { @@ -173,11 +175,7 @@ public static PipelineStageDefinition Bucket(groupBy, translationOptions), - boundaries, - new ExpressionBucketOutputProjection(x => default(TValue), output, translationOptions), - options); + return new BucketWithOutputExpressionStageDefinition(groupBy, boundaries, output, options, translationOptions); } /// @@ -295,7 +293,7 @@ public static PipelineStageDefinition> } /// - /// Creates a $bucketAuto stage. + /// Creates a $bucketAuto stage (this overload can only be used with LINQ3). /// /// The type of the input documents. /// The type of the output documents. @@ -309,7 +307,31 @@ public static PipelineStageDefinition> public static PipelineStageDefinition BucketAuto( Expression> groupBy, int buckets, - Expression, TOutput>> output, + Expression, TInput>, TOutput>> output, + AggregateBucketAutoOptions options = null, + ExpressionTranslationOptions translationOptions = null) + { + Ensure.IsNotNull(groupBy, nameof(groupBy)); + Ensure.IsNotNull(output, nameof(output)); + return new BucketAutoWithOutputExpressionStageDefinition(groupBy, buckets, output, options); + } + + /// + /// Creates a $bucketAuto stage (this method can only be used with LINQ2). + /// + /// The type of the input documents. + /// The type of the output documents. + /// The type of the output documents. + /// The group by expression. + /// The number of buckets. + /// The output projection. + /// The options (optional). + /// The translation options. + /// The stage. + public static PipelineStageDefinition BucketAutoForLinq2( + Expression> groupBy, + int buckets, + Expression, TOutput>> output, // the IGrouping for BucketAuto has been wrong all along, only fixing it for LINQ3 AggregateBucketAutoOptions options = null, ExpressionTranslationOptions translationOptions = null) { @@ -477,6 +499,52 @@ public static PipelineStageDefinition Densify( return Densify(field, range, (IEnumerable>>)partitionByFields); } + /// + /// Creates a $documents stage. + /// + /// The type of the documents. + /// The documents. + /// The document serializer. + /// The stage. + public static PipelineStageDefinition Documents( + AggregateExpressionDefinition> documents, + IBsonSerializer documentSerializer = null) + { + if (typeof(TDocument) == typeof(NoPipelineInput)) + { + throw new ArgumentException("Documents cannot be of type NoPipelineInput.", nameof(documents)); + } + + const string operatorName = "$documents"; + var stage = new DelegatedPipelineStageDefinition( + operatorName, + (s, sr, linqProvider) => + { + var renderedDocuments = documents.Render(NoPipelineInputSerializer.Instance, sr, linqProvider); + return new RenderedPipelineStageDefinition( + operatorName, + new BsonDocument(operatorName, renderedDocuments), + documentSerializer ?? sr.GetSerializer()); + }); + + return stage; + } + + /// + /// Creates a $documents stage. + /// + /// The type of the documents. + /// The documents. + /// The document serializer. + /// The stage. + public static PipelineStageDefinition Documents( + IEnumerable documents, + IBsonSerializer documentSerializer = null) + { + var aggregateExpression = new DocumentsAggregateExpressionDefinition(documents, documentSerializer); + return Documents(aggregateExpression, documentSerializer); + } + /// /// Creates a $facet stage. /// @@ -819,29 +887,7 @@ public static PipelineStageDefinition Group(value, group, translationOptions)); - } - - /// - /// Creates a $group stage (this method can only be used with LINQ3). - /// - /// The type of the input documents. - /// The type of the values. - /// The type of the output documents. - /// The value field. - /// The group projection. - /// The translation options. - /// The stage. - /// This method can only be used with LINQ3 but that can't be verified until Render is called. - public static GroupForLinq3Result GroupForLinq3( - Expression> value, - Expression, TOutput>> group, - ExpressionTranslationOptions translationOptions = null) - { - Ensure.IsNotNull(value, nameof(value)); - Ensure.IsNotNull(group, nameof(group)); - var stages = new Linq.Linq3Implementation.GroupExpressionStageDefinitions(value, group); - return new GroupForLinq3Result(stages.GroupStage, stages.ProjectStage); + return new GroupWithOutputExpressionStageDefinition(value, group); } /// @@ -851,7 +897,7 @@ public static GroupForLinq3Result GroupForLinq3The limit. /// The stage. public static PipelineStageDefinition Limit( - int limit) + long limit) { Ensure.IsGreaterThanZero(limit, nameof(limit)); return new BsonDocumentPipelineStageDefinition(new BsonDocument("$limit", limit)); @@ -1262,6 +1308,80 @@ public static PipelineStageDefinition Project( return Project(new ProjectExpressionProjection(projection, translationOptions)); } + /// + /// Creates a $search stage. + /// + /// The type of the input documents. + /// The search definition. + /// The highlight options. + /// The index name. + /// The count options. + /// + /// Flag that specifies whether to perform a full document lookup on the backend database + /// or return only stored source fields directly from Atlas Search. + /// + /// The stage. + public static PipelineStageDefinition Search( + SearchDefinition searchDefinition, + SearchHighlightOptions highlight = null, + string indexName = null, + SearchCountOptions count = null, + bool returnStoredSource = false) + { + Ensure.IsNotNull(searchDefinition, nameof(searchDefinition)); + + const string operatorName = "$search"; + var stage = new DelegatedPipelineStageDefinition( + operatorName, + (s, sr, linqProvider) => + { + var renderedSearchDefinition = searchDefinition.Render(s, sr); + renderedSearchDefinition.Add("highlight", () => highlight.Render(s, sr), highlight != null); + renderedSearchDefinition.Add("count", () => count.Render(), count != null); + renderedSearchDefinition.Add("index", indexName, indexName != null); + renderedSearchDefinition.Add("returnStoredSource", returnStoredSource, returnStoredSource); + + var document = new BsonDocument(operatorName, renderedSearchDefinition); + return new RenderedPipelineStageDefinition(operatorName, document, s); + }); + + return stage; + } + + /// + /// Creates a $searchMeta stage. + /// + /// The type of the input documents. + /// The search definition. + /// The index name. + /// The count options. + /// The stage. + public static PipelineStageDefinition SearchMeta( + SearchDefinition searchDefinition, + string indexName = null, + SearchCountOptions count = null) + { + Ensure.IsNotNull(searchDefinition, nameof(searchDefinition)); + + const string operatorName = "$searchMeta"; + var stage = new DelegatedPipelineStageDefinition( + operatorName, + (s, sr, linqProvider) => + { + var renderedSearchDefinition = searchDefinition.Render(s, sr); + renderedSearchDefinition.Add("count", () => count.Render(), count != null); + renderedSearchDefinition.Add("index", indexName, indexName != null); + + var document = new BsonDocument(operatorName, renderedSearchDefinition); + return new RenderedPipelineStageDefinition( + operatorName, + document, + sr.GetSerializer()); + }); + + return stage; + } + /// /// Creates a $replaceRoot stage. /// @@ -1532,7 +1652,7 @@ public static PipelineStageDefinition SetWindowFieldsThe skip. /// The stage. public static PipelineStageDefinition Skip( - int skip) + long skip) { Ensure.IsGreaterThanOrEqualToZero(skip, nameof(skip)); return new BsonDocumentPipelineStageDefinition(new BsonDocument("$skip", skip)); @@ -1778,6 +1898,11 @@ public Expression, TOutput>> OutputExpression public override RenderedProjectionDefinition Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry, LinqProvider linqProvider) { + if (linqProvider != LinqProvider.V2) + { + throw new InvalidOperationException("ExpressionBucketOutputProjection can only be used with LINQ2."); + } + return linqProvider.GetAdapter().TranslateExpressionToBucketOutputProjection(_valueExpression, _outputExpression, documentSerializer, serializerRegistry, _translationOptions); } } diff --git a/src/MongoDB.Driver/ProjectionDefinition.cs b/src/MongoDB.Driver/ProjectionDefinition.cs index 70ce6a4e537..4d1c14a88da 100644 --- a/src/MongoDB.Driver/ProjectionDefinition.cs +++ b/src/MongoDB.Driver/ProjectionDefinition.cs @@ -73,7 +73,7 @@ public abstract class ProjectionDefinition /// A . public virtual BsonDocument Render(IBsonSerializer sourceSerializer, IBsonSerializerRegistry serializerRegistry) { - return Render(sourceSerializer, serializerRegistry, LinqProvider.V2); + return Render(sourceSerializer, serializerRegistry, LinqProvider.V3); } /// @@ -135,7 +135,7 @@ public abstract class ProjectionDefinition /// A . public virtual RenderedProjectionDefinition Render(IBsonSerializer sourceSerializer, IBsonSerializerRegistry serializerRegistry) { - return Render(sourceSerializer, serializerRegistry, LinqProvider.V2); + return Render(sourceSerializer, serializerRegistry, LinqProvider.V3); } /// diff --git a/src/MongoDB.Driver/ProjectionDefinitionBuilder.cs b/src/MongoDB.Driver/ProjectionDefinitionBuilder.cs index 42fe10e0c9a..528116cb272 100644 --- a/src/MongoDB.Driver/ProjectionDefinitionBuilder.cs +++ b/src/MongoDB.Driver/ProjectionDefinitionBuilder.cs @@ -156,6 +156,40 @@ public static ProjectionDefinition Meta(this ProjectionDef return builder.Combine(projection, builder.Meta(field, metaFieldName)); } + /// + /// Combines an existing projection with a search highlights projection. + /// + /// The type of the document. + /// The projection. + /// The field. + /// + /// A combined projection. + /// + public static ProjectionDefinition MetaSearchHighlights( + this ProjectionDefinition projection, + string field) + { + var builder = Builders.Projection; + return builder.Combine(projection, builder.MetaSearchHighlights(field)); + } + + /// + /// Combines an existing projection with a search score projection. + /// + /// The type of the document. + /// The projection. + /// The field. + /// + /// A combined projection. + /// + public static ProjectionDefinition MetaSearchScore( + this ProjectionDefinition projection, + string field) + { + var builder = Builders.Projection; + return builder.Combine(projection, builder.MetaSearchScore(field)); + } + /// /// Combines an existing projection with a text score projection. /// @@ -171,6 +205,40 @@ public static ProjectionDefinition MetaTextScore(this Proj return builder.Combine(projection, builder.MetaTextScore(field)); } + /// + /// Combines an existing projection with a search metadata projection. + /// + /// The type of the document. + /// The projection. + /// The field. + /// + /// A combined projection. + /// + public static ProjectionDefinition SearchMeta( + this ProjectionDefinition projection, + FieldDefinition field) + { + var builder = Builders.Projection; + return builder.Combine(projection, builder.SearchMeta(field)); + } + + /// + /// Combines an existing projection with a search metadata projection. + /// + /// The type of the document. + /// The projection. + /// The field. + /// + /// A combined projection. + /// + public static ProjectionDefinition SearchMeta( + this ProjectionDefinition projection, + Expression> field) + { + var builder = Builders.Projection; + return builder.Combine(projection, builder.SearchMeta(field)); + } + /// /// Combines an existing projection with an array slice projection. /// @@ -363,6 +431,30 @@ public ProjectionDefinition Meta(string field, string metaFieldName) return new SingleFieldProjectionDefinition(field, new BsonDocument("$meta", metaFieldName)); } + /// + /// Creates a search highlights projection. + /// + /// The field. + /// + /// A search highlights projection. + /// + public ProjectionDefinition MetaSearchHighlights(string field) + { + return Meta(field, "searchHighlights"); + } + + /// + /// Creates a search score projection. + /// + /// The field. + /// + /// A search score projection. + /// + public ProjectionDefinition MetaSearchScore(string field) + { + return Meta(field, "searchScore"); + } + /// /// Creates a text score projection. /// @@ -375,6 +467,30 @@ public ProjectionDefinition MetaTextScore(string field) return Meta(field, "textScore"); } + /// + /// Creates a search metadata projection. + /// + /// The field. + /// + /// A search metadata projection. + /// + public ProjectionDefinition SearchMeta(FieldDefinition field) + { + return new SingleFieldProjectionDefinition(field, new BsonString("$$SEARCH_META")); + } + + /// + /// Creates a search metadata projection. + /// + /// The field. + /// + /// A search metadata projection. + /// + public ProjectionDefinition SearchMeta(Expression> field) + { + return SearchMeta(new ExpressionFieldDefinition(field)); + } + /// /// Creates an array slice projection. /// diff --git a/src/MongoDB.Driver/Search/CompoundSearchDefinitionBuilder.cs b/src/MongoDB.Driver/Search/CompoundSearchDefinitionBuilder.cs new file mode 100644 index 00000000000..d37516ae84d --- /dev/null +++ b/src/MongoDB.Driver/Search/CompoundSearchDefinitionBuilder.cs @@ -0,0 +1,139 @@ +/* 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.Collections.Generic; +using MongoDB.Driver.Core.Misc; + +namespace MongoDB.Driver.Search +{ + /// + /// A builder for compound search definitions. + /// + /// The type of the document. + public sealed class CompoundSearchDefinitionBuilder + { + private List> _must; + private List> _mustNot; + private List> _should; + private List> _filter; + private int _minimumShouldMatch = 0; + + /// + /// Adds clauses which must match to produce results. + /// + /// The clauses. + /// The compound search definition builder. + public CompoundSearchDefinitionBuilder Must(IEnumerable> clauses) => + AddClauses(ref _must, clauses); + + /// + /// Adds clauses which must match to produce results. + /// + /// The clauses. + /// The compound search definition builder. + public CompoundSearchDefinitionBuilder Must(params SearchDefinition[] clauses) => + Must((IEnumerable>)clauses); + + /// + /// Adds clauses which must not match for a document to be included in the + /// results. + /// + /// The clauses. + /// The compound search definition builder. + public CompoundSearchDefinitionBuilder MustNot(IEnumerable> clauses) => + AddClauses(ref _mustNot, clauses); + + /// + /// Adds clauses which must not match for a document to be included in the + /// results. + /// + /// The clauses. + /// The compound search definition builder. + public CompoundSearchDefinitionBuilder MustNot(params SearchDefinition[] clauses) => + MustNot((IEnumerable>)clauses); + + /// + /// Adds clauses which cause documents in the result set to be scored higher if + /// they match. + /// + /// The clauses. + /// The compound search definition builder. + public CompoundSearchDefinitionBuilder Should(IEnumerable> clauses) => + AddClauses(ref _should, clauses); + + /// + /// Adds clauses which cause documents in the result set to be scored higher if + /// they match. + /// + /// The clauses. + /// The compound search definition builder. + public CompoundSearchDefinitionBuilder Should(params SearchDefinition[] clauses) => + Should((IEnumerable>)clauses); + + /// + /// Adds clauses which must all match for a document to be included in the + /// results. + /// + /// The clauses. + /// The compound search definition builder. + public CompoundSearchDefinitionBuilder Filter(IEnumerable> clauses) => + AddClauses(ref _filter, clauses); + + /// + /// Adds clauses which must all match for a document to be included in the + /// results. + /// + /// The clauses. + /// The compound search definition builder. + public CompoundSearchDefinitionBuilder Filter(params SearchDefinition[] clauses) => + Filter((IEnumerable>)clauses); + + /// + /// Sets a value specifying the minimum number of should clauses the must match + /// to include a document in the results. + /// + /// The value to set. + /// The compound search definition builder. + public CompoundSearchDefinitionBuilder MinimumShouldMatch(int minimumShouldMatch) + { + _minimumShouldMatch = minimumShouldMatch; + return this; + } + + /// + /// Constructs a search definition from the builder. + /// + /// A compound search definition. + public SearchDefinition ToSearchDefinition() => + new CompoundSearchDefinition(_must, _mustNot, _should, _filter, _minimumShouldMatch); + + /// + /// Performs an implicit conversion from a + /// to a . + /// + /// The compound search definition builder. + /// The result of the conversion. + public static implicit operator SearchDefinition(CompoundSearchDefinitionBuilder builder) => + builder.ToSearchDefinition(); + + private CompoundSearchDefinitionBuilder AddClauses(ref List> clauses, IEnumerable> newClauses) + { + Ensure.IsNotNull(newClauses, nameof(newClauses)); + (clauses ??= new()).AddRange(newClauses); + + return this; + } + } +} diff --git a/src/MongoDB.Driver/Search/GeoShapeRelation.cs b/src/MongoDB.Driver/Search/GeoShapeRelation.cs new file mode 100644 index 00000000000..98bab0fec0b --- /dev/null +++ b/src/MongoDB.Driver/Search/GeoShapeRelation.cs @@ -0,0 +1,44 @@ +/* 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. +*/ + +namespace MongoDB.Driver.Search +{ + /// + /// The relation of the query shape geometry to the indexed field geometry in a + /// geo shape search definition. + /// + public enum GeoShapeRelation + { + /// + /// Indicates that the indexed geometry contains the query geometry. + /// + Contains, + + /// + /// Indicates that both the query and indexed geometries have nothing in common. + /// + Disjoint, + + /// + /// Indicates that both the query and indexed geometries intersect. + /// + Intersects, + + /// + /// Indicates that the indexed geometry is within the query geometry. + /// + Within + } +} diff --git a/src/MongoDB.Driver/Search/GeoWithinArea.cs b/src/MongoDB.Driver/Search/GeoWithinArea.cs new file mode 100644 index 00000000000..4622409d474 --- /dev/null +++ b/src/MongoDB.Driver/Search/GeoWithinArea.cs @@ -0,0 +1,113 @@ +/* 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 MongoDB.Bson; +using MongoDB.Driver.Core.Misc; +using MongoDB.Driver.GeoJsonObjectModel; + +namespace MongoDB.Driver.Search +{ + /// + /// Base class for area argument for GeoWithin queries. + /// + /// The type of the coordinates. + public abstract class GeoWithinArea where TCoordinates : GeoJsonCoordinates + { + internal abstract BsonElement Render(); + } + + /// + /// Object that specifies the bottom left and top right GeoJSON points of a box to + /// search within. + /// + /// The type of the coordinates. + public sealed class GeoWithinBox : GeoWithinArea where TCoordinates : GeoJsonCoordinates + { + /// + /// Initializes a new instance of the class. + /// + /// The bottom left GeoJSON point. + /// The top right GeoJSON point. + public GeoWithinBox(GeoJsonPoint bottomLeft, GeoJsonPoint topRight) + { + BottomLeft = Ensure.IsNotNull(bottomLeft, nameof(bottomLeft)); + TopRight = Ensure.IsNotNull(topRight, nameof(topRight)); + } + + /// Gets the bottom left GeoJSON point. + public GeoJsonPoint BottomLeft { get; } + + /// Gets the top right GeoJSON point. + public GeoJsonPoint TopRight { get; } + + internal override BsonElement Render() => + new("box", new BsonDocument + { + { "bottomLeft", BottomLeft.ToBsonDocument() }, + { "topRight", TopRight.ToBsonDocument() } + }); + } + + /// + /// Object that specifies the center point and the radius in meters to search within. + /// + public sealed class GeoWithinCircle : GeoWithinArea where TCoordinates : GeoJsonCoordinates + { + /// + /// Initializes a new instance of the class. + /// + /// Center of the circle specified as a GeoJSON point. + /// Radius specified in meters. + public GeoWithinCircle(GeoJsonPoint center, double radius) + { + Center = Ensure.IsNotNull(center, nameof(center)); + Radius = Ensure.IsGreaterThanZero(radius, nameof(radius)); + } + + /// Gets the center of the circle specified as a GeoJSON point. + public GeoJsonPoint Center { get; } + + /// Gets the radius specified in meters. + public double Radius { get; } + + internal override BsonElement Render() => + new("circle", new BsonDocument + { + { "center", Center.ToBsonDocument() }, + { "radius", Radius } + }); + } + + /// + /// Object that specifies the GeoJson geometry to search within. + /// + /// The type of the coordinates. + public sealed class GeoWithinGeometry : GeoWithinArea where TCoordinates : GeoJsonCoordinates + { + /// + /// Initializes a new instance of the class. + /// + /// GeoJSON object specifying the MultiPolygon or Polygon. + public GeoWithinGeometry(GeoJsonGeometry geometry) + { + Geometry = Ensure.IsNotNull(geometry, nameof(geometry)); + } + + /// Gets the GeoJson geometry. + public GeoJsonGeometry Geometry { get; } + + internal override BsonElement Render() => new("geometry", Geometry.ToBsonDocument()); + } +} diff --git a/src/MongoDB.Driver/Search/OperatorSearchDefinitions.cs b/src/MongoDB.Driver/Search/OperatorSearchDefinitions.cs new file mode 100644 index 00000000000..de3ac5a9b14 --- /dev/null +++ b/src/MongoDB.Driver/Search/OperatorSearchDefinitions.cs @@ -0,0 +1,393 @@ +/* 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 MongoDB.Bson; +using MongoDB.Bson.Serialization; +using MongoDB.Driver.Core.Misc; +using MongoDB.Driver.GeoJsonObjectModel; + +namespace MongoDB.Driver.Search +{ + internal sealed class AutocompleteSearchDefinition : OperatorSearchDefinition + { + private readonly SearchFuzzyOptions _fuzzy; + private readonly SearchQueryDefinition _query; + private readonly SearchAutocompleteTokenOrder _tokenOrder; + + public AutocompleteSearchDefinition( + SearchPathDefinition path, + SearchQueryDefinition query, + SearchAutocompleteTokenOrder tokenOrder, + SearchFuzzyOptions fuzzy, + SearchScoreDefinition score) + : base(OperatorType.Autocomplete, path, score) + { + _query = Ensure.IsNotNull(query, nameof(query)); + _tokenOrder = tokenOrder; + _fuzzy = fuzzy; + } + + private protected override BsonDocument RenderArguments(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) => + new() + { + { "query", _query.Render() }, + { "tokenOrder", _tokenOrder.ToCamelCase(), _tokenOrder != SearchAutocompleteTokenOrder.Any }, + { "fuzzy", () => _fuzzy.Render(), _fuzzy != null }, + }; + } + + internal sealed class CompoundSearchDefinition : OperatorSearchDefinition + { + private readonly List> _filter; + private readonly int _minimumShouldMatch; + private readonly List> _must; + private readonly List> _mustNot; + private readonly List> _should; + + public CompoundSearchDefinition( + List> must, + List> mustNot, + List> should, + List> filter, + int minimumShouldMatch) + : base(OperatorType.Compound) + { + // This constructor should always be called from the compound search definition builder that ensures the arguments are valid. + _must = must; + _mustNot = mustNot; + _should = should; + _filter = filter; + _minimumShouldMatch = minimumShouldMatch; + } + + private protected override BsonDocument RenderArguments(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) + { + return new() + { + { "must", Render(_must), _must != null }, + { "mustNot", Render(_mustNot), _mustNot != null }, + { "should", Render(_should), _should != null }, + { "filter", Render(_filter), _filter != null }, + { "minimumShouldMatch", _minimumShouldMatch, _minimumShouldMatch > 0 }, + }; + + Func Render(List> searchDefinitions) => + () => new BsonArray(searchDefinitions.Select(clause => clause.Render(documentSerializer, serializerRegistry))); + } + } + + internal sealed class EqualsSearchDefinition : OperatorSearchDefinition + { + private readonly BsonValue _value; + + public EqualsSearchDefinition(FieldDefinition path, BsonValue value, SearchScoreDefinition score) + : base(OperatorType.Equals, path, score) + { + _value = value; + } + + private protected override BsonDocument RenderArguments(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) => + new("value", _value); + } + + internal sealed class ExistsSearchDefinition : OperatorSearchDefinition + { + public ExistsSearchDefinition(FieldDefinition path) + : base(OperatorType.Exists, path, null) + { + } + } + + internal sealed class FacetSearchDefinition : OperatorSearchDefinition + { + private readonly SearchFacet[] _facets; + private readonly SearchDefinition _operator; + + public FacetSearchDefinition(SearchDefinition @operator, IEnumerable> facets) + : base(OperatorType.Facet) + { + _operator = Ensure.IsNotNull(@operator, nameof(@operator)); + _facets = Ensure.IsNotNull(facets, nameof(facets)).ToArray(); + } + + private protected override BsonDocument RenderArguments(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) => + new() + { + { "operator", _operator.Render(documentSerializer, serializerRegistry) }, + { "facets", new BsonDocument(_facets.Select(f => new BsonElement(f.Name, f.Render(documentSerializer, serializerRegistry)))) } + }; + } + + internal sealed class GeoShapeSearchDefinition : OperatorSearchDefinition + where TCoordinates : GeoJsonCoordinates + { + private readonly GeoJsonGeometry _geometry; + private readonly GeoShapeRelation _relation; + + public GeoShapeSearchDefinition( + SearchPathDefinition path, + GeoShapeRelation relation, + GeoJsonGeometry geometry, + SearchScoreDefinition score) + : base(OperatorType.GeoShape, path, score) + { + _geometry = Ensure.IsNotNull(geometry, nameof(geometry)); + _relation = relation; + } + + private protected override BsonDocument RenderArguments(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) => + new() + { + { "geometry", _geometry.ToBsonDocument() }, + { "relation", _relation.ToCamelCase() } + }; + } + + internal sealed class GeoWithinSearchDefinition : OperatorSearchDefinition + where TCoordinates : GeoJsonCoordinates + { + private readonly GeoWithinArea _area; + + public GeoWithinSearchDefinition( + SearchPathDefinition path, + GeoWithinArea area, + SearchScoreDefinition score) + : base(OperatorType.GeoWithin, path, score) + { + _area = Ensure.IsNotNull(area, nameof(area)); + } + + private protected override BsonDocument RenderArguments(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) => + new(_area.Render()); + } + + internal sealed class MoreLikeThisSearchDefinition : OperatorSearchDefinition + { + private readonly TLike[] _like; + + public MoreLikeThisSearchDefinition(IEnumerable like) + : base(OperatorType.MoreLikeThis) + { + _like = Ensure.IsNotNull(like, nameof(like)).ToArray(); + } + + private protected override BsonDocument RenderArguments(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) + { + var likeSerializer = typeof(TLike) switch + { + var t when t == typeof(BsonDocument) => null, + var t when t == typeof(TDocument) => (IBsonSerializer)documentSerializer, + _ => serializerRegistry.GetSerializer() + }; + + return new("like", new BsonArray(_like.Select(document => document.ToBsonDocument(likeSerializer)))); + } + } + + internal sealed class NearSearchDefinition : OperatorSearchDefinition + { + private readonly BsonValue _origin; + private readonly BsonValue _pivot; + + public NearSearchDefinition( + SearchPathDefinition path, + BsonValue origin, + BsonValue pivot, + SearchScoreDefinition score = null) + : base(OperatorType.Near, path, score) + { + _origin = origin; + _pivot = pivot; + } + + private protected override BsonDocument RenderArguments(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) => + new() + { + { "origin", _origin }, + { "pivot", _pivot } + }; + } + + internal sealed class PhraseSearchDefinition : OperatorSearchDefinition + { + private readonly SearchQueryDefinition _query; + private readonly int? _slop; + + public PhraseSearchDefinition( + SearchPathDefinition path, + SearchQueryDefinition query, + int? slop, + SearchScoreDefinition score) + : base(OperatorType.Phrase, path, score) + { + _query = Ensure.IsNotNull(query, nameof(query)); + _slop = slop; + } + + private protected override BsonDocument RenderArguments(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) => + new() + { + { "query", _query.Render() }, + { "slop", _slop, _slop != null } + }; + } + + internal sealed class QueryStringSearchDefinition : OperatorSearchDefinition + { + private readonly FieldDefinition _defaultPath; + private readonly string _query; + + public QueryStringSearchDefinition(FieldDefinition defaultPath, string query, SearchScoreDefinition score) + : base(OperatorType.QueryString, score) + { + _defaultPath = Ensure.IsNotNull(defaultPath, nameof(defaultPath)); + _query = Ensure.IsNotNull(query, nameof(query)); + } + + private protected override BsonDocument RenderArguments(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) => + new() + { + { "defaultPath", _defaultPath.Render(documentSerializer, serializerRegistry).FieldName }, + { "query", _query } + }; + } + + internal sealed class RangeSearchDefinition : OperatorSearchDefinition + where TField : struct, IComparable + { + private readonly SearchRange _range; + + public RangeSearchDefinition( + SearchPathDefinition path, + SearchRange range, + SearchScoreDefinition score) + : base(OperatorType.Range, path, score) + { + _range = range; + } + + private protected override BsonDocument RenderArguments(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) => + new() + { + { _range.IsMinInclusive ? "gte" : "gt", () => ToBsonValue(_range.Min.Value), _range.Min != null }, + { _range.IsMaxInclusive ? "lte" : "lt", () => ToBsonValue(_range.Max.Value), _range.Max != null }, + }; + + private static BsonValue ToBsonValue(TField value) => + value switch + { + sbyte v => (BsonInt32)v, + byte v => (BsonInt32)v, + short v => (BsonInt32)v, + ushort v => (BsonInt32)v, + int v => (BsonInt32)v, + uint v => (BsonInt32)v, + long v => (BsonInt64)v, + float v => (BsonDouble)v, + double v => (BsonDouble)v, + DateTime v => (BsonDateTime)v, + _ => throw new InvalidCastException() + }; + } + + internal sealed class RegexSearchDefinition : OperatorSearchDefinition + { + private readonly bool _allowAnalyzedField; + private readonly SearchQueryDefinition _query; + + public RegexSearchDefinition( + SearchPathDefinition path, + SearchQueryDefinition query, + bool allowAnalyzedField, + SearchScoreDefinition score) + : base(OperatorType.Regex, path, score) + { + _query = Ensure.IsNotNull(query, nameof(query)); + _allowAnalyzedField = allowAnalyzedField; + } + + private protected override BsonDocument RenderArguments(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) => + new() + { + { "query", _query.Render() }, + { "allowAnalyzedField", _allowAnalyzedField, _allowAnalyzedField }, + }; + } + + internal sealed class SpanSearchDefinition : OperatorSearchDefinition + { + private readonly SearchSpanDefinition _clause; + + public SpanSearchDefinition(SearchSpanDefinition clause) + : base(OperatorType.Span) + { + _clause = Ensure.IsNotNull(clause, nameof(clause)); + } + + private protected override BsonDocument RenderArguments(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) => + _clause.Render(documentSerializer, serializerRegistry); + } + + internal sealed class TextSearchDefinition : OperatorSearchDefinition + { + private readonly SearchFuzzyOptions _fuzzy; + private readonly SearchQueryDefinition _query; + + public TextSearchDefinition( + SearchPathDefinition path, + SearchQueryDefinition query, + SearchFuzzyOptions fuzzy, + SearchScoreDefinition score) + : base(OperatorType.Text, path, score) + { + _query = Ensure.IsNotNull(query, nameof(query)); + _fuzzy = fuzzy; + } + + private protected override BsonDocument RenderArguments(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) => + new() + { + { "query", _query.Render() }, + { "fuzzy", () => _fuzzy.Render(), _fuzzy != null }, + }; + } + + internal sealed class WildcardSearchDefinition : OperatorSearchDefinition + { + private readonly bool _allowAnalyzedField; + private readonly SearchQueryDefinition _query; + + public WildcardSearchDefinition( + SearchPathDefinition path, + SearchQueryDefinition query, + bool allowAnalyzedField, + SearchScoreDefinition score) + : base(OperatorType.Wildcard, path, score) + { + _query = Ensure.IsNotNull(query, nameof(query)); + _allowAnalyzedField = allowAnalyzedField; + } + + private protected override BsonDocument RenderArguments(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) => + new() + { + { "query", _query.Render() }, + { "allowAnalyzedField", _allowAnalyzedField, _allowAnalyzedField }, + }; + } +} diff --git a/src/MongoDB.Driver/Search/SearchAutocompleteTokenOrder.cs b/src/MongoDB.Driver/Search/SearchAutocompleteTokenOrder.cs new file mode 100644 index 00000000000..780698e2d93 --- /dev/null +++ b/src/MongoDB.Driver/Search/SearchAutocompleteTokenOrder.cs @@ -0,0 +1,34 @@ +/* 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. +*/ + +namespace MongoDB.Driver.Search +{ + /// + /// The order in which to search for tokens in an autocomplete search definition. + /// + public enum SearchAutocompleteTokenOrder + { + /// + /// Indicates that tokens in the query can appear in any order in the documents. + /// + Any, + + /// + /// Indicates that tokens in the query must appear adjacent to each other or in the order + /// specified in the query in the documents. + /// + Sequential + } +} diff --git a/src/MongoDB.Driver/Search/SearchCountOptions.cs b/src/MongoDB.Driver/Search/SearchCountOptions.cs new file mode 100644 index 00000000000..c3624c9175b --- /dev/null +++ b/src/MongoDB.Driver/Search/SearchCountOptions.cs @@ -0,0 +1,55 @@ +/* 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 MongoDB.Bson; +using MongoDB.Driver.Core.Misc; + +namespace MongoDB.Driver.Search +{ + /// + /// Options for counting the search results. + /// + public sealed class SearchCountOptions + { + private int? _threshold; + private SearchCountType _type = SearchCountType.LowerBound; + + /// + /// Gets or sets the number of documents to include in the exact count if + /// is . + /// + public int? Threshold + { + get => _threshold; + set => _threshold = Ensure.IsNullOrGreaterThanZero(value, nameof(value)); + } + + /// + /// Gets or sets the type of count of the documents in the result set. + /// + public SearchCountType Type + { + get => _type; + set => _type = value; + } + + internal BsonDocument Render() => + new() + { + { "type", _type.ToCamelCase(), _type != SearchCountType.LowerBound }, + { "threshold", _threshold, _threshold != null } + }; + } +} diff --git a/src/MongoDB.Driver/Search/SearchCountType.cs b/src/MongoDB.Driver/Search/SearchCountType.cs new file mode 100644 index 00000000000..f4a87047eae --- /dev/null +++ b/src/MongoDB.Driver/Search/SearchCountType.cs @@ -0,0 +1,33 @@ +/* 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. +*/ + +namespace MongoDB.Driver.Search +{ + /// + /// The type of count of the documents in a search result set. + /// + public enum SearchCountType + { + /// + /// A lower bound count of the number of documents that match the query. + /// + LowerBound, + + /// + /// An exact count of the number of documents that match the query. + /// + Total + } +} diff --git a/src/MongoDB.Driver/Search/SearchDefinition.cs b/src/MongoDB.Driver/Search/SearchDefinition.cs new file mode 100644 index 00000000000..a8bc26686e4 --- /dev/null +++ b/src/MongoDB.Driver/Search/SearchDefinition.cs @@ -0,0 +1,166 @@ +/* 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 MongoDB.Bson; +using MongoDB.Bson.Serialization; +using MongoDB.Driver.Core.Misc; + +namespace MongoDB.Driver.Search +{ + /// + /// Base class for search definitions. + /// + /// The type of the document. + public abstract class SearchDefinition + { + /// + /// Renders the search definition to a . + /// + /// The document serializer. + /// The serializer registry. + /// A . + public abstract BsonDocument Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry); + + /// + /// Performs an implicit conversion from a BSON document to a . + /// + /// The BSON document specifying the search definition. + /// + /// The result of the conversion. + /// + public static implicit operator SearchDefinition(BsonDocument document) => + document != null ? new BsonDocumentSearchDefinition(document) : null; + + /// + /// Performs an implicit conversion from a string to a . + /// + /// The string specifying the search definition in JSON. + /// + /// The result of the conversion. + /// + public static implicit operator SearchDefinition(string json) => + json != null ? new JsonSearchDefinition(json) : null; + } + + /// + /// A search definition based on a BSON document. + /// + /// The type of the document. + public sealed class BsonDocumentSearchDefinition : SearchDefinition + { + /// + /// Initializes a new instance of the class. + /// + /// The BSON document specifying the search definition. + public BsonDocumentSearchDefinition(BsonDocument document) + { + Document = Ensure.IsNotNull(document, nameof(document)); + } + + /// + /// Gets the BSON document. + /// + public BsonDocument Document { get; private set; } + + /// + public override BsonDocument Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) => + Document; + } + + /// + /// A search definition based on a JSON string. + /// + /// The type of the document. + public sealed class JsonSearchDefinition : SearchDefinition + { + /// + /// Initializes a new instance of the class. + /// + /// The JSON string specifying the search definition. + public JsonSearchDefinition(string json) + { + Json = Ensure.IsNotNullOrEmpty(json, nameof(json)); + } + + /// + /// Gets the JSON string. + /// + public string Json { get; private set; } + + /// + public override BsonDocument Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) => + BsonDocument.Parse(Json); + } + + internal abstract class OperatorSearchDefinition : SearchDefinition + { + private protected enum OperatorType + { + Autocomplete, + Compound, + EmbeddedDocument, + Equals, + Exists, + Facet, + GeoShape, + GeoWithin, + MoreLikeThis, + Near, + Phrase, + QueryString, + Range, + Regex, + Search, + Span, + Term, + Text, + Wildcard + } + + private readonly OperatorType _operatorType; + // _path and _score used by many but not all subclasses + private readonly SearchPathDefinition _path; + private readonly SearchScoreDefinition _score; + + private protected OperatorSearchDefinition(OperatorType operatorType) + : this(operatorType, null) + { + } + + private protected OperatorSearchDefinition(OperatorType operatorType, SearchScoreDefinition score) + { + _operatorType = operatorType; + _score = score; + } + + private protected OperatorSearchDefinition(OperatorType operatorType, SearchPathDefinition path, SearchScoreDefinition score) + { + _operatorType = operatorType; + _path = Ensure.IsNotNull(path, nameof(path)); + _score = score; + } + + public sealed override BsonDocument Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) + { + var renderedArgs = RenderArguments(documentSerializer, serializerRegistry); + renderedArgs.Add("path", () => _path.Render(documentSerializer, serializerRegistry), _path != null); + renderedArgs.Add("score", () => _score.Render(documentSerializer, serializerRegistry), _score != null); + + return new(_operatorType.ToCamelCase(), renderedArgs); + } + + private protected virtual BsonDocument RenderArguments(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) => new(); + } +} diff --git a/src/MongoDB.Driver/Search/SearchDefinitionBuilder.cs b/src/MongoDB.Driver/Search/SearchDefinitionBuilder.cs new file mode 100644 index 00000000000..b7c3fb8cf2d --- /dev/null +++ b/src/MongoDB.Driver/Search/SearchDefinitionBuilder.cs @@ -0,0 +1,668 @@ +/* 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.Expressions; +using MongoDB.Bson; +using MongoDB.Driver.GeoJsonObjectModel; + +namespace MongoDB.Driver.Search +{ + /// + /// A builder for a search definition. + /// + /// The type of the document. + public sealed class SearchDefinitionBuilder + { + /// + /// Creates a search definition that performs a search for a word or phrase that contains + /// a sequence of characters from an incomplete input string. + /// + /// The indexed field to search. + /// The query definition specifying the string or strings to search for. + /// The order in which to search for tokens. + /// The options for fuzzy search. + /// The score modifier. + /// An autocomplete search definition. + public SearchDefinition Autocomplete( + SearchPathDefinition path, + SearchQueryDefinition query, + SearchAutocompleteTokenOrder tokenOrder = SearchAutocompleteTokenOrder.Any, + SearchFuzzyOptions fuzzy = null, + SearchScoreDefinition score = null) => + new AutocompleteSearchDefinition(path, query, tokenOrder, fuzzy, score); + + /// + /// Creates a search definition that performs a search for a word or phrase that contains + /// a sequence of characters from an incomplete search string. + /// + /// The type of the field. + /// The indexed field to search. + /// The query definition specifying the string or strings to search for. + /// The order in which to search for tokens. + /// The options for fuzzy search. + /// The score modifier. + /// An autocomplete search definition. + public SearchDefinition Autocomplete( + Expression> path, + SearchQueryDefinition query, + SearchAutocompleteTokenOrder tokenOrder = SearchAutocompleteTokenOrder.Any, + SearchFuzzyOptions fuzzy = null, + SearchScoreDefinition score = null) => + Autocomplete(new ExpressionFieldDefinition(path), query, tokenOrder, fuzzy, score); + + /// + /// Creates a builder for a compound search definition. + /// + /// + public CompoundSearchDefinitionBuilder Compound() => new CompoundSearchDefinitionBuilder(); + + /// + /// Creates a search definition that queries for documents where an indexed field is equal + /// to the specified value. + /// + /// The indexed field to search. + /// The value to query for. + /// The score modifier. + /// An equality search definition. + public SearchDefinition Equals( + FieldDefinition path, + bool value, + SearchScoreDefinition score = null) => + new EqualsSearchDefinition(path, value, score); + + /// + /// Creates a search definition that queries for documents where an indexed field is equal + /// to the specified value. + /// + /// The indexed field to search. + /// The value to query for. + /// The score modifier. + /// An equality search definition. + public SearchDefinition Equals( + FieldDefinition path, + ObjectId value, + SearchScoreDefinition score = null) => + new EqualsSearchDefinition(path, value, score); + + /// + /// Creates a search definition that queries for documents where an indexed field is equal + /// to the specified value. + /// + /// The indexed field to search. + /// The value to query for. + /// The score modifier. + /// An equality search definition. + public SearchDefinition Equals( + Expression> path, + bool value, + SearchScoreDefinition score = null) => + Equals(new ExpressionFieldDefinition(path), value, score); + + /// + /// Creates a search definition that queries for documents where an indexed field is equal + /// to the specified value. + /// + /// The indexed field to search. + /// The value to query for. + /// The score modifier. + /// An equality search definition. + public SearchDefinition Equals( + Expression> path, + ObjectId value, + SearchScoreDefinition score = null) => + Equals(new ExpressionFieldDefinition(path), value, score); + + /// + /// Creates a search definition that tests if a path to a specified indexed field name + /// exists in a document. + /// + /// The field to test for. + /// An existence search definition. + public SearchDefinition Exists(FieldDefinition path) => + new ExistsSearchDefinition(path); + + /// + /// Creates a search definition that tests if a path to a specified indexed field name + /// exists in a document. + /// + /// The type of the field. + /// The field to test for. + /// An existence search definition. + public SearchDefinition Exists(Expression> path) => + Exists(new ExpressionFieldDefinition(path)); + + /// + /// Creates a search definition that groups results by values or ranges in the specified + /// faceted fields and returns the count for each of those groups. + /// + /// The operator to use to perform the facet over. + /// Information for bucketing the data for each facet. + /// A facet search definition. + public SearchDefinition Facet( + SearchDefinition @operator, + IEnumerable> facets) => + new FacetSearchDefinition(@operator, facets); + + /// + /// Creates a search definition that groups results by values or ranges in the specified + /// faceted fields and returns the count for each of those groups. + /// + /// The operator to use to perform the facet over. + /// Information for bucketing the data for each facet. + /// A facet search definition. + public SearchDefinition Facet( + SearchDefinition @operator, + params SearchFacet[] facets) => + Facet(@operator, (IEnumerable>)facets); + + /// + /// Creates a search definition that queries for shapes with a given geometry. + /// + /// The type of the coordinates. + /// Indexed geo type field or fields to search. + /// + /// + /// GeoJSON object specifying the Polygon, MultiPolygon, or LineString shape or point + /// to search. + /// + /// Relation of the query shape geometry to the indexed field geometry. + /// + /// The score modifier. + /// A geo shape search definition. + public SearchDefinition GeoShape( + SearchPathDefinition path, + GeoShapeRelation relation, + GeoJsonGeometry geometry, + SearchScoreDefinition score = null) + where TCoordinates : GeoJsonCoordinates => + new GeoShapeSearchDefinition(path, relation, geometry, score); + + /// + /// Creates a search definition that queries for shapes with a given geometry. + /// + /// The type of the coordinates. + /// The type of the field. + /// Indexed geo type field or fields to search. + /// + /// + /// GeoJSON object specifying the Polygon, MultiPolygon, or LineString shape or point + /// to search. + /// + /// Relation of the query shape geometry to the indexed field geometry. + /// + /// The score modifier. + /// A geo shape search definition. + public SearchDefinition GeoShape( + Expression> path, + GeoShapeRelation relation, + GeoJsonGeometry geometry, + SearchScoreDefinition score = null) + where TCoordinates : GeoJsonCoordinates => + GeoShape( + new ExpressionFieldDefinition(path), + relation, + geometry, + score); + + /// + /// Creates a search definition that queries for geographic points within a given + /// geometry. + /// + /// The type of the coordinates. + /// Indexed geo type field or fields to search. + /// + /// GeoJSON object specifying the MultiPolygon or Polygon to search within. + /// + /// The score modifier. + /// A geo within search definition. + public SearchDefinition GeoWithin( + SearchPathDefinition path, + GeoJsonGeometry geometry, + SearchScoreDefinition score = null) + where TCoordinates : GeoJsonCoordinates => + GeoWithin(path, new GeoWithinGeometry(geometry), score); + + /// + /// Creates a search definition that queries for geographic points within a given + /// geometry. + /// + /// The type of the coordinates. + /// The type of the field. + /// Indexed geo type field or fields to search. + /// + /// GeoJSON object specifying the MultiPolygon or Polygon to search within. + /// + /// The score modifier. + /// A geo within search definition. + public SearchDefinition GeoWithin( + Expression> path, + GeoJsonGeometry geometry, + SearchScoreDefinition score = null) + where TCoordinates : GeoJsonCoordinates => + GeoWithin(path, new GeoWithinGeometry(geometry), score); + + /// + /// Creates a search definition that queries for geographic points within a given geo object. + /// + /// The type of the coordinates. + /// The type of the field. + /// Indexed geo type field or fields to search. + /// Object that specifies the area to search within. + /// The score modifier. + /// A geo within search definition. + public SearchDefinition GeoWithin( + Expression> path, + GeoWithinArea area, + SearchScoreDefinition score = null) + where TCoordinates : GeoJsonCoordinates => + GeoWithin(new ExpressionFieldDefinition(path), area, score); + + /// + /// Creates a search definition that queries for geographic points within a given geo object. + /// + /// The type of the coordinates. + /// Indexed geo type field or fields to search. + /// Object that specifies the area to search within. + /// The score modifier. + /// A geo within search definition. + public SearchDefinition GeoWithin( + SearchPathDefinition path, + GeoWithinArea area, + SearchScoreDefinition score = null) + where TCoordinates : GeoJsonCoordinates => + new GeoWithinSearchDefinition(path, area, score); + + /// + /// Creates a search definition that returns documents similar to the input documents. + /// + /// The type of the like documents. + /// + /// One or more documents that Atlas Search uses to extract representative terms for. + /// + /// A more like this search definition. + public SearchDefinition MoreLikeThis(IEnumerable like) => + new MoreLikeThisSearchDefinition(like); + + /// + /// Creates a search definition that returns documents similar to the input documents. + /// + /// The type of the like documents. + /// + /// One or more documents that Atlas Search uses to extract representative terms for. + /// + /// A more like this search definition. + public SearchDefinition MoreLikeThis(params TLike[] like) => + MoreLikeThis((IEnumerable)like); + + /// + /// Creates a search definition that supports querying and scoring numeric and date values. + /// + /// The indexed field or fields to search. + /// The number, date, or geographic point to search near. + /// The value to use to calculate scores of result documents. + /// The score modifier. + /// A near search definition. + public SearchDefinition Near( + SearchPathDefinition path, + double origin, + double pivot, + SearchScoreDefinition score = null) => + new NearSearchDefinition(path, origin, pivot, score); + + /// + /// Creates a search definition that supports querying and scoring numeric and date values. + /// + /// The type of the field. + /// The indexed field or fields to search. + /// The number, date, or geographic point to search near. + /// The value to use to calculate scores of result documents. + /// The score modifier. + /// A near search definition. + public SearchDefinition Near( + Expression> path, + double origin, + double pivot, + SearchScoreDefinition score = null) => + Near(new ExpressionFieldDefinition(path), origin, pivot, score); + + /// + /// Creates a search definition that supports querying and scoring numeric and date values. + /// + /// The indexed field or fields to search. + /// The number, date, or geographic point to search near. + /// The value to use to calculate scores of result documents. + /// The score modifier. + /// A near search definition. + public SearchDefinition Near( + SearchPathDefinition path, + int origin, + int pivot, + SearchScoreDefinition score = null) => + new NearSearchDefinition(path, new BsonInt32(origin), new BsonInt32(pivot), score); + + /// + /// Creates a search definition that supports querying and scoring numeric and date values. + /// + /// The type of the field. + /// The indexed field or fields to search. + /// The number, date, or geographic point to search near. + /// The value to use to calculate scores of result documents. + /// The score modifier. + /// A near search definition. + public SearchDefinition Near( + Expression> path, + int origin, + int pivot, + SearchScoreDefinition score = null) => + Near(new ExpressionFieldDefinition(path), origin, pivot, score); + + /// + /// Creates a search definition that supports querying and scoring numeric and date values. + /// + /// The indexed field or fields to search. + /// The number, date, or geographic point to search near. + /// The value to use to calculate scores of result documents. + /// The score modifier. + /// A near search definition. + public SearchDefinition Near( + SearchPathDefinition path, + long origin, + long pivot, + SearchScoreDefinition score = null) => + new NearSearchDefinition(path, new BsonInt64(origin), new BsonInt64(pivot), score); + + /// + /// Creates a search definition that supports querying and scoring numeric and date values. + /// + /// The type of the field. + /// The indexed field or fields to search. + /// The number, date, or geographic point to search near. + /// The value to use to calculate scores of result documents. + /// The score modifier. + /// A near search definition. + public SearchDefinition Near( + Expression> path, + long origin, + long pivot, + SearchScoreDefinition score = null) => + Near(new ExpressionFieldDefinition(path), origin, pivot, score); + + /// + /// Creates a search definition that supports querying and scoring numeric and date values. + /// + /// The indexed field or fields to search. + /// The number, date, or geographic point to search near. + /// The value to use to calculate scores of result documents. + /// The score modifier. + /// A near search definition. + public SearchDefinition Near( + SearchPathDefinition path, + DateTime origin, + long pivot, + SearchScoreDefinition score = null) => + new NearSearchDefinition(path, new BsonDateTime(origin), new BsonInt64(pivot), score); + + /// + /// Creates a search definition that supports querying and scoring numeric and date values. + /// + /// The type of the field. + /// The indexed field or fields to search. + /// The number, date, or geographic point to search near. + /// The value to use to calculate scores of result documents. + /// The score modifier. + /// A near search definition. + public SearchDefinition Near( + Expression> path, + DateTime origin, + long pivot, + SearchScoreDefinition score = null) => + Near(new ExpressionFieldDefinition(path), origin, pivot, score); + + /// + /// Creates a search definition that supports querying and scoring numeric and date values. + /// + /// The type of the coordinates. + /// The indexed field or fields to search. + /// The number, date, or geographic point to search near. + /// The value to use to calculate scores of result documents. + /// The score modifier. + /// A near search definition. + public SearchDefinition Near( + SearchPathDefinition path, + GeoJsonPoint origin, + double pivot, + SearchScoreDefinition score = null) + where TCoordinates : GeoJsonCoordinates => + new NearSearchDefinition(path, origin.ToBsonDocument(), pivot, score); + + /// + /// Creates a search definition that supports querying and scoring numeric and date values. + /// + /// The type of the coordinates + /// The type of the fields. + /// The indexed field or fields to search. + /// The number, date, or geographic point to search near. + /// The value to user to calculate scores of result documents. + /// The score modifier. + /// A near search definition. + public SearchDefinition Near( + Expression> path, + GeoJsonPoint origin, + double pivot, + SearchScoreDefinition score = null) + where TCoordinates : GeoJsonCoordinates => + Near(new ExpressionFieldDefinition(path), origin, pivot, score); + + /// + /// Creates a search definition that performs search for documents containing an ordered + /// sequence of terms. + /// + /// The indexed field or fields to search. + /// The string or strings to search for. + /// The allowable distance between words in the query phrase. + /// The score modifier. + /// A phrase search definition. + public SearchDefinition Phrase( + SearchPathDefinition path, + SearchQueryDefinition query, + int? slop = null, + SearchScoreDefinition score = null) => + new PhraseSearchDefinition(path, query, slop, score); + + /// + /// Creates a search definition that performs search for documents containing an ordered + /// sequence of terms. + /// + /// The type of the field. + /// The indexed field or fields to search. + /// The string or strings to search for. + /// The allowable distance between words in the query phrase. + /// The score modifier. + /// A phrase search definition. + public SearchDefinition Phrase( + Expression> path, + SearchQueryDefinition query, + int? slop = null, + SearchScoreDefinition score = null) => + Phrase(new ExpressionFieldDefinition(path), query, slop, score); + + /// + /// Creates a search definition that queries a combination of indexed fields and values. + /// + /// The indexed field to search by default. + /// One or more indexed fields and values to search. + /// The score modifier. + /// A query string search definition. + public SearchDefinition QueryString( + FieldDefinition defaultPath, + string query, + SearchScoreDefinition score = null) => + new QueryStringSearchDefinition(defaultPath, query, score); + + /// + /// Creates a search definition that queries a combination of indexed fields and values. + /// + /// The type of the field. + /// The indexed field to search by default. + /// One or more indexed fields and values to search. + /// The score modifier. + /// A query string search definition. + public SearchDefinition QueryString( + Expression> defaultPath, + string query, + SearchScoreDefinition score = null) => + QueryString(new ExpressionFieldDefinition(defaultPath), query, score); + + /// + /// Creates a search definition that queries for documents where a floating-point + /// field is in the specified range. + /// + /// A fluent range interface. + public SearchDefinition Range( + Expression> path, + SearchRange range, + SearchScoreDefinition score = null) + where TField : struct, IComparable => + Range(new ExpressionFieldDefinition(path), range, score); + + /// + /// Creates a search definition that queries for documents where a floating-point + /// field is in the specified range. + /// + /// A fluent range interface. + public SearchDefinition Range( + SearchPathDefinition path, + SearchRange range, + SearchScoreDefinition score = null) + where TField : struct, IComparable => + new RangeSearchDefinition(path, range, score); + + /// + /// Creates a search definition that interprets the query as a regular expression. + /// + /// The indexed field or fields to search. + /// The string or strings to search for. + /// + /// Must be set to true if the query is run against an analyzed field. + /// + /// The score modifier. + /// A regular expression search definition. + public SearchDefinition Regex( + SearchPathDefinition path, + SearchQueryDefinition query, + bool allowAnalyzedField = false, + SearchScoreDefinition score = null) => + new RegexSearchDefinition(path, query, allowAnalyzedField, score); + + /// + /// Creates a search definition that interprets the query as a regular expression. + /// + /// The type of the field. + /// The indexed field or fields to search. + /// The string or strings to search for. + /// + /// Must be set to true if the query is run against an analyzed field. + /// + /// The score modifier. + /// A regular expression search definition. + public SearchDefinition Regex( + Expression> path, + SearchQueryDefinition query, + bool allowAnalyzedField = false, + SearchScoreDefinition score = null) => + Regex(new ExpressionFieldDefinition(path), query, allowAnalyzedField, score); + + /// + /// Creates a search definition that finds text search matches within regions of a text + /// field. + /// + /// The span clause. + /// A span search definition. + public SearchDefinition Span(SearchSpanDefinition clause) => + new SpanSearchDefinition(clause); + + /// + /// Creates a search definition that performs full-text search using the analyzer specified + /// in the index configuration. + /// + /// The indexed field or fields to search. + /// The string or strings to search for. + /// The options for fuzzy search. + /// The score modifier. + /// A text search definition. + public SearchDefinition Text( + SearchPathDefinition path, + SearchQueryDefinition query, + SearchFuzzyOptions fuzzy = null, + SearchScoreDefinition score = null) => + new TextSearchDefinition(path, query, fuzzy, score); + + /// + /// Creates a search definition that performs full-text search using the analyzer specified + /// in the index configuration. + /// + /// The type of the field. + /// The indexed field or field to search. + /// The string or strings to search for. + /// The options for fuzzy search. + /// The score modifier. + /// A text search definition. + public SearchDefinition Text( + Expression> path, + SearchQueryDefinition query, + SearchFuzzyOptions fuzzy = null, + SearchScoreDefinition score = null) => + Text(new ExpressionFieldDefinition(path), query, fuzzy, score); + + /// + /// Creates a search definition that uses special characters in the search string that can + /// match any character. + /// + /// The indexed field or fields to search. + /// The string or strings to search for. + /// + /// Must be set to true if the query is run against an analyzed field. + /// + /// The score modifier. + /// A wildcard search definition. + public SearchDefinition Wildcard( + SearchPathDefinition path, + SearchQueryDefinition query, + bool allowAnalyzedField = false, + SearchScoreDefinition score = null) => + new WildcardSearchDefinition(path, query, allowAnalyzedField, score); + + /// + /// Creates a search definition that uses special characters in the search string that can + /// match any character. + /// + /// The type of the field. + /// The indexed field or fields to search. + /// The string or strings to search for. + /// + /// Must be set to true if the query is run against an analyzed field. + /// + /// The score modifier. + /// A wildcard search definition. + public SearchDefinition Wildcard( + Expression> path, + SearchQueryDefinition query, + bool allowAnalyzedField = false, + SearchScoreDefinition score = null) => + Wildcard(new ExpressionFieldDefinition(path), query, allowAnalyzedField, score); + } +} diff --git a/src/MongoDB.Driver/Search/SearchFacet.cs b/src/MongoDB.Driver/Search/SearchFacet.cs new file mode 100644 index 00000000000..4cd134b04d4 --- /dev/null +++ b/src/MongoDB.Driver/Search/SearchFacet.cs @@ -0,0 +1,49 @@ +/* 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 MongoDB.Bson; +using MongoDB.Bson.Serialization; + +namespace MongoDB.Driver.Search +{ + /// + /// Base class for search facets. + /// + /// The type of the document. + public abstract class SearchFacet + { + /// + /// Initializes a new instance of the class. + /// + /// The name of the facet. + protected SearchFacet(string name) + { + Name = name; + } + + /// + /// Gets the name of the facet. + /// + public string Name { get; } + + /// + /// Renders the search facet to a . + /// + /// The document serializer. + /// The serializer registry. + /// A . + public abstract BsonDocument Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry); + } +} diff --git a/src/MongoDB.Driver/Search/SearchFacetBuilder.cs b/src/MongoDB.Driver/Search/SearchFacetBuilder.cs new file mode 100644 index 00000000000..70472c6e280 --- /dev/null +++ b/src/MongoDB.Driver/Search/SearchFacetBuilder.cs @@ -0,0 +1,276 @@ +/* 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 MongoDB.Bson; +using MongoDB.Bson.Serialization; +using MongoDB.Driver.Core.Misc; + +namespace MongoDB.Driver.Search +{ + /// + /// A builder for a search facet. + /// + /// The type of the document. + public sealed class SearchFacetBuilder + { + /// + /// Creates a facet that narrows down search result based on a date. + /// + /// The name of the fact. + /// The field path to facet on. + /// + /// A list of date values that specify the boundaries for each bucket. + /// + /// + /// The name of an additional bucket that counts documents returned from the operator that + /// do not fall within the specified boundaries. + /// + /// A date search facet. + public SearchFacet Date( + string name, + SearchPathDefinition path, + IEnumerable boundaries, + string @default = null) => + new DateSearchFacet(name, path, boundaries, @default); + + /// + /// Creates a facet that narrows down search result based on a date. + /// + /// The name of the fact. + /// The field path to facet on. + /// + /// A list of date values that specify the boundaries for each bucket. + /// + /// A date search facet. + public SearchFacet Date( + string name, + SearchPathDefinition path, + params DateTime[] boundaries) => + Date(name, path, (IEnumerable)boundaries); + + /// + /// Creates a facet that narrows down search result based on a date. + /// + /// The type of the field. + /// The name of the fact. + /// The field path to facet on. + /// + /// A list of date values that specify the boundaries for each bucket. + /// + /// + /// The name of an additional bucket that counts documents returned from the operator that + /// do not fall within the specified boundaries. + /// + /// A date search facet. + public SearchFacet Date( + string name, + Expression> path, + IEnumerable boundaries, + string @default = null) => + Date(name, new ExpressionFieldDefinition(path), boundaries, @default); + + /// + /// Creates a facet that narrows down search result based on a date. + /// + /// The type of the field. + /// The name of the fact. + /// The field path to facet on. + /// + /// A list of date values that specify the boundaries for each bucket. + /// + /// A date search facet. + public SearchFacet Date( + string name, + Expression> path, + params DateTime[] boundaries) => + Date(name, new ExpressionFieldDefinition(path), boundaries); + + /// + /// Creates a facet that determines the frequency of numeric values by breaking the search + /// results into separate ranges of numbers. + /// + /// The name of the facet. + /// The field path to facet on. + /// + /// A list of numeric values that specify the boundaries for each bucket. + /// + /// + /// The name of an additional bucket that counts documents returned from the operator that + /// do not fall within the specified boundaries. + /// + /// A number search facet. + public SearchFacet Number( + string name, + SearchPathDefinition path, + IEnumerable boundaries, + string @default = null) => + new NumberSearchFacet(name, path, boundaries, @default); + + /// + /// Creates a facet that determines the frequency of numeric values by breaking the search + /// results into separate ranges of numbers. + /// + /// The name of the facet. + /// The field path to facet on. + /// + /// A list of numeric values that specify the boundaries for each bucket. + /// + /// A number search facet. + public SearchFacet Number( + string name, + SearchPathDefinition path, + params BsonValue[] boundaries) => + Number(name, path, (IEnumerable)boundaries); + + /// + /// Creates a facet that determines the frequency of numeric values by breaking the search + /// results into separate ranges of numbers. + /// + /// The type of the field. + /// The name of the facet. + /// The field path to facet on. + /// + /// A list of numeric values that specify the boundaries for each bucket. + /// + /// + /// The name of an additional bucket that counts documents returned from the operator that + /// do not fall within the specified boundaries. + /// + /// A number search facet. + public SearchFacet Number( + string name, + Expression> path, + IEnumerable boundaries, + string @default = null) => + Number(name, new ExpressionFieldDefinition(path), boundaries, @default); + + /// + /// Creates a facet that determines the frequency of numeric values by breaking the search + /// results into separate ranges of numbers. + /// + /// The type of the field. + /// The name of the facet. + /// The field path to facet on. + /// + /// A list of numeric values that specify the boundaries for each bucket. + /// + /// A number search facet. + public SearchFacet Number( + string name, + Expression> path, + params BsonValue[] boundaries) => + Number(name, new ExpressionFieldDefinition(path), boundaries); + + /// + /// Creates a facet that narrows down Atlas Search results based on the most frequent + /// string values in the specified string field. + /// + /// The name of the facet. + /// The field path to facet on. + /// + /// The maximum number of facet categories to return in the results. + /// + /// A string search facet. + public SearchFacet String(string name, SearchPathDefinition path, int? numBuckets = null) => + new StringSearchFacet(name, path, numBuckets); + + /// + /// Creates a facet that narrows down Atlas Search result based on the most frequent + /// string values in the specified string field. + /// + /// The type of the field. + /// The name of the facet. + /// The field path to facet on. + /// + /// The maximum number of facet categories to return in the results. + /// + /// A string search facet. + public SearchFacet String(string name, Expression> path, int? numBuckets = null) => + String(name, new ExpressionFieldDefinition(path), numBuckets); + } + + internal sealed class DateSearchFacet : SearchFacet + { + private readonly DateTime[] _boundaries; + private readonly string _default; + private readonly SearchPathDefinition _path; + + public DateSearchFacet(string name, SearchPathDefinition path, IEnumerable boundaries, string @default) + : base(name) + { + _path = Ensure.IsNotNull(path, nameof(path)); + _boundaries = Ensure.IsNotNull(boundaries, nameof(boundaries)).ToArray(); + _default = @default; + } + + public override BsonDocument Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) => + new() + { + { "type", "date" }, + { "path", _path.Render(documentSerializer, serializerRegistry) }, + { "boundaries", new BsonArray(_boundaries) }, + { "default", _default, _default != null } + }; + } + + internal sealed class NumberSearchFacet : SearchFacet + { + private readonly BsonValue[] _boundaries; + private readonly string _default; + private readonly SearchPathDefinition _path; + + public NumberSearchFacet(string name, SearchPathDefinition path, IEnumerable boundaries, string @default) + : base(name) + { + _path = Ensure.IsNotNull(path, nameof(path)); + _boundaries = Ensure.IsNotNull(boundaries, nameof(boundaries)).ToArray(); + _default = @default; + } + + public override BsonDocument Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) => + new() + { + { "type", "number" }, + { "path", _path.Render(documentSerializer, serializerRegistry) }, + { "boundaries", new BsonArray(_boundaries) }, + { "default", _default, _default != null } + }; + } + + internal sealed class StringSearchFacet : SearchFacet + { + private readonly int? _numBuckets; + private readonly SearchPathDefinition _path; + + public StringSearchFacet(string name, SearchPathDefinition path, int? numBuckets = null) + : base(name) + { + _path = Ensure.IsNotNull(path, nameof(path)); + _numBuckets = Ensure.IsNullOrBetween(numBuckets, 1, 1000, nameof(numBuckets)); + } + + public override BsonDocument Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) => + new() + { + { "type", "string" }, + { "path", _path.Render(documentSerializer, serializerRegistry) }, + { "numBuckets", _numBuckets, _numBuckets != null } + }; + } +} diff --git a/src/MongoDB.Driver/Search/SearchFuzzyOptions.cs b/src/MongoDB.Driver/Search/SearchFuzzyOptions.cs new file mode 100644 index 00000000000..2558cb9e6ae --- /dev/null +++ b/src/MongoDB.Driver/Search/SearchFuzzyOptions.cs @@ -0,0 +1,67 @@ +/* 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 MongoDB.Bson; +using MongoDB.Driver.Core.Misc; + +namespace MongoDB.Driver.Search +{ + /// + /// Options for fuzzy search. + /// + public sealed class SearchFuzzyOptions + { + private int? _maxEdits; + private int? _maxExpansions; + private int? _prefixLength; + + /// + /// Gets or sets the maximum number of single-character edits required to match the + /// specified search term. + /// + public int? MaxEdits + { + get => _maxEdits; + set => _maxEdits = Ensure.IsNullOrBetween(value, 1, 2, nameof(value)); + } + + /// + /// Gets or sets the number of variations to generate and search for. + /// + public int? MaxExpansions + { + get => _maxExpansions; + set => _maxExpansions = Ensure.IsNullOrGreaterThanZero(value, nameof(value)); + } + + /// + /// Gets or sets the number of characters at the beginning of each term in the result that + /// must exactly match. + /// + public int? PrefixLength + { + get => _prefixLength; + set => _prefixLength = Ensure.IsNullOrGreaterThanOrEqualToZero(value, nameof(value)); + } + + internal BsonDocument Render() + => new() + { + { "maxEdits", _maxEdits, _maxEdits != null }, + { "prefixLength", _prefixLength, _prefixLength != null }, + { "maxExpansions", _maxExpansions, _maxExpansions != null } + }; + } +} diff --git a/src/MongoDB.Driver/Search/SearchHighlight.cs b/src/MongoDB.Driver/Search/SearchHighlight.cs new file mode 100644 index 00000000000..204584359a0 --- /dev/null +++ b/src/MongoDB.Driver/Search/SearchHighlight.cs @@ -0,0 +1,105 @@ +/* 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 MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; + +namespace MongoDB.Driver.Search +{ + /// + /// Represents a result of highlighting. + /// + public sealed class SearchHighlight + { + /// + /// Initializes a new instance of the class. + /// + /// document field which returned a match. + /// Score assigned to this result. + /// Objects containing the matching text and the surrounding text. + public SearchHighlight(string path, double score, SearchHighlightText[] texts) + { + Path = path; + Score = score; + Texts = texts; + } + + /// + /// Gets the document field which returned a match. + /// + [BsonElement("path")] + public string Path { get; } + + /// + /// Gets the score assigned to this result. + /// + [BsonElement("score")] + public double Score { get; } + + /// + /// Gets one or more objects containing the matching text and the surrounding text + /// (if any). + /// + [BsonDefaultValue(null)] + [BsonElement("texts")] + public SearchHighlightText[] Texts { get; } + } + + /// + /// Represents the matching text or the surrounding text of a highlighting result. + /// + public sealed class SearchHighlightText + { + /// + /// Initializes a new instance of the class. + /// + /// Type of search highlight. + /// Text from the field which returned a match. + public SearchHighlightText(HighlightTextType type, string value) + { + Type = type; + Value = value; + } + + /// + /// Gets or sets the type of text, matching or surrounding. + /// + [BsonElement("type")] + [BsonRepresentation(BsonType.String)] + public HighlightTextType Type { get; } + + /// + /// Gets the text from the field which returned a match. + /// + [BsonElement("value")] + public string Value { get; } + } + + /// + /// Represents the type of text in a highlighting result, matching or surrounding. + /// + public enum HighlightTextType + { + /// + /// Indicates that the text contains a match. + /// + Hit, + + /// + /// Indicates that the text contains the text content adjacent to a matching string. + /// + Text + } +} diff --git a/src/MongoDB.Driver/Search/SearchHighlightOptions.cs b/src/MongoDB.Driver/Search/SearchHighlightOptions.cs new file mode 100644 index 00000000000..ec4026742fb --- /dev/null +++ b/src/MongoDB.Driver/Search/SearchHighlightOptions.cs @@ -0,0 +1,112 @@ +/* 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.Expressions; +using MongoDB.Bson; +using MongoDB.Bson.Serialization; +using MongoDB.Driver.Core.Misc; + +namespace MongoDB.Driver.Search +{ + /// + /// Options for highlighting. + /// + /// The type of the document. + public sealed class SearchHighlightOptions + { + private int? _maxCharsToExamine; + private int? _maxNumPassages; + private SearchPathDefinition _path; + + // constructors + /// + /// Initializes a new instance of the class. + /// + /// The document field to search. + /// maximum number of characters to examine. + /// The number of high-scoring passages. + public SearchHighlightOptions(SearchPathDefinition path, int? maxCharsToExamine = null, int? maxNumPassages = null) + { + _path = Ensure.IsNotNull(path, nameof(path)); + _maxCharsToExamine = maxCharsToExamine; + _maxNumPassages = maxNumPassages; + } + + /// + /// Creates highlighting options. + /// + /// The document field to search. + /// + /// The maximum number of characters to examine on a document when performing highlighting + /// for a field. + /// + /// + /// The number of high-scoring passages to return per document in the highlighting results + /// for each field. + /// + /// Highlighting options. + public SearchHighlightOptions( + Expression> path, + int? maxCharsToExamine = null, + int? maxNumPassages = null) + : this(new ExpressionFieldDefinition(path), maxCharsToExamine, maxNumPassages) + { + } + + /// + /// Gets or sets the maximum number of characters to examine on a document when performing + /// highlighting for a field. + /// + public int? MaxCharsToExamine + { + get => _maxCharsToExamine; + set => _maxCharsToExamine = Ensure.IsNullOrGreaterThanZero(value, nameof(value)); + } + + /// + /// Gets or sets the number of high-scoring passages to return per document in the + /// highlighting results for each field. + /// + public int? MaxNumPassages + { + get => _maxNumPassages; + set => _maxNumPassages = Ensure.IsNullOrGreaterThanZero(value, nameof(value)); + } + + /// + /// Gets or sets the document field to search. + /// + public SearchPathDefinition Path + { + get => _path; + set => _path = Ensure.IsNotNull(value, nameof(value)); + } + + /// + /// Renders the options to a . + /// + /// The document serializer. + /// The serializer registry. + /// A . + public BsonDocument Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) + => new() + { + { "path", _path.Render(documentSerializer, serializerRegistry) }, + { "maxCharsToExamine", _maxCharsToExamine, _maxCharsToExamine != null}, + { "maxNumPassages", _maxNumPassages, _maxNumPassages != null } + }; + } +} diff --git a/src/MongoDB.Driver/Search/SearchMetaResult.cs b/src/MongoDB.Driver/Search/SearchMetaResult.cs new file mode 100644 index 00000000000..7f57dc35123 --- /dev/null +++ b/src/MongoDB.Driver/Search/SearchMetaResult.cs @@ -0,0 +1,133 @@ +/* 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.Collections.Generic; +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; + +namespace MongoDB.Driver.Search +{ + /// + /// A search count result set. + /// + public sealed class SearchMetaCountResult + { + /// + /// Initializes a new instance of the class. + /// + /// Lower bound for this result set. + /// Total for this result set. + public SearchMetaCountResult(long? lowerBound, long? total) + { + LowerBound = lowerBound; + Total = total; + } + + /// + /// Gets the lower bound for this result set. + /// + [BsonDefaultValue(null)] + [BsonElement("lowerBound")] + public long? LowerBound { get; } + + /// + /// Gets the total for this result set. + /// + [BsonDefaultValue(null)] + [BsonElement("total")] + public long? Total { get; } + } + + /// + /// A search facet bucket result set. + /// + public sealed class SearchMetaFacetBucketResult + { + /// + /// Initializes a new instance of the class. + /// + /// count of documents in this facet bucket. + /// Unique identifier that identifies this facet bucket. + public SearchMetaFacetBucketResult(long count, BsonValue id) + { + Count = count; + Id = id; + } + + /// + /// Gets the count of documents in this facet bucket. + /// + [BsonElement("count")] + public long Count { get; } + + /// + /// Gets the unique identifier that identifies this facet bucket. + /// + [BsonId] + public BsonValue Id { get; } + } + + /// + /// A search facet result set. + /// + public sealed class SearchMetaFacetResult + { + /// + /// Initializes a new instance of the class. + /// + /// An array of bucket result sets. + public SearchMetaFacetResult(SearchMetaFacetBucketResult[] buckets) + { + Buckets = buckets; + } + + /// + /// Gets an array of bucket result sets. + /// + [BsonElement("buckets")] + public SearchMetaFacetBucketResult[] Buckets { get; } + } + + /// + /// A result set for a search metadata query. + /// + public sealed class SearchMetaResult + { + /// + /// Initializes a new instance of the class. + /// + /// Count result set. + /// Facet result sets. + public SearchMetaResult(SearchMetaCountResult count, IReadOnlyDictionary facet) + { + Count = count; + Facet = facet; + } + + /// + /// Gets the count result set. + /// + [BsonDefaultValue(null)] + [BsonElement("count")] + public SearchMetaCountResult Count { get; } + + /// + /// Gets the facet result sets. + /// + [BsonDefaultValue(null)] + [BsonElement("facet")] + public IReadOnlyDictionary Facet { get; } + } +} diff --git a/src/MongoDB.Driver/Search/SearchPathDefinition.cs b/src/MongoDB.Driver/Search/SearchPathDefinition.cs new file mode 100644 index 00000000000..b4efc54e607 --- /dev/null +++ b/src/MongoDB.Driver/Search/SearchPathDefinition.cs @@ -0,0 +1,102 @@ +/* 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.Collections.Generic; +using System.Linq; +using MongoDB.Bson; +using MongoDB.Bson.Serialization; + +namespace MongoDB.Driver.Search +{ + /// + /// Base class for search paths. + /// + /// The type of the document. + public abstract class SearchPathDefinition + { + /// + /// Renders the path to a . + /// + /// The document serializer. + /// The serializer registry. + /// A . + public abstract BsonValue Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry); + + /// + /// Performs an implicit conversion from to + /// . + /// + /// The field. + /// + /// The result of the conversion. + /// + public static implicit operator SearchPathDefinition(FieldDefinition field) => + new SingleSearchPathDefinition(field); + + /// + /// Performs an implicit conversion from a field name to . + /// + /// The field name. + /// + /// The result of the conversion. + /// + public static implicit operator SearchPathDefinition(string fieldName) => + new SingleSearchPathDefinition(new StringFieldDefinition(fieldName)); + + /// + /// Performs an implicit conversion from an array of to + /// . + /// + /// The array of fields. + /// + /// The result of the conversion. + /// + public static implicit operator SearchPathDefinition(FieldDefinition[] fields) => + new MultiSearchPathDefinition(fields); + + /// + /// Performs an implicit conversion from a list of to + /// . + /// + /// The list of fields. + /// + /// The result of the conversion. + /// + public static implicit operator SearchPathDefinition(List> fields) => + new MultiSearchPathDefinition(fields); + + /// + /// Performs an implicit conversion from an array of field names to + /// . + /// + /// The array of field names. + /// + /// The result of the conversion. + /// + public static implicit operator SearchPathDefinition(string[] fieldNames) => + new MultiSearchPathDefinition(fieldNames.Select(fieldName => new StringFieldDefinition(fieldName))); + + /// + /// Performs an implicit conversion from an array of field names to + /// . + /// + /// The list of field names. + /// + /// The result of the conversion. + /// + public static implicit operator SearchPathDefinition(List fieldNames) => + new MultiSearchPathDefinition(fieldNames.Select(fieldName => new StringFieldDefinition(fieldName))); + } +} diff --git a/src/MongoDB.Driver/Search/SearchPathDefinitionBuilder.cs b/src/MongoDB.Driver/Search/SearchPathDefinitionBuilder.cs new file mode 100644 index 00000000000..0537e13ce54 --- /dev/null +++ b/src/MongoDB.Driver/Search/SearchPathDefinitionBuilder.cs @@ -0,0 +1,168 @@ +/* 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 MongoDB.Bson; +using MongoDB.Bson.Serialization; +using MongoDB.Driver.Core.Misc; + +namespace MongoDB.Driver.Search +{ + /// + /// A builder for a search path. + /// + /// The type of the document. + public sealed class SearchPathDefinitionBuilder + { + /// + /// Creates a search path that searches using the specified analyzer. + /// + /// The field definition + /// The name of the analyzer. + /// An analyzer search path. + public SearchPathDefinition Analyzer(FieldDefinition field, string analyzerName) => + new AnalyzerSearchPathDefinition(field, analyzerName); + + /// + /// Creates a search path that searches using the specified analyzer. + /// + /// The type of the field. + /// The field definition + /// The name of the analyzer. + /// An analyzer search path. + public SearchPathDefinition Analyzer(Expression> field, string analyzerName) => + Analyzer(new ExpressionFieldDefinition(field), analyzerName); + + /// + /// Creates a search path for multiple fields. + /// + /// The collection of field definitions. + /// A multi-field search path. + public SearchPathDefinition Multi(IEnumerable> fields) => + new MultiSearchPathDefinition(fields); + + /// + /// Creates a search path for multiple fields. + /// + /// The array of field definitions. + /// A multi-field search path. + public SearchPathDefinition Multi(params FieldDefinition[] fields) => + Multi((IEnumerable>)fields); + + /// + /// Creates a search path for multiple fields. + /// + /// The type of the fields. + /// The array of field definitions. + /// A multi-field search path. + public SearchPathDefinition Multi(params Expression>[] fields) => + Multi(fields.Select(x => new ExpressionFieldDefinition(x))); + + /// + /// Creates a search path for a single field. + /// + /// The field definition. + /// A single-field search path. + public SearchPathDefinition Single(FieldDefinition field) => + new SingleSearchPathDefinition(field); + + /// + /// Creates a search path for a single field. + /// + /// The type of the field. + /// The field definition. + /// A single-field search path. + public SearchPathDefinition Single(Expression> field) => + Single(new ExpressionFieldDefinition(field)); + + /// + /// Creates a search path that uses special characters in the field name + /// that can match any character. + /// + /// + /// The wildcard string that the field name must match. + /// + /// A wildcard search path. + public SearchPathDefinition Wildcard(string query) => + new WildcardSearchPathDefinition(query); + } + + internal sealed class AnalyzerSearchPathDefinition : SearchPathDefinition + { + private readonly string _analyzerName; + private readonly FieldDefinition _field; + + public AnalyzerSearchPathDefinition(FieldDefinition field, string analyzerName) + { + _field = Ensure.IsNotNull(field, nameof(field)); + _analyzerName = Ensure.IsNotNull(analyzerName, nameof(analyzerName)); + } + + public override BsonValue Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) => + new BsonDocument() + { + { "value", _field.Render(documentSerializer, serializerRegistry).FieldName }, + { "multi", _analyzerName } + }; + } + + internal sealed class MultiSearchPathDefinition : SearchPathDefinition + { + private readonly FieldDefinition[] _fields; + + public MultiSearchPathDefinition(IEnumerable> fields) + { + _fields = Ensure.IsNotNull(fields, nameof(fields)).ToArray(); + } + + public override BsonValue Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) => + new BsonArray(_fields.Select(field => field.Render(documentSerializer, serializerRegistry).FieldName)); + } + + internal sealed class SingleSearchPathDefinition : SearchPathDefinition + { + private readonly FieldDefinition _field; + + public SingleSearchPathDefinition(FieldDefinition field) + { + _field = Ensure.IsNotNull(field, nameof(field)); + } + + public override BsonValue Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) + { + var renderedField = _field.Render(documentSerializer, serializerRegistry); + return new BsonString(renderedField.FieldName); + } + } + + internal sealed class WildcardSearchPathDefinition : SearchPathDefinition + { + private readonly string _query; + + public WildcardSearchPathDefinition(string query) + { + _query = Ensure.IsNotNull(query, nameof(query)); + } + + public override BsonValue Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) => + new BsonDocument() + { + { "wildcard", _query } + }; + } +} diff --git a/src/MongoDB.Driver/Search/SearchQueryDefinition.cs b/src/MongoDB.Driver/Search/SearchQueryDefinition.cs new file mode 100644 index 00000000000..765fb4264db --- /dev/null +++ b/src/MongoDB.Driver/Search/SearchQueryDefinition.cs @@ -0,0 +1,104 @@ +/* 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.Collections.Generic; +using System.Linq; +using MongoDB.Bson; +using MongoDB.Driver.Core.Misc; + +namespace MongoDB.Driver.Search +{ + /// + /// Base class for search queries. + /// + public abstract class SearchQueryDefinition + { + /// + /// Renders the query to a . + /// + /// A . + public abstract BsonValue Render(); + + /// + /// Performs an implicit conversion from a string to . + /// + /// The string. + /// + /// The result of the conversion. + /// + public static implicit operator SearchQueryDefinition(string query) => + new SingleSearchQueryDefinition(query); + + /// + /// Performs an implicit conversion from an array of strings to . + /// + /// The array of strings. + /// + /// The result of the conversion. + /// + public static implicit operator SearchQueryDefinition(string[] queries) => + new MultiSearchQueryDefinition(queries); + + /// + /// Performs an implicit conversion from a list of strings to . + /// + /// The list of strings. + /// + /// The result of the conversion. + /// + public static implicit operator SearchQueryDefinition(List queries) => + new MultiSearchQueryDefinition(queries); + } + + /// + /// A query definition for a single string. + /// + public sealed class SingleSearchQueryDefinition : SearchQueryDefinition + { + private readonly string _query; + + /// + /// Initializes a new instance of the class. + /// + /// The query string. + public SingleSearchQueryDefinition(string query) + { + _query = Ensure.IsNotNull(query, nameof(query)); + } + + /// + public override BsonValue Render() => new BsonString(_query); + } + + /// + /// A query definition for multiple strings. + /// + public sealed class MultiSearchQueryDefinition : SearchQueryDefinition + { + private readonly string[] _queries; + + /// + /// Initializes a new instance of the class. + /// + /// The query strings. + public MultiSearchQueryDefinition(IEnumerable queries) + { + _queries = Ensure.IsNotNull(queries, nameof(queries)).ToArray(); + } + + /// + public override BsonValue Render() => new BsonArray(_queries); + } +} diff --git a/src/MongoDB.Driver/Search/SearchRange.cs b/src/MongoDB.Driver/Search/SearchRange.cs new file mode 100644 index 00000000000..e31ac93f941 --- /dev/null +++ b/src/MongoDB.Driver/Search/SearchRange.cs @@ -0,0 +1,138 @@ +/* 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 MongoDB.Driver.Core.Misc; + +namespace MongoDB.Driver.Search +{ + /// + /// Object that specifies range of scalar and DateTime values. + /// + /// The type of the range value. + public struct SearchRange where TValue : struct, IComparable + { + #region static + /// Empty range. + public static SearchRange Empty { get; } = new(default, default, default, default); + #endregion + + /// + /// Initializes a new instance of the class. + /// + /// The lower bound of the range. + /// The upper bound of the range + /// Indicates whether the lower bound of the range is inclusive. + /// Indicates whether the upper bound of the range is inclusive. + public SearchRange(TValue? min, TValue? max, bool isMinInclusive, bool isMaxInclusive) + { + if (min != null && max != null) + { + Ensure.IsGreaterThanOrEqualTo(max.Value, min.Value, nameof(max)); + } + + Min = min; + Max = max; + IsMinInclusive = isMinInclusive; + IsMaxInclusive = isMaxInclusive; + } + + /// Gets the value that indicates whether the upper bound of the range is inclusive. + public bool IsMaxInclusive { get; } + + /// Gets the value that indicates whether the lower bound of the range is inclusive. + public bool IsMinInclusive { get; } + + /// Gets the lower bound of the range. + public TValue? Max { get; } + + /// Gets the lower bound of the range. + public TValue? Min { get; } + } + + /// + /// A builder for a SearchRange. + /// + public static class SearchRangeBuilder + { + /// + /// Creates a greater than search range. + /// + /// The value. + /// Search range. + public static SearchRange Gt(TValue value) where TValue : struct, IComparable + => SearchRange.Empty.Gt(value); + + /// + /// Adds a greater than value to a search range. + /// + /// Search range. + /// The value. + /// Search range. + public static SearchRange Gt(this SearchRange searchRange, TValue value) where TValue : struct, IComparable + => new(min: value, searchRange.Max, isMinInclusive: false, searchRange.IsMaxInclusive); + + /// + /// Creates a greater or equal than search range. + /// + /// The value. + /// Search range. + public static SearchRange Gte(TValue value) where TValue : struct, IComparable + => SearchRange.Empty.Gte(value); + + /// + /// Adds a greater or equal than value to a search range. + /// + /// Search range. + /// The value. + /// Search range. + public static SearchRange Gte(this SearchRange searchRange, TValue value) where TValue : struct, IComparable + => new(min: value, searchRange.Max, isMinInclusive: true, searchRange.IsMaxInclusive); + + /// + /// Creates a less than search range. + /// + /// The value. + /// Search range. + public static SearchRange Lt(TValue value) where TValue : struct, IComparable + => SearchRange.Empty.Lt(value); + + /// + /// Adds a less than value to a search range. + /// + /// Search range. + /// The value. + /// Search range. + public static SearchRange Lt(this SearchRange searchRange, TValue value) where TValue : struct, IComparable + => new(searchRange.Min, max: value, searchRange.IsMinInclusive, isMaxInclusive: false); + + /// + /// Creates a less than or equal search range. + /// + /// The value. + /// search range. + public static SearchRange Lte(TValue value) where TValue : struct, IComparable + => SearchRange.Empty.Lte(value); + + /// + /// Adds a less than or equal value to a search range. + /// + /// Search range. + /// The value. + /// search range. + public static SearchRange Lte(this SearchRange searchRange, TValue value) where TValue : struct, IComparable + => new(searchRange.Min, max: value, searchRange.IsMinInclusive, isMaxInclusive: true); + } +} diff --git a/src/MongoDB.Driver/Search/SearchScoreDefinition.cs b/src/MongoDB.Driver/Search/SearchScoreDefinition.cs new file mode 100644 index 00000000000..5e94b79633e --- /dev/null +++ b/src/MongoDB.Driver/Search/SearchScoreDefinition.cs @@ -0,0 +1,35 @@ +/* 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 MongoDB.Bson; +using MongoDB.Bson.Serialization; + +namespace MongoDB.Driver.Search +{ + /// + /// Base class for search score modifiers. + /// + /// The type of the document. + public abstract class SearchScoreDefinition + { + /// + /// Renders the score modifier to a . + /// + /// The document serializer. + /// The serializer registry. + /// A . + public abstract BsonDocument Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry); + } +} diff --git a/src/MongoDB.Driver/Search/SearchScoreDefinitionBuilder.cs b/src/MongoDB.Driver/Search/SearchScoreDefinitionBuilder.cs new file mode 100644 index 00000000000..cfcd33036a5 --- /dev/null +++ b/src/MongoDB.Driver/Search/SearchScoreDefinitionBuilder.cs @@ -0,0 +1,150 @@ +/* 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.Expressions; +using MongoDB.Bson; +using MongoDB.Bson.Serialization; +using MongoDB.Driver.Core.Misc; + +namespace MongoDB.Driver.Search +{ + /// + /// A builder for a score modifier. + /// + /// The type of the document. + public sealed class SearchScoreDefinitionBuilder + { + /// + /// Creates a score modifier that multiplies a result's base score by a given number. + /// + /// The number to multiply the default base score by. + /// + /// A boost score modifier. + /// + public SearchScoreDefinition Boost(double value) => + new BoostValueSearchScoreDefinition(value); + + /// + /// Creates a score modifier that multiples a result's base score by the value of a numeric + /// field in the documents. + /// + /// + /// The path to the numeric field whose value to multiply the default base score by. + /// + /// + /// The numeric value to substitute if the numeric field is not found in the documents. + /// + /// + /// A boost score modifier. + /// + public SearchScoreDefinition Boost(SearchPathDefinition path, double undefined = 0) => + new BoostPathSearchScoreDefinition(path, undefined); + + /// + /// Creates a score modifier that multiplies a result's base score by the value of a numeric + /// field in the documents. + /// + /// + /// The path to the numeric field whose value to multiply the default base score by. + /// + /// + /// The numeric value to substitute if the numeric field is not found in the documents. + /// + /// + /// A boost score modifier. + /// + public SearchScoreDefinition Boost(Expression> path, double undefined = 0) => + Boost(new ExpressionFieldDefinition(path), undefined); + + /// + /// Creates a score modifier that replaces the base score with a given number. + /// + /// The number to replace the base score with. + /// + /// A constant score modifier. + /// + public SearchScoreDefinition Constant(double value) => + new ConstantSearchScoreDefinition(value); + + /// + /// Creates a score modifier that computes the final score through an expression. + /// + /// The expression used to compute the score. + /// + /// A function score modifier. + /// + public SearchScoreDefinition Function(SearchScoreFunction function) => + new FunctionSearchScoreDefinition(function); + } + + internal sealed class BoostPathSearchScoreDefinition : SearchScoreDefinition + { + private readonly SearchPathDefinition _path; + private readonly double _undefined; + + public BoostPathSearchScoreDefinition(SearchPathDefinition path, double undefined) + { + _path = Ensure.IsNotNull(path, nameof(path)); + _undefined = undefined; + } + + public override BsonDocument Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) => + new("boost", new BsonDocument + { + { "path", _path.Render(documentSerializer, serializerRegistry) }, + { "undefined", _undefined, _undefined != 0 } + }); + } + + internal sealed class BoostValueSearchScoreDefinition : SearchScoreDefinition + { + private readonly double _value; + + public BoostValueSearchScoreDefinition(double value) + { + _value = Ensure.IsGreaterThanZero(value, nameof(value)); + } + + public override BsonDocument Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) => + new("boost", new BsonDocument("value", _value)); + } + + internal sealed class ConstantSearchScoreDefinition : SearchScoreDefinition + { + private readonly double _value; + + public ConstantSearchScoreDefinition(double value) + { + _value = Ensure.IsGreaterThanZero(value, nameof(value)); + } + + public override BsonDocument Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) => + new("constant", new BsonDocument("value", _value)); + } + + internal sealed class FunctionSearchScoreDefinition : SearchScoreDefinition + { + private readonly SearchScoreFunction _function; + + public FunctionSearchScoreDefinition(SearchScoreFunction function) + { + _function = Ensure.IsNotNull(function, nameof(function)); + } + + public override BsonDocument Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) => + new("function", _function.Render(documentSerializer, serializerRegistry)); + } +} diff --git a/src/MongoDB.Driver/Search/SearchScoreFunction.cs b/src/MongoDB.Driver/Search/SearchScoreFunction.cs new file mode 100644 index 00000000000..e77ac9d153e --- /dev/null +++ b/src/MongoDB.Driver/Search/SearchScoreFunction.cs @@ -0,0 +1,35 @@ +/* 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 MongoDB.Bson; +using MongoDB.Bson.Serialization; + +namespace MongoDB.Driver.Search +{ + /// + /// Base class for search score functions. + /// + /// The type of the document. + public abstract class SearchScoreFunction + { + /// + /// Renders the score function to a . + /// + /// The document serializer. + /// The serializer registry. + /// A . + public abstract BsonDocument Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry); + } +} diff --git a/src/MongoDB.Driver/Search/SearchScoreFunctionBuilder.cs b/src/MongoDB.Driver/Search/SearchScoreFunctionBuilder.cs new file mode 100644 index 00000000000..10d08cf3fb3 --- /dev/null +++ b/src/MongoDB.Driver/Search/SearchScoreFunctionBuilder.cs @@ -0,0 +1,276 @@ +/* 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 MongoDB.Bson; +using MongoDB.Bson.Serialization; +using MongoDB.Driver.Core.Misc; + +namespace MongoDB.Driver.Search +{ + /// + /// A builder for a score function. + /// + /// The type of the document. + public sealed class SearchScoreFunctionBuilder + { + /// + /// Creates a function that adds a series of numbers. + /// + /// An array of expressions, which can have negative values. + /// An addition score function. + public SearchScoreFunction Add(IEnumerable> operands) => + new ArithmeticSearchScoreFunction("add", operands); + + /// + /// Creates a function that adds a series of numbers. + /// + /// An array of expressions, which can have negative values. + /// An addition score function. + public SearchScoreFunction Add(params SearchScoreFunction[] operands) => + Add((IEnumerable>)operands); + + /// + /// Creates a function that represents a constant number. + /// + /// Number that indicates a fixed value. + /// A constant score function. + public SearchScoreFunction Constant(double value) => + new ConstantSearchScoreFunction(value); + + /// + /// Creates a function that decays, or reduces by multiplying, the final scores of the + /// documents based on the distance of a numeric field from a specified origin point. + /// + /// The path to the numeric field. + /// The point of origin from which to calculate the distance. + /// + /// The distance from plus or minus at + /// which scores must be multiplied. + /// + /// + /// The rate at which to multiply score values, which must be a positive number between + /// 0 and 1 exclusive. + /// + /// + /// The number of use to determine the distance from . + /// + /// A Guassian score function. + public SearchScoreFunction Gauss( + SearchPathDefinition path, + double origin, + double scale, + double decay = 0.5, + double offset = 0) + => new GaussSearchScoreFunction(path, origin, scale, decay, offset); + + /// + /// Creates a function that decays, or reduces by multiplying, the final scores of the + /// documents based on the distance of a numeric field from a specified origin point. + /// + /// The path to the numeric field. + /// The point of origin from which to calculate the distance. + /// + /// The distance from plus or minus at + /// which scores must be multiplied. + /// + /// + /// The rate at which to multiply score values, which must be a positive number between + /// 0 and 1 exclusive. + /// + /// + /// The number of use to determine the distance from . + /// + /// A Guassian score function. + public SearchScoreFunction Gauss( + Expression> path, + double origin, + double scale, + double decay = 0.5, + double offset = 0) + => Gauss(new ExpressionFieldDefinition(path), origin, scale, decay, offset); + + /// + /// Creates a function that calculates the base-10 logarithm of a number. + /// + /// The number. + /// A logarithmic score function. + public SearchScoreFunction Log(SearchScoreFunction operand) => + new UnarySearchScoreFunction("log", operand); + + /// + /// Creates a function that adds 1 to a number and then calculates its base-10 logarithm. + /// + /// The number. + /// A logarithmic score function. + public SearchScoreFunction Log1p(SearchScoreFunction operand) => + new UnarySearchScoreFunction("log1p", operand); + + /// + /// Creates a function that multiplies a series of numbers. + /// + /// An array of expressions, which can have negative values. + /// A multiplication score function. + public SearchScoreFunction Multiply(IEnumerable> operands) => + new ArithmeticSearchScoreFunction("multiply", operands); + + /// + /// Creates a function that multiplies a series of numbers. + /// + /// An array of expressions, which can have negative values. + /// A mulitplication score function. + public SearchScoreFunction Multiply(params SearchScoreFunction[] operands) => + Multiply((IEnumerable>)operands); + + /// + /// Creates a function that incorporates an indexed numeric field value into the score. + /// + /// The path to the numeric field. + /// + /// The value to use if the numeric field specified using is + /// missing in the document. + /// + /// A path score function. + public SearchScoreFunction Path(SearchPathDefinition path, double undefined = 0) => + new PathSearchScoreFunction(path, undefined); + + /// + /// Creates a function that incorporates an indexed numeric field value into the score. + /// + /// The path to the numeric field. + /// + /// The value to use if the numeric field specified using is + /// missing in the document. + /// + /// A path score function. + public SearchScoreFunction Path(Expression> path, double undefined = 0) => + Path(new ExpressionFieldDefinition(path), undefined); + + /// + /// Creates a function that represents the relevance score, which is the score Atlas Search + /// assigns documents based on relevance. + /// + /// A relevance score function. + public SearchScoreFunction Relevance() => new RelevanceSearchScoreFunction(); + } + + internal sealed class ArithmeticSearchScoreFunction : SearchScoreFunction + { + private readonly SearchScoreFunction[] _operands; + private readonly string _operatorName; + + public ArithmeticSearchScoreFunction(string operatorName, IEnumerable> operands) + { + _operatorName = operatorName; + _operands = Ensure.IsNotNull(operands, nameof(operands)).ToArray(); + } + + public override BsonDocument Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegister) => + new(_operatorName, new BsonArray(_operands.Select(o => o.Render(documentSerializer, serializerRegister)))); + } + + internal sealed class ConstantSearchScoreFunction : SearchScoreFunction + { + private readonly double _value; + + public ConstantSearchScoreFunction(double value) + { + _value = value; + } + + public override BsonDocument Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegister) => + new("constant", _value); + } + + internal sealed class GaussSearchScoreFunction : SearchScoreFunction + { + private readonly double _decay; + private readonly double _offset; + private readonly double _origin; + private readonly SearchPathDefinition _path; + private readonly double _scale; + + public GaussSearchScoreFunction( + SearchPathDefinition path, + double origin, + double scale, + double decay, + double offset) + { + _path = Ensure.IsNotNull(path, nameof(path)); + _origin = origin; + _scale = scale; + _decay = Ensure.IsBetween(decay, 0, 1, nameof(decay)); + _offset = offset; + } + + public override BsonDocument Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegister) => + new("gauss", new BsonDocument() + { + { "path", _path.Render(documentSerializer, serializerRegister) }, + { "origin", _origin }, + { "scale", _scale }, + { "decay", _decay, _decay != 0.5 }, + { "offset", _offset, _offset != 0 }, + }); + } + + internal sealed class PathSearchScoreFunction : SearchScoreFunction + { + private readonly SearchPathDefinition _path; + private readonly double _undefined; + + public PathSearchScoreFunction(SearchPathDefinition path, double undefined) + { + _path = Ensure.IsNotNull(path, nameof(path)); + _undefined = undefined; + } + + public override BsonDocument Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegister) + { + var renderedPath = _path.Render(documentSerializer, serializerRegister); + var pathDocument = _undefined == 0 ? renderedPath : new BsonDocument() + { + { "value", renderedPath }, + { "undefined", _undefined } + }; + + return new("path", pathDocument); + } + } + + internal sealed class RelevanceSearchScoreFunction : SearchScoreFunction + { + public override BsonDocument Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegister) => + new("score", "relevance"); + } + + internal sealed class UnarySearchScoreFunction : SearchScoreFunction + { + private readonly SearchScoreFunction _operand; + private readonly string _operatorName; + public UnarySearchScoreFunction(string operatorName, SearchScoreFunction operand) + { + _operatorName = operatorName; + _operand = Ensure.IsNotNull(operand, nameof(operand)); + } + + public override BsonDocument Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegister) => + new(_operatorName, _operand.Render(documentSerializer, serializerRegister)); + } +} diff --git a/src/MongoDB.Driver/Search/SearchSpanDefinition.cs b/src/MongoDB.Driver/Search/SearchSpanDefinition.cs new file mode 100644 index 00000000000..59fa5de3c8f --- /dev/null +++ b/src/MongoDB.Driver/Search/SearchSpanDefinition.cs @@ -0,0 +1,51 @@ +/* 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 MongoDB.Bson; +using MongoDB.Bson.Serialization; + +namespace MongoDB.Driver.Search +{ + /// + /// Base class for span clauses. + /// + /// + public abstract class SearchSpanDefinition + { + private protected enum ClauseType + { + First, + Near, + Or, + Subtract, + Term + } + + private readonly ClauseType _clauseType; + + private protected SearchSpanDefinition(ClauseType clauseType) => _clauseType = clauseType; + + /// + /// Renders the span clause to a . + /// + /// The document serializer. + /// The serializer registry. + /// A . + public BsonDocument Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) => + new(_clauseType.ToCamelCase(), RenderClause(documentSerializer, serializerRegistry)); + + private protected virtual BsonDocument RenderClause(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) => new(); + } +} diff --git a/src/MongoDB.Driver/Search/SearchSpanDefinitionBuilder.cs b/src/MongoDB.Driver/Search/SearchSpanDefinitionBuilder.cs new file mode 100644 index 00000000000..93a6c65a542 --- /dev/null +++ b/src/MongoDB.Driver/Search/SearchSpanDefinitionBuilder.cs @@ -0,0 +1,199 @@ +/* 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 MongoDB.Bson; +using MongoDB.Bson.Serialization; +using MongoDB.Driver; +using MongoDB.Driver.Core.Misc; + +namespace MongoDB.Driver.Search +{ + /// + /// A builder for a span clause. + /// + /// The type of the document. + public sealed class SearchSpanDefinitionBuilder + { + /// + /// Creates a span clause that matches near the beginning of the string. + /// + /// The span operator. + /// The highest position in which to match the query. + /// A first span clause. + public SearchSpanDefinition First(SearchSpanDefinition @operator, int endPositionLte) => + new FirstSearchSpanDefinition(@operator, endPositionLte); + + /// + /// Creates a span clause that matches multiple string found near each other. + /// + /// The clauses. + /// The allowable distance between words in the query phrase. + /// Whether to require that the clauses appear in the specified order. + /// A near span clause. + public SearchSpanDefinition Near( + IEnumerable> clauses, + int slop, + bool inOrder = false) => + new NearSearchSpanDefinition(clauses, slop, inOrder); + + /// + /// Creates a span clause that matches any of its subclauses. + /// + /// The clauses. + /// An or span clause. + public SearchSpanDefinition Or(IEnumerable> clauses) => + new OrSearchSpanDefinition(clauses); + + /// + /// Creates a span clause that matches any of its subclauses. + /// + /// The clauses. + /// An or span clause. + public SearchSpanDefinition Or(params SearchSpanDefinition[] clauses) => + Or((IEnumerable>)clauses); + + /// + /// Creates a span clause that excludes certain strings from the search results. + /// + /// Clause to be included. + /// Clause to be excluded. + /// A subtract span clause. + public SearchSpanDefinition Subtract( + SearchSpanDefinition include, + SearchSpanDefinition exclude) => + new SubtractSearchSpanDefinition(include, exclude); + + /// + /// Creates a span clause that matches a single term. + /// + /// The indexed field or fields to search. + /// The string or strings to search for. + /// A term span clause. + public SearchSpanDefinition Term(SearchPathDefinition path, SearchQueryDefinition query) => + new TermSearchSpanDefinition(path, query); + + /// + /// Creates a span clause that matches a single term. + /// + /// The type of the field. + /// The indexed field or fields to search. + /// The string or string to search for. + /// A term span clause. + public SearchSpanDefinition Term( + Expression> path, + SearchQueryDefinition query) => + Term(new ExpressionFieldDefinition(path), query); + } + + internal sealed class FirstSearchSpanDefinition : SearchSpanDefinition + { + private readonly int _endPositionLte; + private readonly SearchSpanDefinition _operator; + + public FirstSearchSpanDefinition(SearchSpanDefinition @operator, int endPositionLte) + : base(ClauseType.First) + { + _operator = Ensure.IsNotNull(@operator, nameof(@operator)); + _endPositionLte = endPositionLte; + } + private protected override BsonDocument RenderClause(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) => + new() + { + { "operator", _operator.Render(documentSerializer, serializerRegistry) }, + { "endPositionLte", _endPositionLte } + }; + } + + internal sealed class NearSearchSpanDefinition : SearchSpanDefinition + { + private readonly List> _clauses; + private readonly bool _inOrder; + private readonly int _slop; + + public NearSearchSpanDefinition(IEnumerable> clauses, int slop, bool inOrder) + : base(ClauseType.Near) + { + _clauses = Ensure.IsNotNull(clauses, nameof(clauses)).ToList(); + _slop = slop; + _inOrder = inOrder; + } + + private protected override BsonDocument RenderClause(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) => + new() + { + { "clauses", new BsonArray(_clauses.Select(clause => clause.Render(documentSerializer, serializerRegistry))) }, + { "slop", _slop }, + { "inOrder", _inOrder }, + }; + } + + internal sealed class OrSearchSpanDefinition : SearchSpanDefinition + { + private readonly List> _clauses; + + public OrSearchSpanDefinition(IEnumerable> clauses) + : base(ClauseType.Or) + { + _clauses = Ensure.IsNotNull(clauses, nameof(clauses)).ToList(); + } + + private protected override BsonDocument RenderClause(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) => + new("clauses", new BsonArray(_clauses.Select(clause => clause.Render(documentSerializer, serializerRegistry)))); + } + + internal sealed class SubtractSearchSpanDefinition : SearchSpanDefinition + { + private readonly SearchSpanDefinition _exclude; + private readonly SearchSpanDefinition _include; + + public SubtractSearchSpanDefinition(SearchSpanDefinition include, SearchSpanDefinition exclude) + : base(ClauseType.Subtract) + { + _include = Ensure.IsNotNull(include, nameof(include)); + _exclude = Ensure.IsNotNull(exclude, nameof(exclude)); + } + + private protected override BsonDocument RenderClause(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) => + new() + { + { "include", _include.Render(documentSerializer, serializerRegistry) }, + { "exclude", _exclude.Render(documentSerializer, serializerRegistry) }, + }; + } + + internal sealed class TermSearchSpanDefinition : SearchSpanDefinition + { + private readonly SearchPathDefinition _path; + private readonly SearchQueryDefinition _query; + + public TermSearchSpanDefinition(SearchPathDefinition path, SearchQueryDefinition query) + : base(ClauseType.Term) + { + _query = Ensure.IsNotNull(query, nameof(query)); + _path = Ensure.IsNotNull(path, nameof(path)); + } + + private protected override BsonDocument RenderClause(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) => + new() + { + { "query", _query.Render() }, + { "path", _path.Render(documentSerializer, serializerRegistry) }, + }; + } +} diff --git a/src/MongoDB.Driver/SortDefinition.cs b/src/MongoDB.Driver/SortDefinition.cs index ecf875caff2..afee0031c5f 100644 --- a/src/MongoDB.Driver/SortDefinition.cs +++ b/src/MongoDB.Driver/SortDefinition.cs @@ -51,7 +51,7 @@ public abstract class SortDefinition /// A . public virtual BsonDocument Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) { - return Render(documentSerializer, serializerRegistry, LinqProvider.V2); + return Render(documentSerializer, serializerRegistry, LinqProvider.V3); } /// diff --git a/src/MongoDB.Driver/SslSettings.cs b/src/MongoDB.Driver/SslSettings.cs index 34cb7d4ae96..2928010d359 100644 --- a/src/MongoDB.Driver/SslSettings.cs +++ b/src/MongoDB.Driver/SslSettings.cs @@ -220,7 +220,7 @@ public override int GetHashCode() return new Hasher() .Hash(_checkCertificateRevocation) - .HashElements(_clientCertificateCollection) + .HashElements(_clientCertificateCollection?.Cast()) .Hash(_clientCertificateSelectionCallback) .Hash(_enabledSslProtocols) .Hash(_serverCertificateValidationCallback) diff --git a/src/MongoDB.Driver/UpdateDefinition.cs b/src/MongoDB.Driver/UpdateDefinition.cs index 77c86cda26d..20ceada52f2 100644 --- a/src/MongoDB.Driver/UpdateDefinition.cs +++ b/src/MongoDB.Driver/UpdateDefinition.cs @@ -36,7 +36,7 @@ public abstract class UpdateDefinition /// A . public virtual BsonValue Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) { - return Render(documentSerializer, serializerRegistry, LinqProvider.V2); + return Render(documentSerializer, serializerRegistry, LinqProvider.V3); } /// @@ -115,6 +115,11 @@ public PipelineUpdateDefinition(PipelineDefinition pipelin _pipeline = Ensure.IsNotNull(pipeline, nameof(pipeline)); } + /// + /// Gets the pipeline. + /// + public PipelineDefinition Pipeline => _pipeline; + /// /// Renders the update to a that represents . /// @@ -146,7 +151,7 @@ public override string ToString() /// public string ToString(IBsonSerializer inputSerializer, IBsonSerializerRegistry serializerRegistry) { - return ToString(inputSerializer, serializerRegistry, LinqProvider.V2); + return ToString(inputSerializer, serializerRegistry, LinqProvider.V3); } /// diff --git a/src/MongoDB.Shared/Hasher.cs b/src/MongoDB.Shared/Hasher.cs index 255f31dbe73..580a0621d28 100644 --- a/src/MongoDB.Shared/Hasher.cs +++ b/src/MongoDB.Shared/Hasher.cs @@ -14,13 +14,21 @@ */ using System; -using System.Collections; using System.Collections.Generic; +using System.Runtime.CompilerServices; namespace MongoDB.Shared { internal class Hasher { + #region static + + // public static methods + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetHashCode(T obj) => EqualityComparer.Default.GetHashCode(obj); + + #endregion + // private fields private int _hashCode; @@ -75,7 +83,7 @@ public Hasher Hash(object obj) return this; } - public Hasher HashElements(IEnumerable sequence) + public Hasher HashElements(IEnumerable sequence) { if (sequence == null) { @@ -85,7 +93,7 @@ public Hasher HashElements(IEnumerable sequence) { foreach (var value in sequence) { - _hashCode = 37 * _hashCode + ((value == null) ? -1 : value.GetHashCode()); + _hashCode = 37 * _hashCode + (value == null ? -1 : GetHashCode(value)); } } return this; diff --git a/tests/AstrolabeWorkloadExecutor/AstrolabeEventFormatter.cs b/tests/AstrolabeWorkloadExecutor/AstrolabeEventFormatter.cs index 0101f97df82..f4e766bb35b 100644 --- a/tests/AstrolabeWorkloadExecutor/AstrolabeEventFormatter.cs +++ b/tests/AstrolabeWorkloadExecutor/AstrolabeEventFormatter.cs @@ -121,7 +121,7 @@ private BsonDocument CreateCmapEventDocument(string eventName, DateTime timestam private BsonDocument CreateCmapEventDocument(string eventName, DateTime timestamp, ConnectionId connectionId) => CreateCmapEventDocument(eventName, timestamp, connectionId.ServerId) - .Add("connectionId", connectionId.LocalValue); + .Add("connectionId", connectionId.LongLocalValue); private BsonDocument CreateCommandEventDocument(string eventName, DateTime timestamp, string commandName, int requestId) => new BsonDocument diff --git a/tests/AstrolabeWorkloadExecutor/AstrolabeWorkloadExecutor.csproj b/tests/AstrolabeWorkloadExecutor/AstrolabeWorkloadExecutor.csproj index 4ae67917bdc..0f374ee7d91 100644 --- a/tests/AstrolabeWorkloadExecutor/AstrolabeWorkloadExecutor.csproj +++ b/tests/AstrolabeWorkloadExecutor/AstrolabeWorkloadExecutor.csproj @@ -1,16 +1,8 @@ - - true - + Exe - netcoreapp2.1;netcoreapp3.1;net472 - netcoreapp2.1;netcoreapp3.1 - 9 - true - - false workload-executor ..\..\MongoDBTest.ruleset @@ -18,23 +10,12 @@ AstrolabeWorkloadExecutor AstrolabeWorkloadExecutor - MongoDB Inc. - Copyright © 2010-present MongoDB Inc. Astrolabe workload executor. - - - - - 0.0.0-local - - - - TRACE - + diff --git a/tests/AstrolabeWorkloadExecutor/Program.cs b/tests/AstrolabeWorkloadExecutor/Program.cs index 2d672ee87a2..c2c3c89378d 100644 --- a/tests/AstrolabeWorkloadExecutor/Program.cs +++ b/tests/AstrolabeWorkloadExecutor/Program.cs @@ -24,6 +24,7 @@ using MongoDB.Driver; using MongoDB.Driver.Core; using MongoDB.Driver.Core.Misc; +using MongoDB.Driver.Core.TestHelpers.Logging; using MongoDB.Driver.Tests.UnifiedTestOperations; namespace WorkloadExecutor @@ -113,6 +114,7 @@ private static (string EventsJson, string ResultsJson) ExecuteWorkload(string co { "events", new AstrolabeEventFormatter() } // "events" matches to the "storeEventsAsEntities.id" in the driverWorkload document }; using (var runner = new UnifiedTestRunner( + NullLoggingService.Instance, additionalArgs: additionalArgs, eventFormatters: eventFormatters)) { diff --git a/tests/AtlasConnectivity.Tests/AtlasConnectivity.Tests.csproj b/tests/AtlasConnectivity.Tests/AtlasConnectivity.Tests.csproj index f46dda5be96..42ee47d9f6a 100644 --- a/tests/AtlasConnectivity.Tests/AtlasConnectivity.Tests.csproj +++ b/tests/AtlasConnectivity.Tests/AtlasConnectivity.Tests.csproj @@ -1,38 +1,20 @@ - - true - + - netcoreapp2.1;netcoreapp3.1;net472 - netcoreapp2.1;netcoreapp3.1 - 9 - true - - false ..\..\MongoDBTest.ruleset AtlasConnectivity.Tests AtlasConnectivity.Tests - MongoDB Inc. - Copyright © 2010-present MongoDB Inc. Atlas connectivity tests. - - 0.0.0-local - - - - TRACE - - 1701;1702; @@ -40,12 +22,8 @@ - - - - - + diff --git a/tests/BuildProps/Tests.Build.props b/tests/BuildProps/Tests.Build.props new file mode 100644 index 00000000000..ca5a8480127 --- /dev/null +++ b/tests/BuildProps/Tests.Build.props @@ -0,0 +1,50 @@ + + + true + true + true + + + + netcoreapp2.1;netcoreapp3.1;net6.0 + $(TargetFrameworks);net472 + netstandard2.0;netstandard2.1 + $(StandardTargetFrameworks);net472 + false + + + + 10 + true + + + + 0.0.0-local + + + + MongoDB Inc. + Copyright © 2010-present MongoDB Inc. + + + + TRACE + + + $(DefineConstants);WINDOWS + + + $(DefineConstants);LINUX + + + $(DefineConstants);MACOS + + + + + + + + + + diff --git a/tests/MongoDB.Bson.TestHelpers/BsonValueEquivalencyComparer.cs b/tests/MongoDB.Bson.TestHelpers/BsonValueEquivalencyComparer.cs index 692a1e1e927..7ce19fe6f5d 100644 --- a/tests/MongoDB.Bson.TestHelpers/BsonValueEquivalencyComparer.cs +++ b/tests/MongoDB.Bson.TestHelpers/BsonValueEquivalencyComparer.cs @@ -13,6 +13,7 @@ * limitations under the License. */ +using System; using System.Collections.Generic; namespace MongoDB.Bson.TestHelpers @@ -22,25 +23,27 @@ public class BsonValueEquivalencyComparer : IEqualityComparer #region static public static BsonValueEquivalencyComparer Instance { get; } = new BsonValueEquivalencyComparer(); - public static bool Compare(BsonValue a, BsonValue b) + public static bool Compare(BsonValue a, BsonValue b, Action massageAction = null, bool allowTypesMismatching = true) { + massageAction?.Invoke(a, b); + if (a.BsonType == BsonType.Document && b.BsonType == BsonType.Document) { - return CompareDocuments((BsonDocument)a, (BsonDocument)b); + return CompareDocuments((BsonDocument)a, (BsonDocument)b, massageAction); } else if (a.BsonType == BsonType.Array && b.BsonType == BsonType.Array) { - return CompareArrays((BsonArray)a, (BsonArray)b); + return CompareArrays((BsonArray)a, (BsonArray)b, massageAction); } else if (a.BsonType == b.BsonType) { return a.Equals(b); } - else if (IsNumber(a) && IsNumber(b)) + else if (IsNumber(a) && IsNumber(b) && allowTypesMismatching) { return a.ToDouble() == b.ToDouble(); } - else if (CouldBeBoolean(a) && CouldBeBoolean(b)) + else if (CouldBeBoolean(a) && CouldBeBoolean(b) && allowTypesMismatching) { return a.ToBoolean() == b.ToBoolean(); } @@ -50,7 +53,7 @@ public static bool Compare(BsonValue a, BsonValue b) } } - private static bool CompareArrays(BsonArray a, BsonArray b) + private static bool CompareArrays(BsonArray a, BsonArray b, Action massageAction = null) { if (a.Count != b.Count) { @@ -59,7 +62,7 @@ private static bool CompareArrays(BsonArray a, BsonArray b) for (var i = 0; i < a.Count; i++) { - if (!Compare(a[i], b[i])) + if (!Compare(a[i], b[i], massageAction)) { return false; } @@ -68,7 +71,7 @@ private static bool CompareArrays(BsonArray a, BsonArray b) return true; } - private static bool CompareDocuments(BsonDocument a, BsonDocument b) + private static bool CompareDocuments(BsonDocument a, BsonDocument b, Action massageAction = null) { if (a.ElementCount != b.ElementCount) { @@ -83,7 +86,7 @@ private static bool CompareDocuments(BsonDocument a, BsonDocument b) return false; } - if (!Compare(aElement.Value, bElement.Value)) + if (!Compare(aElement.Value, bElement.Value, massageAction)) { return false; } diff --git a/tests/MongoDB.Bson.TestHelpers/GuidModeValues.cs b/tests/MongoDB.Bson.TestHelpers/GuidModeValues.cs index 1dd17246ef2..78692150aee 100644 --- a/tests/MongoDB.Bson.TestHelpers/GuidModeValues.cs +++ b/tests/MongoDB.Bson.TestHelpers/GuidModeValues.cs @@ -13,8 +13,8 @@ * limitations under the License. */ -using MongoDB.Bson.TestHelpers.XunitExtensions; using System.Linq; +using MongoDB.TestHelpers.XunitExtensions; namespace MongoDB.Bson.TestHelpers { diff --git a/tests/MongoDB.Bson.TestHelpers/MongoDB.Bson.TestHelpers.csproj b/tests/MongoDB.Bson.TestHelpers/MongoDB.Bson.TestHelpers.csproj index e741a54e38a..868cf275e19 100644 --- a/tests/MongoDB.Bson.TestHelpers/MongoDB.Bson.TestHelpers.csproj +++ b/tests/MongoDB.Bson.TestHelpers/MongoDB.Bson.TestHelpers.csproj @@ -1,48 +1,24 @@ - - true - + - netstandard2.0;netstandard2.1;net472 - netstandard2.0;netstandard2.1 - 9 - true - - false + $(StandardTargetFrameworks) ..\..\MongoDBLegacyTest.ruleset MongoDB.Bson.TestHelpers MongoDB.Bson.TestHelpers - MongoDB Inc. - Copyright © 2010-present MongoDB Inc. Helper classes applicable to test projects that reference MongoDB.Bson. - - 0.0.0-local - - - - TRACE - - - - - - - - - - + diff --git a/tests/MongoDB.Bson.TestHelpers/RegisterObjectSerializerFixture.cs b/tests/MongoDB.Bson.TestHelpers/RegisterObjectSerializerFixture.cs new file mode 100644 index 00000000000..0f0ac43d303 --- /dev/null +++ b/tests/MongoDB.Bson.TestHelpers/RegisterObjectSerializerFixture.cs @@ -0,0 +1,55 @@ +/* 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.Concurrent; +using System.Reflection; +using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Serializers; + +namespace MongoDB.Bson.TestHelpers +{ + public sealed class RegisterObjectSerializerFixture + { + public const string CollectionName = "RegisterObjectSerializer"; + + public RegisterObjectSerializerFixture() + { + // note: because xUnit has no way of letting us run some initialization code before any tests are run + // and because xUnit runs tests in random order it's very likely some other test has had the side effect + // of already registering a standard ObjectSerializer, so we have to use reflection below to replace + // any existing ObjectSerializer with one configured with AllowedTypes for testing + + var testObjectSerializer = new ObjectSerializer(TestAllowedTypes); + RegisterOrReplaceExistingSerializer(testObjectSerializer); + } + + public static bool TestAllowedTypes(Type type) + { + return + ObjectSerializer.DefaultAllowedTypes(type) || + type.FullName.StartsWith("MongoDB"); + } + + private static void RegisterOrReplaceExistingSerializer(IBsonSerializer serializer) + { + // we have to use reflection because the serialization API specifically prohibits this operation + var serializerRegistry = BsonSerializer.SerializerRegistry; + var cacheInfo = typeof(BsonSerializerRegistry).GetField("_cache", BindingFlags.NonPublic | BindingFlags.Instance); + var cache = (ConcurrentDictionary)cacheInfo.GetValue(serializerRegistry); + cache.AddOrUpdate(typeof(object), serializer, (objectType, existingObjectSerializer) => serializer); // might replace existing serializer! + } + } +} diff --git a/tests/MongoDB.Bson.Tests/Exceptions/BsonExceptionTests.cs b/tests/MongoDB.Bson.Tests/Exceptions/BsonExceptionTests.cs index e7c7bd17387..22efde595b3 100644 --- a/tests/MongoDB.Bson.Tests/Exceptions/BsonExceptionTests.cs +++ b/tests/MongoDB.Bson.Tests/Exceptions/BsonExceptionTests.cs @@ -31,9 +31,11 @@ public void Serialization_should_work() var formatter = new BinaryFormatter(); using (var stream = new MemoryStream()) { +#pragma warning disable SYSLIB0011 // BinaryFormatter serialization is obsolete formatter.Serialize(stream, subject); stream.Position = 0; var rehydrated = (BsonException)formatter.Deserialize(stream); +#pragma warning restore SYSLIB0011 // BinaryFormatter serialization is obsolete rehydrated.Message.Should().Be(subject.Message); rehydrated.InnerException.Message.Should().Be(subject.InnerException.Message); diff --git a/tests/MongoDB.Bson.Tests/Exceptions/BsonInternalExceptionTests.cs b/tests/MongoDB.Bson.Tests/Exceptions/BsonInternalExceptionTests.cs index 5ed1c31e748..3613260a73d 100644 --- a/tests/MongoDB.Bson.Tests/Exceptions/BsonInternalExceptionTests.cs +++ b/tests/MongoDB.Bson.Tests/Exceptions/BsonInternalExceptionTests.cs @@ -31,9 +31,11 @@ public void Serialization_should_work() var formatter = new BinaryFormatter(); using (var stream = new MemoryStream()) { +#pragma warning disable SYSLIB0011 // BinaryFormatter serialization is obsolete formatter.Serialize(stream, subject); stream.Position = 0; var rehydrated = (BsonInternalException)formatter.Deserialize(stream); +#pragma warning restore SYSLIB0011 // BinaryFormatter serialization is obsolete rehydrated.Message.Should().Be(subject.Message); rehydrated.InnerException.Message.Should().Be(subject.InnerException.Message); diff --git a/tests/MongoDB.Bson.Tests/Exceptions/BsonSerializationExceptionTests.cs b/tests/MongoDB.Bson.Tests/Exceptions/BsonSerializationExceptionTests.cs index 0c90ba727fe..b081ffdb2f6 100644 --- a/tests/MongoDB.Bson.Tests/Exceptions/BsonSerializationExceptionTests.cs +++ b/tests/MongoDB.Bson.Tests/Exceptions/BsonSerializationExceptionTests.cs @@ -31,9 +31,11 @@ public void Serialization_should_work() var formatter = new BinaryFormatter(); using (var stream = new MemoryStream()) { +#pragma warning disable SYSLIB0011 // BinaryFormatter serialization is obsolete formatter.Serialize(stream, subject); stream.Position = 0; var rehydrated = (BsonSerializationException)formatter.Deserialize(stream); +#pragma warning restore SYSLIB0011 // BinaryFormatter serialization is obsolete rehydrated.Message.Should().Be(subject.Message); rehydrated.InnerException.Message.Should().Be(subject.InnerException.Message); diff --git a/tests/MongoDB.Bson.Tests/Exceptions/DuplicateBsonMemberMapAttributeExceptionTests.cs b/tests/MongoDB.Bson.Tests/Exceptions/DuplicateBsonMemberMapAttributeExceptionTests.cs index f97d9071844..806995d65a7 100644 --- a/tests/MongoDB.Bson.Tests/Exceptions/DuplicateBsonMemberMapAttributeExceptionTests.cs +++ b/tests/MongoDB.Bson.Tests/Exceptions/DuplicateBsonMemberMapAttributeExceptionTests.cs @@ -31,9 +31,11 @@ public void Serialization_should_work() var formatter = new BinaryFormatter(); using (var stream = new MemoryStream()) { +#pragma warning disable SYSLIB0011 // BinaryFormatter serialization is obsolete formatter.Serialize(stream, subject); stream.Position = 0; var rehydrated = (DuplicateBsonMemberMapAttributeException)formatter.Deserialize(stream); +#pragma warning restore SYSLIB0011 // BinaryFormatter serialization is obsolete rehydrated.Message.Should().Be(subject.Message); rehydrated.InnerException.Message.Should().Be(subject.InnerException.Message); diff --git a/tests/MongoDB.Bson.Tests/Exceptions/TruncationExceptionTests.cs b/tests/MongoDB.Bson.Tests/Exceptions/TruncationExceptionTests.cs index 0531e05f6e4..2103a391aac 100644 --- a/tests/MongoDB.Bson.Tests/Exceptions/TruncationExceptionTests.cs +++ b/tests/MongoDB.Bson.Tests/Exceptions/TruncationExceptionTests.cs @@ -31,9 +31,11 @@ public void Serialization_should_work() var formatter = new BinaryFormatter(); using (var stream = new MemoryStream()) { +#pragma warning disable SYSLIB0011 // BinaryFormatter serialization is obsolete formatter.Serialize(stream, subject); stream.Position = 0; var rehydrated = (TruncationException)formatter.Deserialize(stream); +#pragma warning restore SYSLIB0011 // BinaryFormatter serialization is obsolete rehydrated.Message.Should().Be(subject.Message); rehydrated.InnerException.Message.Should().Be(subject.InnerException.Message); diff --git a/tests/MongoDB.Bson.Tests/IO/ArrayElementNameAcceleratorTests.cs b/tests/MongoDB.Bson.Tests/IO/ArrayElementNameAcceleratorTests.cs index d3e3fb15652..90458f44a1f 100644 --- a/tests/MongoDB.Bson.Tests/IO/ArrayElementNameAcceleratorTests.cs +++ b/tests/MongoDB.Bson.Tests/IO/ArrayElementNameAcceleratorTests.cs @@ -15,7 +15,7 @@ using System; using MongoDB.Bson.IO; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests.IO diff --git a/tests/MongoDB.Bson.Tests/IO/BsonBinaryReaderTests.cs b/tests/MongoDB.Bson.Tests/IO/BsonBinaryReaderTests.cs index 4adc967349e..1dcec7b925c 100644 --- a/tests/MongoDB.Bson.Tests/IO/BsonBinaryReaderTests.cs +++ b/tests/MongoDB.Bson.Tests/IO/BsonBinaryReaderTests.cs @@ -21,14 +21,14 @@ using MongoDB.Bson.IO; using MongoDB.Bson.Serialization; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests.IO { public class BsonBinaryReaderTests { - [SkippableFact] + [Fact] public void BsonBinaryReader_should_support_reading_more_than_2GB() { RequireEnvironment.Check().EnvironmentVariable("EXPLICIT"); diff --git a/tests/MongoDB.Bson.Tests/IO/BsonBinaryWriterTests.cs b/tests/MongoDB.Bson.Tests/IO/BsonBinaryWriterTests.cs index ed569cc5d45..82203c21fde 100644 --- a/tests/MongoDB.Bson.Tests/IO/BsonBinaryWriterTests.cs +++ b/tests/MongoDB.Bson.Tests/IO/BsonBinaryWriterTests.cs @@ -18,9 +18,8 @@ using System.Linq; using FluentAssertions; using MongoDB.Bson.IO; -using MongoDB.Bson.TestHelpers; using MongoDB.Bson.TestHelpers.IO; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests.IO @@ -53,7 +52,7 @@ public void BsonBinaryWriter_should_support_writing_multiple_documents( } } - [SkippableFact] + [Fact] public void BsonBinaryWriter_should_support_writing_more_than_2GB() { RequireProcess.Check().Bits(64); @@ -78,7 +77,7 @@ public void BsonBinaryWriter_should_support_writing_more_than_2GB() } } - [SkippableFact] + [Fact] public void BackpatchSize_should_throw_when_size_is_larger_than_2GB() { RequireProcess.Check().Bits(64); diff --git a/tests/MongoDB.Bson.Tests/IO/BsonChunkPoolTests.cs b/tests/MongoDB.Bson.Tests/IO/BsonChunkPoolTests.cs index 2defe775b33..bfe39a7d557 100644 --- a/tests/MongoDB.Bson.Tests/IO/BsonChunkPoolTests.cs +++ b/tests/MongoDB.Bson.Tests/IO/BsonChunkPoolTests.cs @@ -21,7 +21,7 @@ using System.Threading.Tasks; using FluentAssertions; using MongoDB.Bson.IO; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests.IO diff --git a/tests/MongoDB.Bson.Tests/IO/BsonDocumentReaderTests.cs b/tests/MongoDB.Bson.Tests/IO/BsonDocumentReaderTests.cs index 07bef0a3fbf..7dca4f29c63 100644 --- a/tests/MongoDB.Bson.Tests/IO/BsonDocumentReaderTests.cs +++ b/tests/MongoDB.Bson.Tests/IO/BsonDocumentReaderTests.cs @@ -19,7 +19,7 @@ using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests.IO diff --git a/tests/MongoDB.Bson.Tests/IO/BsonStreamAdapterTests.cs b/tests/MongoDB.Bson.Tests/IO/BsonStreamAdapterTests.cs index 34dcfeabfb2..0f752c6f5b6 100644 --- a/tests/MongoDB.Bson.Tests/IO/BsonStreamAdapterTests.cs +++ b/tests/MongoDB.Bson.Tests/IO/BsonStreamAdapterTests.cs @@ -21,7 +21,7 @@ using System.Threading.Tasks; using FluentAssertions; using MongoDB.Bson.IO; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Moq; using Xunit; diff --git a/tests/MongoDB.Bson.Tests/IO/BsonStreamExtensionsTests.cs b/tests/MongoDB.Bson.Tests/IO/BsonStreamExtensionsTests.cs index dcf6d272f4f..1a17564d5c2 100644 --- a/tests/MongoDB.Bson.Tests/IO/BsonStreamExtensionsTests.cs +++ b/tests/MongoDB.Bson.Tests/IO/BsonStreamExtensionsTests.cs @@ -19,7 +19,7 @@ using System.Reflection; using FluentAssertions; using MongoDB.Bson.IO; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Moq; using Xunit; diff --git a/tests/MongoDB.Bson.Tests/IO/BsonWriterTests.cs b/tests/MongoDB.Bson.Tests/IO/BsonWriterTests.cs index 3f2f149394f..643f7321f6b 100644 --- a/tests/MongoDB.Bson.Tests/IO/BsonWriterTests.cs +++ b/tests/MongoDB.Bson.Tests/IO/BsonWriterTests.cs @@ -18,7 +18,7 @@ using System.Linq; using FluentAssertions; using MongoDB.Bson.IO; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Moq; using Xunit; diff --git a/tests/MongoDB.Bson.Tests/IO/ByteArrayBufferTests.cs b/tests/MongoDB.Bson.Tests/IO/ByteArrayBufferTests.cs index bc81535a860..af5208910e4 100644 --- a/tests/MongoDB.Bson.Tests/IO/ByteArrayBufferTests.cs +++ b/tests/MongoDB.Bson.Tests/IO/ByteArrayBufferTests.cs @@ -18,7 +18,7 @@ using System.Reflection; using FluentAssertions; using MongoDB.Bson.IO; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests.IO diff --git a/tests/MongoDB.Bson.Tests/IO/ByteArrayChunkTests.cs b/tests/MongoDB.Bson.Tests/IO/ByteArrayChunkTests.cs index 9160aa48120..4e45b31c18e 100644 --- a/tests/MongoDB.Bson.Tests/IO/ByteArrayChunkTests.cs +++ b/tests/MongoDB.Bson.Tests/IO/ByteArrayChunkTests.cs @@ -17,7 +17,7 @@ using System.Reflection; using FluentAssertions; using MongoDB.Bson.IO; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests.IO diff --git a/tests/MongoDB.Bson.Tests/IO/ByteBufferFactoryTests.cs b/tests/MongoDB.Bson.Tests/IO/ByteBufferFactoryTests.cs index dca061b1663..7af07ae9267 100644 --- a/tests/MongoDB.Bson.Tests/IO/ByteBufferFactoryTests.cs +++ b/tests/MongoDB.Bson.Tests/IO/ByteBufferFactoryTests.cs @@ -20,7 +20,7 @@ using System.Threading.Tasks; using FluentAssertions; using MongoDB.Bson.IO; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Moq; using Xunit; diff --git a/tests/MongoDB.Bson.Tests/IO/ByteBufferSliceTests.cs b/tests/MongoDB.Bson.Tests/IO/ByteBufferSliceTests.cs index 308ae40d1af..d0d42b6c608 100644 --- a/tests/MongoDB.Bson.Tests/IO/ByteBufferSliceTests.cs +++ b/tests/MongoDB.Bson.Tests/IO/ByteBufferSliceTests.cs @@ -21,7 +21,7 @@ using System.Threading.Tasks; using FluentAssertions; using MongoDB.Bson.IO; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Moq; using Xunit; diff --git a/tests/MongoDB.Bson.Tests/IO/ByteBufferStreamTests.cs b/tests/MongoDB.Bson.Tests/IO/ByteBufferStreamTests.cs index 4acadc1173f..b9d2e1d61b0 100644 --- a/tests/MongoDB.Bson.Tests/IO/ByteBufferStreamTests.cs +++ b/tests/MongoDB.Bson.Tests/IO/ByteBufferStreamTests.cs @@ -20,7 +20,7 @@ using System.Reflection; using FluentAssertions; using MongoDB.Bson.IO; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Moq; using Xunit; @@ -272,7 +272,7 @@ public void Position_set_should_throw_when_subject_is_disposed() action.ShouldThrow().And.ObjectName.Should().Be("ByteBufferStream"); } - [SkippableFact] + [Fact] public void PrepareToWrite_should_throw_when_stream_would_exceed_2GB() { RequireProcess.Check().Bits(64); diff --git a/tests/MongoDB.Bson.Tests/IO/CStringUtf8EncodingTests.cs b/tests/MongoDB.Bson.Tests/IO/CStringUtf8EncodingTests.cs index 1c984cb3a8b..c0c95d9046b 100644 --- a/tests/MongoDB.Bson.Tests/IO/CStringUtf8EncodingTests.cs +++ b/tests/MongoDB.Bson.Tests/IO/CStringUtf8EncodingTests.cs @@ -18,7 +18,7 @@ using FluentAssertions; using MongoDB.Bson.IO; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests.IO diff --git a/tests/MongoDB.Bson.Tests/IO/EncodingHelperTests.cs b/tests/MongoDB.Bson.Tests/IO/EncodingHelperTests.cs index 93996067d63..45eb62034c8 100644 --- a/tests/MongoDB.Bson.Tests/IO/EncodingHelperTests.cs +++ b/tests/MongoDB.Bson.Tests/IO/EncodingHelperTests.cs @@ -19,7 +19,7 @@ using FluentAssertions; using MongoDB.Bson.IO; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests.IO diff --git a/tests/MongoDB.Bson.Tests/IO/InputBufferChunkSourceTests.cs b/tests/MongoDB.Bson.Tests/IO/InputBufferChunkSourceTests.cs index fd1e1d4362a..1d9db95f058 100644 --- a/tests/MongoDB.Bson.Tests/IO/InputBufferChunkSourceTests.cs +++ b/tests/MongoDB.Bson.Tests/IO/InputBufferChunkSourceTests.cs @@ -17,7 +17,7 @@ using System.Reflection; using FluentAssertions; using MongoDB.Bson.IO; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Moq; using Xunit; diff --git a/tests/MongoDB.Bson.Tests/IO/JsonReaderTests.cs b/tests/MongoDB.Bson.Tests/IO/JsonReaderTests.cs index 50ec4fbf87a..76c31fa01cd 100644 --- a/tests/MongoDB.Bson.Tests/IO/JsonReaderTests.cs +++ b/tests/MongoDB.Bson.Tests/IO/JsonReaderTests.cs @@ -22,7 +22,7 @@ using MongoDB.Bson.IO; using MongoDB.Bson.Serialization; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests.IO diff --git a/tests/MongoDB.Bson.Tests/IO/JsonWriterTests.cs b/tests/MongoDB.Bson.Tests/IO/JsonWriterTests.cs index e0b5337f8bd..b40c21b84b2 100644 --- a/tests/MongoDB.Bson.Tests/IO/JsonWriterTests.cs +++ b/tests/MongoDB.Bson.Tests/IO/JsonWriterTests.cs @@ -22,7 +22,7 @@ using MongoDB.Bson.IO; using MongoDB.Bson.Serialization; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests.IO @@ -213,7 +213,7 @@ public void TestDouble() } } - [SkippableFact] + [Fact] public void TestDoubleRoundTripOn64BitProcess() { RequireProcess.Check().Bits(64); diff --git a/tests/MongoDB.Bson.Tests/IO/MultiChunkBufferTests.cs b/tests/MongoDB.Bson.Tests/IO/MultiChunkBufferTests.cs index 0f1e0569f73..e28e95acf32 100644 --- a/tests/MongoDB.Bson.Tests/IO/MultiChunkBufferTests.cs +++ b/tests/MongoDB.Bson.Tests/IO/MultiChunkBufferTests.cs @@ -20,7 +20,7 @@ using FluentAssertions; using MongoDB.Bson.IO; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Moq; using Xunit; @@ -432,7 +432,7 @@ public void EnsureCapacity_should_throw_when_subject_is_read_only() action.ShouldThrow(); } - [SkippableFact] + [Fact] public void ExpandCapacity_should_throw_when_expanded_capacity_exceeds_2GB() { RequireProcess.Check().Bits(64); diff --git a/tests/MongoDB.Bson.Tests/IO/OutputBufferChunkSourceTests.cs b/tests/MongoDB.Bson.Tests/IO/OutputBufferChunkSourceTests.cs index f96e1df5fde..843f7ec6201 100644 --- a/tests/MongoDB.Bson.Tests/IO/OutputBufferChunkSourceTests.cs +++ b/tests/MongoDB.Bson.Tests/IO/OutputBufferChunkSourceTests.cs @@ -21,7 +21,7 @@ using System.Threading.Tasks; using FluentAssertions; using MongoDB.Bson.IO; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Moq; using Xunit; diff --git a/tests/MongoDB.Bson.Tests/IO/SingleChunkBufferTests.cs b/tests/MongoDB.Bson.Tests/IO/SingleChunkBufferTests.cs index 322feb46fff..5c9ef93fd0a 100644 --- a/tests/MongoDB.Bson.Tests/IO/SingleChunkBufferTests.cs +++ b/tests/MongoDB.Bson.Tests/IO/SingleChunkBufferTests.cs @@ -21,7 +21,7 @@ using System.Threading.Tasks; using FluentAssertions; using MongoDB.Bson.IO; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Moq; using Xunit; diff --git a/tests/MongoDB.Bson.Tests/IO/ThreadStaticBufferTests.cs b/tests/MongoDB.Bson.Tests/IO/ThreadStaticBufferTests.cs index 1d16372e099..38f6bd2dbf7 100644 --- a/tests/MongoDB.Bson.Tests/IO/ThreadStaticBufferTests.cs +++ b/tests/MongoDB.Bson.Tests/IO/ThreadStaticBufferTests.cs @@ -20,7 +20,7 @@ using FluentAssertions; using MongoDB.Bson.IO; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests.IO diff --git a/tests/MongoDB.Bson.Tests/Jira/CSharp147Tests.cs b/tests/MongoDB.Bson.Tests/Jira/CSharp147Tests.cs index 690dd80cb71..87274e4bdbb 100644 --- a/tests/MongoDB.Bson.Tests/Jira/CSharp147Tests.cs +++ b/tests/MongoDB.Bson.Tests/Jira/CSharp147Tests.cs @@ -18,7 +18,7 @@ using MongoDB.Bson.IO; using MongoDB.Bson.Serialization; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests.Jira.CSharp147 diff --git a/tests/MongoDB.Bson.Tests/Jira/CSharp1559Tests.cs b/tests/MongoDB.Bson.Tests/Jira/CSharp1559Tests.cs index f42fc63eff0..7e9526b8fe7 100644 --- a/tests/MongoDB.Bson.Tests/Jira/CSharp1559Tests.cs +++ b/tests/MongoDB.Bson.Tests/Jira/CSharp1559Tests.cs @@ -24,6 +24,7 @@ namespace MongoDB.Bson.Tests.Jira { + [Collection(RegisterObjectSerializerFixture.CollectionName)] public class CSharp1559Tests { [Theory] diff --git a/tests/MongoDB.Bson.Tests/Jira/CSharp263Tests.cs b/tests/MongoDB.Bson.Tests/Jira/CSharp263Tests.cs index bfad065dc53..a871463e4d5 100644 --- a/tests/MongoDB.Bson.Tests/Jira/CSharp263Tests.cs +++ b/tests/MongoDB.Bson.Tests/Jira/CSharp263Tests.cs @@ -13,12 +13,13 @@ * limitations under the License. */ -using MongoDB.Bson; using MongoDB.Bson.Serialization; +using MongoDB.Bson.TestHelpers; using Xunit; namespace MongoDB.Bson.Tests.Jira { + [Collection(RegisterObjectSerializerFixture.CollectionName)] public class CSharp263Tests { public class C diff --git a/tests/MongoDB.Bson.Tests/Jira/CSharp275Tests.cs b/tests/MongoDB.Bson.Tests/Jira/CSharp275Tests.cs index 1b371e85314..64566845177 100644 --- a/tests/MongoDB.Bson.Tests/Jira/CSharp275Tests.cs +++ b/tests/MongoDB.Bson.Tests/Jira/CSharp275Tests.cs @@ -15,7 +15,7 @@ using System; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests.Jira diff --git a/tests/MongoDB.Bson.Tests/Jira/CSharp301Tests.cs b/tests/MongoDB.Bson.Tests/Jira/CSharp301Tests.cs index 1d369672aaa..23a12e00faa 100644 --- a/tests/MongoDB.Bson.Tests/Jira/CSharp301Tests.cs +++ b/tests/MongoDB.Bson.Tests/Jira/CSharp301Tests.cs @@ -15,12 +15,13 @@ using System.Collections; using System.Collections.Generic; -using MongoDB.Bson; using MongoDB.Bson.Serialization; +using MongoDB.Bson.TestHelpers; using Xunit; namespace MongoDB.Bson.Tests.Jira { + [Collection(RegisterObjectSerializerFixture.CollectionName)] public class CSharp301Tests { public class C @@ -60,7 +61,7 @@ public void TestHashtableEmpty() { var c = new C { Id = 1, Obj = new Hashtable { } }; var json = c.ToJson(); -#if NET472 || NETCOREAPP3_1 +#if NET472 || NETCOREAPP3_1_OR_GREATER // Hashtable is situated in well-known libraries for: // - NET472: mscorlib // - NETCOREAPP3_1: System.Private.CoreLib @@ -81,7 +82,7 @@ public void TestHashtableOneElement() { var c = new C { Id = 1, Obj = new Hashtable { { "x", 1 } } }; var json = c.ToJson(); -#if NET472 || NETCOREAPP3_1 +#if NET472 || NETCOREAPP3_1_OR_GREATER // Hashtable is situated in well-known libraries for: // - NET472: mscorlib // - NETCOREAPP3_1: System.Private.CoreLib diff --git a/tests/MongoDB.Bson.Tests/Jira/CSharp313Tests.cs b/tests/MongoDB.Bson.Tests/Jira/CSharp313Tests.cs index 23b0da39f9d..a7c5ee9e4cf 100644 --- a/tests/MongoDB.Bson.Tests/Jira/CSharp313Tests.cs +++ b/tests/MongoDB.Bson.Tests/Jira/CSharp313Tests.cs @@ -14,12 +14,13 @@ */ using System; -using MongoDB.Bson; using MongoDB.Bson.Serialization; +using MongoDB.Bson.TestHelpers; using Xunit; namespace MongoDB.Bson.Tests.Jira { + [Collection(RegisterObjectSerializerFixture.CollectionName)] public class CSharp313Tests { private static object[] __scalarValues = new object[] diff --git a/tests/MongoDB.Bson.Tests/Jira/CSharp4412Tests.cs b/tests/MongoDB.Bson.Tests/Jira/CSharp4412Tests.cs new file mode 100644 index 00000000000..d26a2f4e507 --- /dev/null +++ b/tests/MongoDB.Bson.Tests/Jira/CSharp4412Tests.cs @@ -0,0 +1,169 @@ +/* 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.Collections.Generic; +using System.Collections.ObjectModel; +using FluentAssertions; +using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Attributes; +using Xunit; + +namespace MongoDB.Bson.Tests.Jira.CSharp783 +{ + public class CSharp4412Tests + { + [Fact] + public void IEnumerable_Ids_should_deserialize_from_ObjectIds() + { + var json = "{ \"Ids\" : [ObjectId(\"0102030405060708090a0b0c\")] }"; + + var rehydrated = BsonSerializer.Deserialize(json); + + rehydrated.Ids.Should().Equal("0102030405060708090a0b0c"); + } + + public static readonly object[][] IEnumerable_should_serialize_as_ObjectIds_MemberData = + { + new object[] { new string[] { "0102030405060708090a0b0c" } }, + new object[] { new List { "0102030405060708090a0b0c" } }, + new object[] { new Collection { "0102030405060708090a0b0c" } }, + new object[] { new ReadOnlyCollection(new List { "0102030405060708090a0b0c" }) }, + new object[] { new HashSet { "0102030405060708090a0b0c" } }, + new object[] { new SortedSet { "0102030405060708090a0b0c" } } + }; + + [Theory] + [MemberData(nameof(IEnumerable_should_serialize_as_ObjectIds_MemberData))] + public void IEnumerable_should_serialize_as_ObjectIds(IEnumerable value) + { + var document = new ClassWithIEnumerableIds { Ids = value }; + + var json = document.ToJson(); + + json.Should().Be("{ \"Ids\" : [ObjectId(\"0102030405060708090a0b0c\")] }"); + } + + [Fact] + public void ICollection_Ids_should_deserialize_from_ObjectIds() + { + var json = "{ \"Ids\" : [ObjectId(\"0102030405060708090a0b0c\")] }"; + + var rehydrated = BsonSerializer.Deserialize(json); + + rehydrated.Ids.Should().Equal("0102030405060708090a0b0c"); + } + + public static readonly object[][] ICollection_should_serialize_as_ObjectIds_MemberData = + { + new object[] { new string[] { "0102030405060708090a0b0c" } }, + new object[] { new List { "0102030405060708090a0b0c" } }, + new object[] { new Collection { "0102030405060708090a0b0c" } }, + new object[] { new ReadOnlyCollection(new List { "0102030405060708090a0b0c" }) }, + new object[] { new HashSet { "0102030405060708090a0b0c" } }, + new object[] { new SortedSet { "0102030405060708090a0b0c" } } + }; + + [Theory] + [MemberData(nameof(ICollection_should_serialize_as_ObjectIds_MemberData))] + public void ICollection_should_serialize_as_ObjectIds(ICollection value) + { + var document = new ClassWithICollectionIds { Ids = value }; + + var json = document.ToJson(); + + json.Should().Be("{ \"Ids\" : [ObjectId(\"0102030405060708090a0b0c\")] }"); + } + + [Fact] + public void IList_Ids_should_deserialize_from_ObjectIds() + { + var json = "{ \"Ids\" : [ObjectId(\"0102030405060708090a0b0c\")] }"; + + var rehydrated = BsonSerializer.Deserialize(json); + + rehydrated.Ids.Should().Equal("0102030405060708090a0b0c"); + } + + public static readonly object[][] IList_should_serialize_as_ObjectIds_MemberData = + { + new object[] { new string[] { "0102030405060708090a0b0c" } }, + new object[] { new List { "0102030405060708090a0b0c" } }, + new object[] { new Collection { "0102030405060708090a0b0c" } }, + new object[] { new ReadOnlyCollection(new List { "0102030405060708090a0b0c" }) } + }; + + [Theory] + [MemberData(nameof(IList_should_serialize_as_ObjectIds_MemberData))] + public void IList_should_serialize_as_ObjectIds(IList value) + { + var document = new ClassWithIListIds { Ids = value }; + + var json = document.ToJson(); + + json.Should().Be("{ \"Ids\" : [ObjectId(\"0102030405060708090a0b0c\")] }"); + } + + [Fact] + public void ISet_Ids_should_deserialize_from_ObjectIds() + { + var json = "{ \"Ids\" : [ObjectId(\"0102030405060708090a0b0c\")] }"; + + var rehydrated = BsonSerializer.Deserialize(json); + + rehydrated.Ids.Should().Equal("0102030405060708090a0b0c"); + } + + public static readonly object[][] ISet_should_serialize_as_ObjectIds_MemberData = + { + new object[] { new HashSet { "0102030405060708090a0b0c" } }, + new object[] { new SortedSet { "0102030405060708090a0b0c" } } + }; + + [Theory] + [MemberData(nameof(ISet_should_serialize_as_ObjectIds_MemberData))] + public void ISet_should_serialize_as_ObjectIds(ISet value) + { + var document = new ClassWithISetIds { Ids = value }; + + var json = document.ToJson(); + + json.Should().Be("{ \"Ids\" : [ObjectId(\"0102030405060708090a0b0c\")] }"); + } + + private class ClassWithIEnumerableIds + { + [BsonRepresentation(BsonType.ObjectId)] + public IEnumerable Ids { get; set; } + } + + private class ClassWithICollectionIds + { + [BsonRepresentation(BsonType.ObjectId)] + public ICollection Ids { get; set; } + } + + private class ClassWithIListIds + { + [BsonRepresentation(BsonType.ObjectId)] + public IList Ids { get; set; } + } + + private class ClassWithISetIds + { + [BsonRepresentation(BsonType.ObjectId)] + public ISet Ids { get; set; } + } + } +} diff --git a/tests/MongoDB.Bson.Tests/Jira/CSharp4424Tests.cs b/tests/MongoDB.Bson.Tests/Jira/CSharp4424Tests.cs new file mode 100644 index 00000000000..9330bfa9af6 --- /dev/null +++ b/tests/MongoDB.Bson.Tests/Jira/CSharp4424Tests.cs @@ -0,0 +1,104 @@ +/* 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.Serialization; +using Xunit; + +namespace MongoDB.Bson.Tests.Jira +{ + public class CSharp4424Tests + { + [Fact] + public void TryRegisterClassMap_with_no_arguments_can_be_called_more_than_once() + { + var result = BsonClassMap.TryRegisterClassMap(); + result.Should().BeTrue(); + + result = BsonClassMap.TryRegisterClassMap(); + result.Should().BeFalse(); + } + + [Fact] + public void TryRegisterClassMap_with_classMap_can_be_called_more_than_once() + { + var classMap = new BsonClassMap(cm => cm.AutoMap()); + + var result = BsonClassMap.TryRegisterClassMap(classMap); + result.Should().BeTrue(); + + result = BsonClassMap.TryRegisterClassMap(classMap); + result.Should().BeFalse(); + } + + [Fact] + public void TryRegisterClassMap_with_classMapInitializer_can_be_called_more_than_once() + { + var classMapInitializerCallCount = 0; + + var result = BsonClassMap.TryRegisterClassMap(ClassMapInitializer); + result.Should().BeTrue(); + classMapInitializerCallCount.Should().Be(1); + + result = BsonClassMap.TryRegisterClassMap(ClassMapInitializer); + result.Should().BeFalse(); + classMapInitializerCallCount.Should().Be(1); + + void ClassMapInitializer(BsonClassMap cm) + { + classMapInitializerCallCount++; + cm.AutoMap(); + } + } + + [Fact] + public void TryRegisterClassMap_with_classMapFactory_should_only_call_factory_once() + { + var classMapFactoryCallCount = 0; + + var result = BsonClassMap.TryRegisterClassMap(ClassMapFactory); + result.Should().BeTrue(); + classMapFactoryCallCount.Should().Be(1); + + result = BsonClassMap.TryRegisterClassMap(ClassMapFactory); + result.Should().BeFalse(); + classMapFactoryCallCount.Should().Be(1); + + BsonClassMap ClassMapFactory() + { + classMapFactoryCallCount++; + var classMap = new BsonClassMap(); + classMap.AutoMap(); + return classMap; + } + } + + public class C + { + } + + public class D + { + } + + public class E + { + } + + public class F + { + } + } +} diff --git a/tests/MongoDB.Bson.Tests/Jira/CSharp4475Tests.cs b/tests/MongoDB.Bson.Tests/Jira/CSharp4475Tests.cs new file mode 100644 index 00000000000..980893e1149 --- /dev/null +++ b/tests/MongoDB.Bson.Tests/Jira/CSharp4475Tests.cs @@ -0,0 +1,95 @@ +/* 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 FluentAssertions; +using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Serializers; +using Xunit; + +namespace MongoDB.Bson.Tests.Jira +{ + public class CSharp4475Tests + { + static CSharp4475Tests() + { + var objectSerializerWithAllowedTypes = new ObjectSerializer(t => t == typeof(Allowed)); + + BsonClassMap.RegisterClassMap(classMap => + { + classMap.AutoMap(); + classMap.MapProperty(x => x.Object).SetSerializer(objectSerializerWithAllowedTypes); + }); + } + + [Fact] + public void Deserialize_should_return_expected_result_when_deserializing_an_allowed_type() + { + var json = "{ Object : { _t : 'MongoDB.Bson.Tests.Jira.CSharp4475Tests+Allowed, MongoDB.Bson.Tests', X : 1 } }"; + + var result = BsonSerializer.Deserialize(json); + + var allowed = result.Object.Should().BeOfType().Subject; + allowed.X.Should().Be(1); + } + + [Fact] + public void Deserialize_should_throw_when_deserializing_a_not_allowed_type() + { + var json = "{ Object : { _t : 'MongoDB.Bson.Tests.Jira.CSharp4475Tests+NotAllowed, MongoDB.Bson.Tests', X : 1 } }"; + + var exception = Record.Exception(() => BsonSerializer.Deserialize(json)); + + exception.Should().BeOfType(); + exception.Message.Should().Be("An error occurred while deserializing the Object property of class MongoDB.Bson.Tests.Jira.CSharp4475Tests+C: Type MongoDB.Bson.Tests.Jira.CSharp4475Tests+NotAllowed is not configured as an allowed type for this instance of ObjectSerializer."); + } + + [Fact] + public void Serialize_should_have_expected_result_when_serializing_an_allowed_type() + { + var c = new C { Object = new Allowed { X = 1 } }; + + var result = c.ToJson(); + + result.Should().Be("{ \"Object\" : { \"_t\" : \"Allowed\", \"X\" : 1 } }"); + } + + [Fact] + public void Serialize_should_throw_when_serializing_a_not_allowed_type() + { + var c = new C { Object = new NotAllowed { X = 1 } }; + + var exception = Record.Exception(() => c.ToJson()); + + exception.Should().BeOfType(); + exception.Message.Should().Be("An error occurred while serializing the Object property of class MongoDB.Bson.Tests.Jira.CSharp4475Tests+C: Type MongoDB.Bson.Tests.Jira.CSharp4475Tests+NotAllowed is not configured as an allowed type for this instance of ObjectSerializer."); + } + + public class C + { + public object Object { get; set; } + } + + public class Allowed + { + public int X { get; set; } + } + + public class NotAllowed + { + public int X { get; set; } + } + } +} diff --git a/tests/MongoDB.Bson.Tests/Jira/CSharp515Tests.cs b/tests/MongoDB.Bson.Tests/Jira/CSharp515Tests.cs index 4fda6e577f5..54920f37ef1 100644 --- a/tests/MongoDB.Bson.Tests/Jira/CSharp515Tests.cs +++ b/tests/MongoDB.Bson.Tests/Jira/CSharp515Tests.cs @@ -16,12 +16,13 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Reflection; -using MongoDB.Bson; using MongoDB.Bson.Serialization; +using MongoDB.Bson.TestHelpers; using Xunit; namespace MongoDB.Bson.Tests.Jira.CSharp515 { + [Collection(RegisterObjectSerializerFixture.CollectionName)] public class CSharp515Tests { public class C diff --git a/tests/MongoDB.Bson.Tests/Jira/CSharp564Tests.cs b/tests/MongoDB.Bson.Tests/Jira/CSharp564Tests.cs index b385151e1d9..8c670ad3502 100644 --- a/tests/MongoDB.Bson.Tests/Jira/CSharp564Tests.cs +++ b/tests/MongoDB.Bson.Tests/Jira/CSharp564Tests.cs @@ -13,12 +13,13 @@ * limitations under the License. */ -using MongoDB.Bson; using MongoDB.Bson.Serialization; +using MongoDB.Bson.TestHelpers; using Xunit; namespace MongoDB.Bson.Tests.Jira.CSharp564 { + [Collection(RegisterObjectSerializerFixture.CollectionName)] public class CSharp564Tests { interface IWhatever { } diff --git a/tests/MongoDB.Bson.Tests/Jira/CSharp783Tests.cs b/tests/MongoDB.Bson.Tests/Jira/CSharp783Tests.cs index 82d983a7589..8408afab25f 100644 --- a/tests/MongoDB.Bson.Tests/Jira/CSharp783Tests.cs +++ b/tests/MongoDB.Bson.Tests/Jira/CSharp783Tests.cs @@ -19,11 +19,13 @@ using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.Serialization.Serializers; +using MongoDB.Bson.TestHelpers; using Xunit; namespace MongoDB.Bson.Tests.Jira.CSharp783 { #if WINDOWS + [Collection(RegisterObjectSerializerFixture.CollectionName)] public class CSharp783DiscriminatedInterfaceTests { // nested types @@ -144,6 +146,7 @@ public void TestSortedSetTwoInts(int x, int y) } #endif + [Collection(RegisterObjectSerializerFixture.CollectionName)] public class CSharp783ImpliedHashSetImplementationTests { // nested types @@ -262,6 +265,7 @@ public void TestSortedSetTwoInts(int x, int y) } } + [Collection(RegisterObjectSerializerFixture.CollectionName)] public class CSharp783ImpliedSortedSetImplementationTests { // nested types diff --git a/tests/MongoDB.Bson.Tests/MongoDB.Bson.Tests.csproj b/tests/MongoDB.Bson.Tests/MongoDB.Bson.Tests.csproj index 20077470bf7..7f262a14faa 100644 --- a/tests/MongoDB.Bson.Tests/MongoDB.Bson.Tests.csproj +++ b/tests/MongoDB.Bson.Tests/MongoDB.Bson.Tests.csproj @@ -1,23 +1,13 @@ - - true - + - netcoreapp2.1;netcoreapp3.1;net472 - netcoreapp2.1;netcoreapp3.1 - 9 - true - - false ..\..\MongoDBLegacyTest.ruleset MongoDB.Bson.Tests MongoDB.Bson.Tests - MongoDB Inc. - Copyright © 2010-present MongoDB Inc. MongoDB.Bson tests. @@ -25,18 +15,6 @@ - - 0.0.0-local - - - - TRACE - - - - $(DefineConstants);WINDOWS - - 1701;1702; @@ -56,15 +34,10 @@ - + - - - - - - + @@ -72,14 +45,6 @@ - - - - - - - - Always diff --git a/tests/MongoDB.Bson.Tests/ObjectModel/BsonArrayTests.cs b/tests/MongoDB.Bson.Tests/ObjectModel/BsonArrayTests.cs index 73e3aaab53f..206687cb210 100644 --- a/tests/MongoDB.Bson.Tests/ObjectModel/BsonArrayTests.cs +++ b/tests/MongoDB.Bson.Tests/ObjectModel/BsonArrayTests.cs @@ -17,6 +17,7 @@ using System.Linq; using MongoDB.Bson; using Xunit; +using Xunit.Sdk; namespace MongoDB.Bson.Tests { diff --git a/tests/MongoDB.Bson.Tests/ObjectModel/BsonBinaryDataTests.cs b/tests/MongoDB.Bson.Tests/ObjectModel/BsonBinaryDataTests.cs index 62f28d71991..8e73f9c1039 100644 --- a/tests/MongoDB.Bson.Tests/ObjectModel/BsonBinaryDataTests.cs +++ b/tests/MongoDB.Bson.Tests/ObjectModel/BsonBinaryDataTests.cs @@ -17,7 +17,7 @@ using System.Linq; using FluentAssertions; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests diff --git a/tests/MongoDB.Bson.Tests/ObjectModel/BsonBooleanTests.cs b/tests/MongoDB.Bson.Tests/ObjectModel/BsonBooleanTests.cs index a760bdd1036..c7bf1a680c1 100644 --- a/tests/MongoDB.Bson.Tests/ObjectModel/BsonBooleanTests.cs +++ b/tests/MongoDB.Bson.Tests/ObjectModel/BsonBooleanTests.cs @@ -19,7 +19,7 @@ using System.Text; using System.Threading.Tasks; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests.ObjectModel diff --git a/tests/MongoDB.Bson.Tests/ObjectModel/BsonDocumentTests.cs b/tests/MongoDB.Bson.Tests/ObjectModel/BsonDocumentTests.cs index 5469025d738..9489c93a7e6 100644 --- a/tests/MongoDB.Bson.Tests/ObjectModel/BsonDocumentTests.cs +++ b/tests/MongoDB.Bson.Tests/ObjectModel/BsonDocumentTests.cs @@ -25,7 +25,7 @@ using MongoDB.Bson.Serialization.IdGenerators; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests diff --git a/tests/MongoDB.Bson.Tests/ObjectModel/BsonDoubleTests.cs b/tests/MongoDB.Bson.Tests/ObjectModel/BsonDoubleTests.cs index 067468a8364..da4f828f528 100644 --- a/tests/MongoDB.Bson.Tests/ObjectModel/BsonDoubleTests.cs +++ b/tests/MongoDB.Bson.Tests/ObjectModel/BsonDoubleTests.cs @@ -14,12 +14,8 @@ */ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests.ObjectModel diff --git a/tests/MongoDB.Bson.Tests/ObjectModel/BsonInt32Tests.cs b/tests/MongoDB.Bson.Tests/ObjectModel/BsonInt32Tests.cs index cc2c7f17c5a..269daef31f3 100644 --- a/tests/MongoDB.Bson.Tests/ObjectModel/BsonInt32Tests.cs +++ b/tests/MongoDB.Bson.Tests/ObjectModel/BsonInt32Tests.cs @@ -14,12 +14,8 @@ */ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests.ObjectModel diff --git a/tests/MongoDB.Bson.Tests/ObjectModel/BsonInt64Tests.cs b/tests/MongoDB.Bson.Tests/ObjectModel/BsonInt64Tests.cs index c7ad86422c9..5f13a6294af 100644 --- a/tests/MongoDB.Bson.Tests/ObjectModel/BsonInt64Tests.cs +++ b/tests/MongoDB.Bson.Tests/ObjectModel/BsonInt64Tests.cs @@ -14,12 +14,8 @@ */ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests.ObjectModel diff --git a/tests/MongoDB.Bson.Tests/ObjectModel/BsonStringTests.cs b/tests/MongoDB.Bson.Tests/ObjectModel/BsonStringTests.cs index 5fc230732bf..c21faa2de10 100644 --- a/tests/MongoDB.Bson.Tests/ObjectModel/BsonStringTests.cs +++ b/tests/MongoDB.Bson.Tests/ObjectModel/BsonStringTests.cs @@ -19,7 +19,7 @@ using System.Text; using System.Threading.Tasks; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests.ObjectModel diff --git a/tests/MongoDB.Bson.Tests/ObjectModel/BsonTypeMapperTests.cs b/tests/MongoDB.Bson.Tests/ObjectModel/BsonTypeMapperTests.cs index f6d9d66a081..bc9d3711efe 100644 --- a/tests/MongoDB.Bson.Tests/ObjectModel/BsonTypeMapperTests.cs +++ b/tests/MongoDB.Bson.Tests/ObjectModel/BsonTypeMapperTests.cs @@ -18,7 +18,7 @@ using System.Text.RegularExpressions; using FluentAssertions; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests diff --git a/tests/MongoDB.Bson.Tests/ObjectModel/BsonValueTests.cs b/tests/MongoDB.Bson.Tests/ObjectModel/BsonValueTests.cs index eb7b347a093..43788b12a7e 100644 --- a/tests/MongoDB.Bson.Tests/ObjectModel/BsonValueTests.cs +++ b/tests/MongoDB.Bson.Tests/ObjectModel/BsonValueTests.cs @@ -18,7 +18,7 @@ using System.Text.RegularExpressions; using FluentAssertions; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests diff --git a/tests/MongoDB.Bson.Tests/ObjectModel/LazyBsonDocumentTests.cs b/tests/MongoDB.Bson.Tests/ObjectModel/LazyBsonDocumentTests.cs index 97a2b08b257..9d81840ae3b 100644 --- a/tests/MongoDB.Bson.Tests/ObjectModel/LazyBsonDocumentTests.cs +++ b/tests/MongoDB.Bson.Tests/ObjectModel/LazyBsonDocumentTests.cs @@ -19,7 +19,7 @@ using System.Linq; using MongoDB.Bson; using MongoDB.Bson.Serialization; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests @@ -853,7 +853,7 @@ public void TestValues() } } - [SkippableFact] + [Fact] public void TestLargeDocumentDeserialization() { RequireProcess.Check().Bits(64); diff --git a/tests/MongoDB.Bson.Tests/ObjectModel/RawBsonArrayTests.cs b/tests/MongoDB.Bson.Tests/ObjectModel/RawBsonArrayTests.cs index 67b5714b7c2..b748bd57cf5 100644 --- a/tests/MongoDB.Bson.Tests/ObjectModel/RawBsonArrayTests.cs +++ b/tests/MongoDB.Bson.Tests/ObjectModel/RawBsonArrayTests.cs @@ -19,7 +19,7 @@ using MongoDB.Bson; using MongoDB.Bson.IO; using MongoDB.Bson.Serialization; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests diff --git a/tests/MongoDB.Bson.Tests/PowerOf2Tests.cs b/tests/MongoDB.Bson.Tests/PowerOf2Tests.cs index 55992fbec77..1c8fa00a027 100644 --- a/tests/MongoDB.Bson.Tests/PowerOf2Tests.cs +++ b/tests/MongoDB.Bson.Tests/PowerOf2Tests.cs @@ -19,7 +19,7 @@ using System.Text; using System.Threading.Tasks; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests diff --git a/tests/MongoDB.Bson.Tests/Properties/AssemblyInfo.cs b/tests/MongoDB.Bson.Tests/Properties/AssemblyInfo.cs index cc39fd111c9..de81526bc77 100644 --- a/tests/MongoDB.Bson.Tests/Properties/AssemblyInfo.cs +++ b/tests/MongoDB.Bson.Tests/Properties/AssemblyInfo.cs @@ -14,5 +14,9 @@ */ using System.Runtime.InteropServices; +using MongoDB.TestHelpers.XunitExtensions; +using Xunit; [assembly: ComVisible(false)] + +[assembly: TestFramework(XunitExtensionsConstants.TimeoutEnforcingXunitFramework, XunitExtensionsConstants.TimeoutEnforcingFrameworkAssembly)] diff --git a/tests/MongoDB.Bson.Tests/RegisterObjectSerializerCollection.cs b/tests/MongoDB.Bson.Tests/RegisterObjectSerializerCollection.cs new file mode 100644 index 00000000000..5ea624b4564 --- /dev/null +++ b/tests/MongoDB.Bson.Tests/RegisterObjectSerializerCollection.cs @@ -0,0 +1,25 @@ +/* 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 MongoDB.Bson.TestHelpers; +using Xunit; + +namespace MongoDB.Bson.Tests +{ + [CollectionDefinition(RegisterObjectSerializerFixture.CollectionName)] + public sealed class RegisterObjectSerializerCollection : ICollectionFixture + { + } +} diff --git a/tests/MongoDB.Bson.Tests/Serialization/BsonClassMapSerializerTests.cs b/tests/MongoDB.Bson.Tests/Serialization/BsonClassMapSerializerTests.cs new file mode 100644 index 00000000000..f04507eabdf --- /dev/null +++ b/tests/MongoDB.Bson.Tests/Serialization/BsonClassMapSerializerTests.cs @@ -0,0 +1,48 @@ +/* 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.IO; +using MongoDB.Bson.Serialization; +using Xunit; + +namespace MongoDB.Bson.Tests.Serialization +{ + public class BsonClassMapSerializerTests + { + // public methods + [Fact] + public void Deserialize_should_throw_invalidOperationException_when_creator_returns_null() + { + var bsonClassMap = new BsonClassMap(); + bsonClassMap.SetCreator(() => null); + bsonClassMap.Freeze(); + + var subject = new BsonClassMapSerializer(bsonClassMap); + + using var reader = new JsonReader("{ \"_id\": \"just_an_id\" }"); + var context = BsonDeserializationContext.CreateRoot(reader); + + var exception = Record.Exception(() => subject.Deserialize(context)); + exception.Should().BeOfType(); + } + + // nested classes + private class MyModel + { + public string Id { get; set; } + } + } +} diff --git a/tests/MongoDB.Bson.Tests/Serialization/BsonSerializerRegistryTests.cs b/tests/MongoDB.Bson.Tests/Serialization/BsonSerializerRegistryTests.cs new file mode 100644 index 00000000000..6989565923c --- /dev/null +++ b/tests/MongoDB.Bson.Tests/Serialization/BsonSerializerRegistryTests.cs @@ -0,0 +1,193 @@ +/* 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 FluentAssertions; +using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Serializers; +using Moq; +using Xunit; + +namespace MongoDB.Bson.Tests.Serialization +{ + public class BsonSerializerRegistryTests + { + [Fact] + public void RegisterSerializer_should_work() + { + var subject = new BsonSerializerRegistry(); + var serializer = new ObjectSerializer(); + + subject.RegisterSerializer(typeof(object), serializer); + + subject.GetSerializer(typeof(object)).Should().BeSameAs(serializer); + } + + [Fact] + public void RegisterSerializer_should_throw_when_type_is_null() + { + var subject = new BsonSerializerRegistry(); + var serializer = new ObjectSerializer(); + + var exception = Record.Exception(() => subject.RegisterSerializer(type: null, serializer)); + + var argumentNullException = exception.Should().BeOfType().Subject; + argumentNullException.ParamName.Should().Be("type"); + } + + [Fact] + public void RegisterSerializer_should_throw_when_type_is_BsonValue() + { + var subject = new BsonSerializerRegistry(); + var serializer = BsonValueSerializer.Instance; + + var exception = Record.Exception(() => subject.RegisterSerializer(typeof(BsonValue), serializer)); + + exception.Should().BeOfType(); + exception.Message.Should().Contain("A serializer cannot be registered for type BsonValue because it is a subclass of BsonValue"); + } + + [Fact] + public void RegisterSerializer_should_throw_when_type_is_not_closed_generic_type() + { + var subject = new BsonSerializerRegistry(); + var serializer = Mock.Of>(); + + var exception = Record.Exception(() => subject.RegisterSerializer(typeof(List<>), serializer)); + + var argumentException = exception.Should().BeOfType().Subject; + argumentException.ParamName.Should().Be("type"); + argumentException.Message.Should().Contain("Generic type List has unassigned type parameters"); + } + + [Fact] + public void RegisterSerializer_should_throw_when_serializer_is_null() + { + var subject = new BsonSerializerRegistry(); + + var exception = Record.Exception(() => subject.RegisterSerializer(typeof(object), serializer: null)); + + var argumentNullException = exception.Should().BeOfType().Subject; + argumentNullException.ParamName.Should().Be("serializer"); + } + + [Fact] + public void RegisterSerializer_should_throw_when_serializer_is_already_registered() + { + var subject = new BsonSerializerRegistry(); + var serializer = new ObjectSerializer(); + + subject.RegisterSerializer(typeof(object), serializer); + var exception = Record.Exception(() => subject.RegisterSerializer(typeof(object), serializer)); + + exception.Should().BeOfType(); + exception.Message.Should().Contain("There is already a serializer registered for type Object"); + } + + [Fact] + public void TryRegisterSerializer_should_return_true_when_serializer_is_not_already_registered() + { + var subject = new BsonSerializerRegistry(); + var serializer = new ObjectSerializer(); + + var result = subject.TryRegisterSerializer(typeof(object), serializer); + + result.Should().BeTrue(); + subject.GetSerializer(typeof(object)).Should().BeSameAs(serializer); + } + + [Fact] + public void TryRegisterSerializer_should_return_false_when_equivalent_serializer_is_already_registered() + { + var subject = new BsonSerializerRegistry(); + var serializer1 = new ObjectSerializer(ObjectSerializer.DefaultAllowedTypes); + var serializer2 = new ObjectSerializer(ObjectSerializer.DefaultAllowedTypes); + + var result1 = subject.TryRegisterSerializer(typeof(object), serializer1); + var result2 = subject.TryRegisterSerializer(typeof(object), serializer2); + + result1.Should().BeTrue(); + result2.Should().BeFalse(); + subject.GetSerializer(typeof(object)).Should().BeSameAs(serializer1); + subject.GetSerializer(typeof(object)).Should().NotBeSameAs(serializer2); + } + + [Fact] + public void TryRegisterSerializer_should_throw_when_type_is_null() + { + var subject = new BsonSerializerRegistry(); + var serializer = new ObjectSerializer(); + + var exception = Record.Exception(() => subject.TryRegisterSerializer(type: null, serializer)); + + var argumentNullException = exception.Should().BeOfType().Subject; + argumentNullException.ParamName.Should().Be("type"); + } + + [Fact] + public void TryRegisterSerializer_should_throw_when_type_is_BsonValue() + { + var subject = new BsonSerializerRegistry(); + var serializer = BsonValueSerializer.Instance; + + var exception = Record.Exception(() => subject.TryRegisterSerializer(typeof(BsonValue), serializer)); + + exception.Should().BeOfType(); + exception.Message.Should().Contain("A serializer cannot be registered for type BsonValue because it is a subclass of BsonValue"); + } + + [Fact] + public void TryRegisterSerializer_should_throw_when_type_is_not_closed_generic_type() + { + var subject = new BsonSerializerRegistry(); + var serializer = Mock.Of>(); + + var exception = Record.Exception(() => subject.TryRegisterSerializer(typeof(List<>), serializer)); + + var argumentException = exception.Should().BeOfType().Subject; + argumentException.ParamName.Should().Be("type"); + argumentException.Message.Should().Contain("Generic type List has unassigned type parameters"); + } + + [Fact] + public void TryRegisterSerializer_should_throw_when_serializer_is_null() + { + var subject = new BsonSerializerRegistry(); + + var exception = Record.Exception(() => subject.TryRegisterSerializer(typeof(object), serializer: null)); + + var argumentNullException = exception.Should().BeOfType().Subject; + argumentNullException.ParamName.Should().Be("serializer"); + } + + [Fact] + public void TryRegisterSerializer_should_throw_when_different_serializer_is_already_registered() + { + var subject = new BsonSerializerRegistry(); + var serializer1 = new ObjectSerializer(ObjectSerializer.DefaultAllowedTypes); + var serializer2 = new ObjectSerializer(ObjectSerializer.AllAllowedTypes); + + var result1 = subject.TryRegisterSerializer(typeof(object), serializer1); + var exception = Record.Exception(() => subject.TryRegisterSerializer(typeof(object), serializer2)); + + result1.Should().BeTrue(); + subject.GetSerializer(typeof(object)).Should().BeSameAs(serializer1); + subject.GetSerializer(typeof(object)).Should().NotBeSameAs(serializer2); + exception.Should().BeOfType(); + exception.Message.Should().Contain("There is already a different serializer registered for type Object"); + } + } +} diff --git a/tests/MongoDB.Bson.Tests/Serialization/Conventions/ConventionRunnerTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Conventions/ConventionRunnerTests.cs index 913b74e2555..f86afe3836a 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Conventions/ConventionRunnerTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Conventions/ConventionRunnerTests.cs @@ -18,7 +18,7 @@ using System.Threading; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Conventions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests.Serialization.Conventions diff --git a/tests/MongoDB.Bson.Tests/Serialization/Options/RepresentationConverterTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Options/RepresentationConverterTests.cs index 0d5e990bd12..958ea7b3685 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Options/RepresentationConverterTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Options/RepresentationConverterTests.cs @@ -235,8 +235,6 @@ public void TestAllowTruncationFalse() { var converter = new RepresentationConverter(false, false); - Assert.Throws(() => converter.ToDouble(long.MaxValue)); - Assert.Throws(() => converter.ToDouble(ulong.MaxValue)); Assert.Throws(() => converter.ToInt16((double)1.5)); Assert.Throws(() => converter.ToInt32((double)1.5)); Assert.Throws(() => converter.ToInt32((float)1.5F)); diff --git a/tests/MongoDB.Bson.Tests/Serialization/Serializers/BsonBinaryDataSerializerTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Serializers/BsonBinaryDataSerializerTests.cs index 1bc319213ba..e970e7e3c33 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Serializers/BsonBinaryDataSerializerTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Serializers/BsonBinaryDataSerializerTests.cs @@ -18,7 +18,7 @@ using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Moq; using Xunit; diff --git a/tests/MongoDB.Bson.Tests/Serialization/Serializers/BsonPrimitiveSerializerTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Serializers/BsonPrimitiveSerializerTests.cs index c2a6ff4a22f..75c18bfbf9d 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Serializers/BsonPrimitiveSerializerTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Serializers/BsonPrimitiveSerializerTests.cs @@ -21,7 +21,7 @@ using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests.Serialization.Serializers diff --git a/tests/MongoDB.Bson.Tests/Serialization/Serializers/BsonValueSerializerTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Serializers/BsonValueSerializerTests.cs index 3e8fafcd97a..1278d851aa6 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Serializers/BsonValueSerializerTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Serializers/BsonValueSerializerTests.cs @@ -23,7 +23,7 @@ using MongoDB.Bson.Serialization.IdGenerators; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests.Serialization diff --git a/tests/MongoDB.Bson.Tests/Serialization/Serializers/CollectionSerializerGenericTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Serializers/CollectionSerializerGenericTests.cs index 42d1cacc9f0..6d6a33a06ad 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Serializers/CollectionSerializerGenericTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Serializers/CollectionSerializerGenericTests.cs @@ -21,7 +21,7 @@ using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests.Serialization.CollectionSerializersGeneric @@ -32,6 +32,7 @@ public class C public string P { get; set; } } + [Collection(RegisterObjectSerializerFixture.CollectionName)] public class EnumerableSerializerTests { public class T @@ -277,6 +278,7 @@ public void TestMixedPrimitiveTypes( } #if WINDOWS + [Collection(RegisterObjectSerializerFixture.CollectionName)] public class EnumerableSerializerNominalTypeObjectTests { public class T diff --git a/tests/MongoDB.Bson.Tests/Serialization/Serializers/CollectionSerializerTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Serializers/CollectionSerializerTests.cs index 83848a5d26f..2f97d67c933 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Serializers/CollectionSerializerTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Serializers/CollectionSerializerTests.cs @@ -21,7 +21,7 @@ using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests.Serialization.CollectionSerializers @@ -32,6 +32,7 @@ public class C public string P { get; set; } } + [Collection(RegisterObjectSerializerFixture.CollectionName)] public class CollectionSerializerTests { public class T @@ -274,6 +275,7 @@ public void TestMixedPrimitiveTypes( } } + [Collection(RegisterObjectSerializerFixture.CollectionName)] public class CollectionSerializerNominalTypeObjectTests { public class T @@ -310,7 +312,7 @@ public void TestEmpty() var arrayListDiscriminator = "System.Collections.ArrayList"; var queueDiscriminator = "System.Collections.Queue"; var stackDiscriminator = "System.Collections.Stack"; -#elif NETCOREAPP3_1 +#elif NETCOREAPP3_1_OR_GREATER // In netcore3.1 ArrayList is situated in System.Private.CoreLib that is a well-known library var arrayListDiscriminator = "System.Collections.ArrayList"; var queueDiscriminator = typeof(Queue).AssemblyQualifiedName; @@ -342,7 +344,7 @@ public void TestOneInt() var arrayListDiscriminator = "System.Collections.ArrayList"; var queueDiscriminator = "System.Collections.Queue"; var stackDiscriminator = "System.Collections.Stack"; -#elif NETCOREAPP3_1 +#elif NETCOREAPP3_1_OR_GREATER // In netcore3.1 ArrayList is situated in System.Private.CoreLib that is a well-known library var arrayListDiscriminator = "System.Collections.ArrayList"; var queueDiscriminator = typeof(Queue).AssemblyQualifiedName; diff --git a/tests/MongoDB.Bson.Tests/Serialization/Serializers/DateTimeOffsetSerializerTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Serializers/DateTimeOffsetSerializerTests.cs index 3e3dcb3925e..ac91c84c25b 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Serializers/DateTimeOffsetSerializerTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Serializers/DateTimeOffsetSerializerTests.cs @@ -19,7 +19,7 @@ using MongoDB.Bson.IO; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests.Serialization.Serializers @@ -87,6 +87,34 @@ public void Deserialize_should_return_expected_result(string json, string expect result.Should().Be(DateTimeOffset.Parse(expectedResult)); } + [Theory] + [InlineData("{ x : [{ $numberDouble : '0' }, { $numberDouble : '0' }] }", "0001-01-01T00:00:00+00:00")] + [InlineData("{ x : [{ $numberDouble : '621355968000000000' }, { $numberDouble : '0' }] }", "1970-01-01T00:00:00+00:00")] + [InlineData("{ x : [{ $numberDouble : '621355968000000000' }, { $numberDouble : '60' }] }", "1970-01-01T00:00:00+01:00")] + [InlineData("{ x : [{ $numberDouble : '621355968000000000' }, { $numberDouble : '-60' }] }", "1970-01-01T00:00:00-01:00")] + [InlineData("{ x : { DateTime : 'ignored', Ticks : { $numberDouble : 0 }, Offset : { $numberDouble : '0' } } }", "0001-01-01T00:00:00Z")] + [InlineData("{ x : { DateTime : 'ignored', Ticks : { $numberDouble : '621355968000000000' }, Offset : { $numberDouble : '0' } } }", "1970-01-01T00:00:00Z")] + [InlineData("{ x : { DateTime : 'ignored', Ticks : { $numberDouble : '621355968000000000' }, Offset : { $numberDouble : '60' } } }", "1970-01-01T00:00:00+01:00")] + [InlineData("{ x : { DateTime : 'ignored', Ticks : { $numberDouble : '621355968000000000' }, Offset : { $numberDouble : '-60' } } }", "1970-01-01T00:00:00-01:00")] + public void Deserialize_should_be_forgiving_of_actual_numeric_types(string json, string expectedResult) + { + var x = DateTimeOffset.Parse(expectedResult); + var m = BsonUtils.ToMillisecondsSinceEpoch(x.UtcDateTime); + var subject = new DateTimeOffsetSerializer(); + + DateTimeOffset result; + using (var reader = new JsonReader(json)) + { + reader.ReadStartDocument(); + reader.ReadName("x"); + var context = BsonDeserializationContext.CreateRoot(reader); + result = subject.Deserialize(context); + reader.ReadEndDocument(); + } + + result.Should().Be(DateTimeOffset.Parse(expectedResult)); + } + [Theory] [InlineData(BsonType.Array, "0001-01-01T00:00:00Z", "{ \"x\" : [{ \"$numberLong\" : \"0\" }, { \"$numberInt\" : \"0\" }] }")] [InlineData(BsonType.Array, "1970-01-01T00:00:00Z", "{ \"x\" : [{ \"$numberLong\" : \"621355968000000000\" }, { \"$numberInt\" : \"0\" }] }")] diff --git a/tests/MongoDB.Bson.Tests/Serialization/Serializers/DictionaryGenericSerializerTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Serializers/DictionaryGenericSerializerTests.cs index 191fa8484a3..42f64421431 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Serializers/DictionaryGenericSerializerTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Serializers/DictionaryGenericSerializerTests.cs @@ -22,8 +22,10 @@ using MongoDB.Bson.IO; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Bson.Serialization.Options; +using MongoDB.Bson.Serialization.Serializers; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests.Serialization.DictionaryGenericSerializers @@ -34,8 +36,35 @@ public class C public string P { get; set; } } + [Collection(RegisterObjectSerializerFixture.CollectionName)] public class DictionaryGenericSerializerTests { + static DictionaryGenericSerializerTests() + { + _ = new RegisterObjectSerializerFixture(); // ensure correct ObjectSerializer is registered + + var objectSerializer = BsonSerializer.LookupSerializer(); + var dictionaryRepresentation = DictionaryRepresentation.Document; + var keySerializer = objectSerializer; + var valueSerializer = objectSerializer; + var dictionarySerializer = new DictionaryInterfaceImplementerSerializer, object, object>(dictionaryRepresentation, keySerializer, valueSerializer); + var idictionarySerializer = new ImpliedImplementationInterfaceSerializer, Dictionary>(dictionarySerializer); + var readOnlyDictionarySerializer = new ReadOnlyDictionaryInterfaceImplementerSerializer, object, object>(dictionaryRepresentation, keySerializer, valueSerializer); + var ireadOnlyDictionarySerializer = new ImpliedImplementationInterfaceSerializer, ReadOnlyDictionary>(readOnlyDictionarySerializer); + var sortedDictionarySerializer = new DictionaryInterfaceImplementerSerializer, object, object>(dictionaryRepresentation, keySerializer, valueSerializer); + var sortedListSerializer = new DictionaryInterfaceImplementerSerializer, object, object>(dictionaryRepresentation, keySerializer, valueSerializer); + + BsonClassMap.RegisterClassMap(cm => + { + cm.MapProperty(t => t.D).SetSerializer(dictionarySerializer); + cm.MapProperty(t => t.ID).SetSerializer(idictionarySerializer); + cm.MapProperty(t => t.IROD).SetSerializer(ireadOnlyDictionarySerializer); + cm.MapProperty(t => t.ROD).SetSerializer(readOnlyDictionarySerializer); + cm.MapProperty(t => t.SD).SetSerializer(sortedDictionarySerializer); + cm.MapProperty(t => t.SL).SetSerializer(sortedListSerializer); + }); + } + public class T { public Dictionary D { get; set; } diff --git a/tests/MongoDB.Bson.Tests/Serialization/Serializers/DictionarySerializerTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Serializers/DictionarySerializerTests.cs index 877d14fbed6..e8ba363c16d 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Serializers/DictionarySerializerTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Serializers/DictionarySerializerTests.cs @@ -23,8 +23,10 @@ using MongoDB.Bson.IO; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Bson.Serialization.Options; +using MongoDB.Bson.Serialization.Serializers; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests.Serialization.DictionarySerializers @@ -51,8 +53,33 @@ public override int GetHashCode() } } + [Collection(RegisterObjectSerializerFixture.CollectionName)] public class DictionarySerializerTests { + static DictionarySerializerTests() + { + _ = new RegisterObjectSerializerFixture(); // ensure correct ObjectSerializer is registered + + var objectSerializer = BsonSerializer.LookupSerializer(); + var dictionaryRepresentation = DictionaryRepresentation.Document; + var keySerializer = objectSerializer; + var valueSerializer = objectSerializer; + var hashTableSerializer = new DictionaryInterfaceImplementerSerializer(dictionaryRepresentation, keySerializer, valueSerializer); + var iDictionarySerializer = new ImpliedImplementationInterfaceSerializer(hashTableSerializer); + var listDictionarySerializer = new DictionaryInterfaceImplementerSerializer(dictionaryRepresentation, keySerializer, valueSerializer); + var orderedDictionarySerializer = new DictionaryInterfaceImplementerSerializer(dictionaryRepresentation, keySerializer, valueSerializer); + var sortedListDictionarySerializer = new DictionaryInterfaceImplementerSerializer(dictionaryRepresentation, keySerializer, valueSerializer); + + BsonClassMap.RegisterClassMap(cm => + { + cm.MapProperty(t => t.HT).SetSerializer(hashTableSerializer); + cm.MapProperty(t => t.ID).SetSerializer(iDictionarySerializer); + cm.MapProperty(t => t.LD).SetSerializer(listDictionarySerializer); + cm.MapProperty(t => t.OD).SetSerializer(orderedDictionarySerializer); + cm.MapProperty(t => t.SL).SetSerializer(sortedListDictionarySerializer); + }); + } + public class T { public Hashtable HT { get; set; } diff --git a/tests/MongoDB.Bson.Tests/Serialization/Serializers/DiscriminatorTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Serializers/DiscriminatorTests.cs index 900cdd69277..826b0969da6 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Serializers/DiscriminatorTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Serializers/DiscriminatorTests.cs @@ -14,13 +14,14 @@ */ using System.Linq; -using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Bson.TestHelpers; using Xunit; namespace MongoDB.Bson.Tests.Serialization { + [Collection(RegisterObjectSerializerFixture.CollectionName)] public class DiscriminatorTests { [BsonDiscriminator("A~")] // make discriminators unique with respect to object diff --git a/tests/MongoDB.Bson.Tests/Serialization/Serializers/ElementAppendingSerializerTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Serializers/ElementAppendingSerializerTests.cs index 7314e09713f..d99e1422699 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Serializers/ElementAppendingSerializerTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Serializers/ElementAppendingSerializerTests.cs @@ -22,8 +22,8 @@ using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; using MongoDB.Bson.Tests.IO; +using MongoDB.TestHelpers.XunitExtensions; using Moq; using Xunit; diff --git a/tests/MongoDB.Bson.Tests/Serialization/Serializers/EnumSerializerTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Serializers/EnumSerializerTests.cs index e9f6850c2e1..e18902601bf 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Serializers/EnumSerializerTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Serializers/EnumSerializerTests.cs @@ -20,7 +20,7 @@ using MongoDB.Bson.IO; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests.Serialization.Serializers @@ -119,7 +119,8 @@ public void constructor_with_no_arguments_should_return_expected_result( { var subject = new EnumSerializer(); - subject.Representation.Should().Be((BsonType)0); + var expectedRepresentation = GetExpectedRepresentation(0); + subject.Representation.Should().Be((BsonType)expectedRepresentation); } [Theory] @@ -133,7 +134,8 @@ public void constructor_with_representation_should_return_expected_result { var subject = new EnumSerializer(representation); - subject.Representation.Should().Be(representation); + var expectedRepresentation = GetExpectedRepresentation(representation); + subject.Representation.Should().Be(expectedRepresentation); } [Theory] @@ -488,14 +490,17 @@ public void WithRepresentation_should_return_expected_result( var result = subject.WithRepresentation(newRepresentation); - if (newRepresentation == originalRepresentation) + var effectiveOriginalRepresentation = GetExpectedRepresentation(originalRepresentation); + var expectedRepresentation = GetExpectedRepresentation(newRepresentation); + + if (expectedRepresentation == effectiveOriginalRepresentation) { result.Should().BeSameAs(subject); } else { result.Should().NotBeSameAs(subject); - result.Representation.Should().Be(newRepresentation); + result.Representation.Should().Be(expectedRepresentation); } } @@ -514,6 +519,19 @@ private TEnum Deserialize(IBsonSerializer serializer, byte[] bson) } } + private BsonType GetExpectedRepresentation(BsonType representation) + { + if (representation == 0) + { + var underlyingType = Enum.GetUnderlyingType(typeof(TEnum)); + return (underlyingType == typeof(long) || underlyingType == typeof(ulong)) ? BsonType.Int64 : BsonType.Int32; + } + else + { + return representation; + } + } + private byte[] Serialize(IBsonSerializer serializer, TEnum value) { using (var stream = new MemoryStream()) diff --git a/tests/MongoDB.Bson.Tests/Serialization/Serializers/ExpandoObjectSerializerTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Serializers/ExpandoObjectSerializerTests.cs index 69248b92b3e..4d489ed4c08 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Serializers/ExpandoObjectSerializerTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Serializers/ExpandoObjectSerializerTests.cs @@ -16,16 +16,13 @@ using System.Collections.Generic; using System.Dynamic; using System.Linq; -using System.IO; -using System.Text; -using MongoDB.Bson; -using MongoDB.Bson.IO; using MongoDB.Bson.Serialization; -using MongoDB.Bson.Serialization.Serializers; +using MongoDB.Bson.TestHelpers; using Xunit; namespace MongoDB.Bson.Tests.Serialization { + [Collection(RegisterObjectSerializerFixture.CollectionName)] public class ExpandoSerializerTests { [Fact] diff --git a/tests/MongoDB.Bson.Tests/Serialization/Serializers/ExtraElementsTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Serializers/ExtraElementsTests.cs index 4dccbae5319..2b299ba3ae2 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Serializers/ExtraElementsTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Serializers/ExtraElementsTests.cs @@ -20,7 +20,7 @@ using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests.Serialization diff --git a/tests/MongoDB.Bson.Tests/Serialization/Serializers/ExtraElementsWithImmutableClassTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Serializers/ExtraElementsWithImmutableClassTests.cs index 5ba59182f00..6b039feb85c 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Serializers/ExtraElementsWithImmutableClassTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Serializers/ExtraElementsWithImmutableClassTests.cs @@ -20,7 +20,7 @@ using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests.Serialization diff --git a/tests/MongoDB.Bson.Tests/Serialization/Serializers/ExtraElementsWithPartiallyImmutableClassTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Serializers/ExtraElementsWithPartiallyImmutableClassTests.cs index eb85b68e6d3..bf24e6df7e4 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Serializers/ExtraElementsWithPartiallyImmutableClassTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Serializers/ExtraElementsWithPartiallyImmutableClassTests.cs @@ -20,7 +20,7 @@ using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests.Serialization diff --git a/tests/MongoDB.Bson.Tests/Serialization/Serializers/GuidSerializerTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Serializers/GuidSerializerTests.cs index 6fe7536bc53..c90a0ab3ddb 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Serializers/GuidSerializerTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Serializers/GuidSerializerTests.cs @@ -21,7 +21,7 @@ using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests.Serialization.Serializers diff --git a/tests/MongoDB.Bson.Tests/Serialization/Serializers/NetPrimitiveSerializerTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Serializers/NetPrimitiveSerializerTests.cs index 1773d4e2f0b..bb449fe9d0f 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Serializers/NetPrimitiveSerializerTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Serializers/NetPrimitiveSerializerTests.cs @@ -1130,7 +1130,7 @@ public void TestMin() var json = obj.ToJson(); var expected = "{ 'D' : #D, 'I' : 0, 'L' : NumberLong(0), 'S' : '#S' }"; expected = expected.Replace("#D", "-1.7976931348623157E+308"); -#if NETCOREAPP3_1 +#if NETCOREAPP3_1_OR_GREATER expected = expected.Replace("#S", "-3.4028235E+38"); #else expected = expected.Replace("#S", "-3.40282347E+38"); @@ -1233,7 +1233,7 @@ public void TestMax() var expected = "{ 'D' : #D, 'I' : 0, 'L' : NumberLong(0), 'S' : '#S' }"; expected = expected.Replace("#D", "1.7976931348623157E+308"); -#if NETCOREAPP3_1 +#if NETCOREAPP3_1_OR_GREATER expected = expected.Replace("#S", "3.4028235E+38"); #else expected = expected.Replace("#S", "3.40282347E+38"); diff --git a/tests/MongoDB.Bson.Tests/Serialization/Serializers/NullableTypeSerializerTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Serializers/NullableTypeSerializerTests.cs index 3ee1b8c4d26..5e6317fc67e 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Serializers/NullableTypeSerializerTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Serializers/NullableTypeSerializerTests.cs @@ -21,7 +21,7 @@ using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests.Serialization diff --git a/tests/MongoDB.Bson.Tests/Serialization/Serializers/ObjectSerializerTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Serializers/ObjectSerializerTests.cs index 4dbd44412db..75eedcca133 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Serializers/ObjectSerializerTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Serializers/ObjectSerializerTests.cs @@ -23,12 +23,15 @@ using MongoDB.Bson.Serialization.Conventions; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Moq; using Xunit; +using Xunit.Sdk; +using Reflector = MongoDB.Bson.TestHelpers.Reflector; namespace MongoDB.Bson.Tests.Serialization { + [Collection(RegisterObjectSerializerFixture.CollectionName)] public class ObjectSerializerTests { public class C @@ -292,7 +295,7 @@ public void constructor_with_discriminator_convention_should_initialize_instance [Fact] public void constructor_with_discriminator_convention_should_throw_when_discriminator_convention_is_null() { - var exception = Record.Exception(() => new ObjectSerializer(null)); + var exception = Record.Exception(() => new ObjectSerializer(discriminatorConvention: null)); var e = exception.Should().BeOfType().Subject; e.ParamName.Should().Be("discriminatorConvention"); @@ -320,7 +323,7 @@ public void constructor_with_discriminator_convention_and_guid_representation_sh e.ParamName.Should().Be("discriminatorConvention"); } - [SkippableTheory] + [Theory] [ParameterAttributeData] [ResetGuidModeAfterTest] public void Deserialize_binary_data_should_return_expected_result_when_guid_representation_is_unspecified_and_mode_is_v2( @@ -363,6 +366,56 @@ public void Deserialize_binary_data_should_return_expected_result_when_guid_repr #pragma warning restore 618 } + [Fact] + public void Equals_should_return_true_when_instances_are_equal() + { + var discriminatorConvention = new ScalarDiscriminatorConvention("_t"); + var subject1 = new ObjectSerializer(discriminatorConvention, GuidRepresentation.Standard, ObjectSerializer.DefaultAllowedTypes); + var subject2 = new ObjectSerializer(discriminatorConvention, GuidRepresentation.Standard, ObjectSerializer.DefaultAllowedTypes); + + var result = subject1.Equals(subject2); + var hashCode1 = subject1.GetHashCode(); + var hashCode2 = subject2.GetHashCode(); + + result.Should().BeTrue(); + hashCode2.Should().Be(hashCode1); // required by the contract of Equals + } + + [Theory] + [ParameterAttributeData] + public void Equals_should_return_false_when_instances_are_not_equal( + [Values("allowedTypes", "discriminatorConvention", "guidRepresentation")] + string notEqualFieldName) + { + IDiscriminatorConvention discriminatorConvention = new ScalarDiscriminatorConvention("_t"); + var guidRepresentation = GuidRepresentation.Standard; + var allowedTypes = ObjectSerializer.DefaultAllowedTypes; + var subject1 = new ObjectSerializer(discriminatorConvention, guidRepresentation, allowedTypes); + + switch (notEqualFieldName) + { + case "allowedTypes": allowedTypes = ObjectSerializer.NoAllowedTypes; break; + case "discriminatorConvention": discriminatorConvention = new HierarchicalDiscriminatorConvention("_t"); break; + case "guidRepresentation": guidRepresentation = GuidRepresentation.CSharpLegacy; break; + default: throw new ArgumentException($"Invalid notEqualFieldName: {notEqualFieldName}.", nameof(notEqualFieldName)); + } + var subject2 = new ObjectSerializer(discriminatorConvention, guidRepresentation, allowedTypes); + + var result = subject1.Equals(subject2); + var hashCode1 = subject1.GetHashCode(); + var hashCode2 = subject2.GetHashCode(); + + result.Should().BeFalse(); + if (notEqualFieldName == "allowedTypes") + { + hashCode2.Should().Be(hashCode1); // because allowedTypes is not part of the hash code computation + } + else + { + hashCode2.Should().NotBe(hashCode1); // not strictly required but desirable + } + } + [Theory] [ParameterAttributeData] [ResetGuidModeAfterTest] @@ -462,7 +515,7 @@ public void Deserialize_binary_data_should_throw_when_guid_representation_is_spe #pragma warning restore 618 } - [SkippableTheory] + [Theory] [ParameterAttributeData] [ResetGuidModeAfterTest] public void Serialize_guid_should_have_expected_result_when_guid_representation_is_unspecified_and_mode_is_v2( diff --git a/tests/MongoDB.Bson.Tests/Serialization/Serializers/SerializeInterfaceTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Serializers/SerializeInterfaceTests.cs index 3d297c0b9a5..5d6123fbbec 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Serializers/SerializeInterfaceTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Serializers/SerializeInterfaceTests.cs @@ -14,12 +14,13 @@ */ using System.Linq; -using MongoDB.Bson; using MongoDB.Bson.Serialization; +using MongoDB.Bson.TestHelpers; using Xunit; namespace MongoDB.Bson.Tests.Serialization { + [Collection(RegisterObjectSerializerFixture.CollectionName)] public class SerializeInterfaceTests { private interface IX diff --git a/tests/MongoDB.Bson.Tests/TargetFrameworkTests.cs b/tests/MongoDB.Bson.Tests/TargetFrameworkTests.cs index 9dcea08bb3a..d52eb38ff85 100644 --- a/tests/MongoDB.Bson.Tests/TargetFrameworkTests.cs +++ b/tests/MongoDB.Bson.Tests/TargetFrameworkTests.cs @@ -23,7 +23,7 @@ public class TargetFrameworkTests [Fact] public void TargetFramework_should_be_valid() { - var actualFramework = MongoDB.Bson.TargetFramework.Moniker; + var actualFramework = TargetFramework.Moniker; var expectedFramework = GetExpectedTargetFramework(); actualFramework.Should().Be(expectedFramework); } @@ -33,7 +33,7 @@ private string GetExpectedTargetFramework() { #if NETCOREAPP2_1 return "netstandard20"; -#elif NETCOREAPP3_1 +#elif NETCOREAPP3_1_OR_GREATER return "netstandard21"; #elif NET472 return "net472"; diff --git a/tests/MongoDB.Driver.Core.TestHelpers/JsonDrivenTests/CommandStartedEventAsserter.cs b/tests/MongoDB.Driver.Core.TestHelpers/JsonDrivenTests/CommandStartedEventAsserter.cs index ff4352a3066..164e458e426 100644 --- a/tests/MongoDB.Driver.Core.TestHelpers/JsonDrivenTests/CommandStartedEventAsserter.cs +++ b/tests/MongoDB.Driver.Core.TestHelpers/JsonDrivenTests/CommandStartedEventAsserter.cs @@ -150,10 +150,10 @@ private void AssertCommandAspect(BsonDocument actualCommand, string name, BsonVa AdaptExpectedUpdateModels(actualValue.AsBsonArray.Cast().ToList(), expectedValue.AsBsonArray.Cast().ToList()); } - var namesToUseOrderInsensitiveComparisonWith = new[] { "writeConcern", "maxTimeMS", "updates", "indexes", "getMore", "deletes", "compactionTokens" }; + var namesToUseOrderInsensitiveComparisonWith = new[] { "writeConcern", "maxTimeMS", "updates", "indexes", "getMore", "deletes", "compactionTokens", "encryptionInformation" }; var useOrderInsensitiveComparison = namesToUseOrderInsensitiveComparisonWith.Contains(name); - if (!(useOrderInsensitiveComparison ? BsonValueEquivalencyComparer.Compare(actualValue, expectedValue) : actualValue.Equals(expectedValue))) + if (!(useOrderInsensitiveComparison ? BsonValueEquivalencyComparer.Compare(actualValue, expectedValue, allowTypesMismatching: name != "encryptionInformation") : actualValue.Equals(expectedValue))) { switch (name) { diff --git a/tests/MongoDB.Driver.Core.TestHelpers/Logging/ILoggerExtensions.cs b/tests/MongoDB.Driver.Core.TestHelpers/Logging/ILoggerExtensions.cs deleted file mode 100644 index 8844d9985a8..00000000000 --- a/tests/MongoDB.Driver.Core.TestHelpers/Logging/ILoggerExtensions.cs +++ /dev/null @@ -1,59 +0,0 @@ -/* Copyright 2021-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. -*/ - -namespace MongoDB.Driver.Core.TestHelpers.Logging -{ - public static class ILoggerExtensions - { - public static ILogger Decorate(this ILogger logger, string decoration) => - logger != null ? new LoggerDecorator(logger, decoration) : null; - - public static void Error(this ILogger logger, string format, params object[] arguments) => - logger?.Log(LogLevel.Error, null, format, arguments); - - public static void Error(this ILogger logger, string message, params object[] arguments) => - logger?.Log(LogLevel.Error, null, message, arguments); - - public static void Warning(this ILogger logger, string format, params object[] arguments) => - logger?.Log(LogLevel.Warning, null, format, arguments); - - public static void Warning(this ILogger logger, string message, params object[] arguments) => - logger?.Log(LogLevel.Warning, null, message, arguments); - - public static void Info(this ILogger logger, string format, params object[] arguments) => - logger?.Log(LogLevel.Information, null, format, arguments); - - public static void Info(this ILogger logger, string message, params object[] arguments) => - logger?.Log(LogLevel.Information, null, message, arguments); - - public static void Debug(this ILogger logger, string format, params object[] arguments) => - logger?.Log(LogLevel.Debug, null, format, arguments); - - public static void Debug(this ILogger logger, string message, params object[] arguments) => - logger?.Log(LogLevel.Debug, null, message, arguments); - - public static void Trace(this ILogger logger, string format, params object[] arguments) => - logger?.Log(LogLevel.Trace, null, format, arguments); - - public static void Trace(this ILogger logger, string format, params object[] arguments) => - logger?.Log(LogLevel.Trace, null, format, arguments); - } - - internal static class ILoggerFactoryExtensions - { - public static ILogger CreateLogger(this ILoggerFactory loggerFactory, string decoration) => - loggerFactory?.CreateLogger().Decorate(decoration); - } -} diff --git a/tests/MongoDB.Driver.Core.TestHelpers/Logging/LogLevel.cs b/tests/MongoDB.Driver.Core.TestHelpers/Logging/ILoggingService.cs similarity index 73% rename from tests/MongoDB.Driver.Core.TestHelpers/Logging/LogLevel.cs rename to tests/MongoDB.Driver.Core.TestHelpers/Logging/ILoggingService.cs index 7e4803994cc..7be0301efb9 100644 --- a/tests/MongoDB.Driver.Core.TestHelpers/Logging/LogLevel.cs +++ b/tests/MongoDB.Driver.Core.TestHelpers/Logging/ILoggingService.cs @@ -1,4 +1,4 @@ -/* Copyright 2021-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. @@ -13,16 +13,13 @@ * limitations under the License. */ +using MongoDB.Driver.Core.Configuration; + namespace MongoDB.Driver.Core.TestHelpers.Logging { - public enum LogLevel + public interface ILoggingService { - Trace, - Debug, - Information, - Warning, - Error, - Critical, - None + public LoggingSettings LoggingSettings { get; } + public LogEntry[] Logs { get; } } } diff --git a/tests/MongoDB.Driver.Core.TestHelpers/Logging/LogEntry.cs b/tests/MongoDB.Driver.Core.TestHelpers/Logging/LogEntry.cs new file mode 100644 index 00000000000..5f1fae09002 --- /dev/null +++ b/tests/MongoDB.Driver.Core.TestHelpers/Logging/LogEntry.cs @@ -0,0 +1,63 @@ +/* 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 Microsoft.Extensions.Logging; + +namespace MongoDB.Driver.Core.TestHelpers.Logging +{ + public sealed class LogEntry + { + private readonly Lazy _message; + + public DateTime Timestamp { get; } + public LogLevel LogLevel { get; } + public string Category { get; } + public int ClusterId { get; } + public IEnumerable> State { get; } + public Exception Exception { get; } + public Func Formatter { get; } + + public string FormattedMessage => _message.Value; + + public LogEntry(LogLevel logLevel, + string category, + IEnumerable> state, + Exception exception, + Func formatter) + { + Timestamp = DateTime.UtcNow; + LogLevel = logLevel; + Category = category; + State = state; + Exception = exception; + Formatter = formatter; + _message = new Lazy(() => Formatter(State, Exception)); + + ClusterId = GetParameter("ClusterId") is int clusterId ? clusterId : -1; + } + + public object GetParameter(string key) => + State.FirstOrDefault(s => s.Key == key).Value; + + public T GetParameter(string key) where T : class => + GetParameter(key) as T; + + public override string ToString() => + $"{Timestamp.ToString("hh:mm:ss.FFFFFFF")}_{LogLevel}<{Category}> {FormattedMessage}"; + } +} diff --git a/tests/MongoDB.Driver.Core.TestHelpers/Logging/LoggableTestClass.cs b/tests/MongoDB.Driver.Core.TestHelpers/Logging/LoggableTestClass.cs index b7a92c62a49..56ba6b4e1ee 100644 --- a/tests/MongoDB.Driver.Core.TestHelpers/Logging/LoggableTestClass.cs +++ b/tests/MongoDB.Driver.Core.TestHelpers/Logging/LoggableTestClass.cs @@ -1,4 +1,4 @@ -/* Copyright 2021-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. @@ -17,6 +17,11 @@ using System.Diagnostics; using System.Linq; using Microsoft.Diagnostics.Runtime; +using Microsoft.Extensions.Logging; +using MongoDB.TestHelpers.XunitExtensions.TimeoutEnforcing; +using MongoDB.Driver.Core.Configuration; +using MongoDB.Driver.Core.Events; +using MongoDB.Driver.Core.Logging; using MongoDB.Driver.Core.Misc; using Xunit.Abstractions; using Xunit.Sdk; @@ -24,53 +29,60 @@ namespace MongoDB.Driver.Core.TestHelpers.Logging { [DebuggerStepThrough] - public abstract class LoggableTestClass : IDisposable + public abstract class LoggableTestClass : IDisposable, ILoggingService, ITestExceptionHandler { - private readonly XunitLogger _loggerBase; - private readonly ITestOutputHelper _output; - - public LoggableTestClass(ITestOutputHelper output) + public LoggableTestClass(ITestOutputHelper output, bool includeAllCategories = false) { - _output = Ensure.IsNotNull(output, nameof(output)); + var logCategoriesToExclude = includeAllCategories ? null : new[] + { + "MongoDB.Command", + "MongoDB.Connection" + }; - _loggerBase = new XunitLogger(_output); + TestOutput = Ensure.IsNotNull(output, nameof(output)); + Accumulator = new XUnitOutputAccumulator(logCategoriesToExclude); MinLogLevel = LogLevel.Warning; - LoggerFactory = new XUnitLoggerFactory(_loggerBase); + LoggingSettings = new LoggingSettings(new XUnitLoggerFactory(Accumulator), 10000); // Spec test require larger truncation default + LoggerFactory = LoggingSettings.ToInternalLoggerFactory(); Logger = LoggerFactory.CreateLogger(); } + private ITestOutputHelper TestOutput { get; } + private XUnitOutputAccumulator Accumulator { get; } + protected ILogger Logger { get; } - protected ILoggerFactory LoggerFactory { get; } protected LogLevel MinLogLevel { get; set; } + public ILoggerFactory LoggerFactory { get; } + public LoggingSettings LoggingSettings { get; } + public LogEntry[] Logs => Accumulator.Logs; + protected ILogger CreateLogger() => LoggerFactory.CreateLogger(); + private protected EventLogger CreateEventLogger(IEventSubscriber eventSubscriber) where TCategory : LogCategories.EventCategory => + LoggerFactory.CreateEventLogger(eventSubscriber); + + protected virtual void DisposeInternal() { } - public void OnException(Exception ex) + public void Dispose() { - _output.WriteLine("Formatted exception: {0}", FormatException(ex)); + DisposeInternal(); - if (ex is TestTimeoutException) + Flush(MinLogLevel); + } + + public void Flush(LogLevel? minLogLevel) + { + var logs = Logs; + var minLogLevelActual = minLogLevel ?? LogLevel.Trace; + + foreach (var logEntry in logs) { - try + if (logEntry.LogLevel >= minLogLevelActual) { - LogStackTrace(); - } - catch - { - // fail silently + TestOutput.WriteLine(logEntry.ToString()); } } - - if (MinLogLevel > LogLevel.Debug) - { - MinLogLevel = LogLevel.Debug; - } - } - - public virtual void Dispose() - { - _loggerBase.Flush(MinLogLevel); } private string FormatException(Exception exception) @@ -91,6 +103,28 @@ private string FormatException(Exception exception) return result; } + public void HandleException(Exception ex) + { + TestOutput.WriteLine("Formatted exception: {0}", FormatException(ex)); + + if (ex is TestTimeoutException) + { + try + { + LogStackTrace(); + } + catch + { + // fail silently + } + } + + if (MinLogLevel > LogLevel.Debug) + { + MinLogLevel = LogLevel.Debug; + } + } + private void LogStackTrace() { var pid = Process.GetCurrentProcess().Id; @@ -100,7 +134,7 @@ private void LogStackTrace() var runtimeInfo = dataTarget.ClrVersions[0]; var runtime = runtimeInfo.CreateRuntime(); - _output.WriteLine("Found {0} threads", runtime.Threads.Length); + TestOutput.WriteLine("Found {0} threads", runtime.Threads.Length); foreach (var clrThread in runtime.Threads) { @@ -112,7 +146,7 @@ private void LogStackTrace() if (!string.IsNullOrWhiteSpace(methods)) { - _output.WriteLine("Thread {0} at {1}", clrThread.ManagedThreadId, methods); + TestOutput.WriteLine("Thread {0} at {1}", clrThread.ManagedThreadId, methods); } } } diff --git a/tests/MongoDB.Driver.Core.TestHelpers/Logging/LoggerDecorator.cs b/tests/MongoDB.Driver.Core.TestHelpers/Logging/LoggerDecorator.cs deleted file mode 100644 index e6925c823a5..00000000000 --- a/tests/MongoDB.Driver.Core.TestHelpers/Logging/LoggerDecorator.cs +++ /dev/null @@ -1,63 +0,0 @@ -/* Copyright 2021-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.Runtime.CompilerServices; - -namespace MongoDB.Driver.Core.TestHelpers.Logging -{ - internal class LoggerDecorator : ILogger - { - private readonly string _decoration; - private ILogger _loggerBase; - - public LoggerDecorator(ILogger loggerBase, string decoration) - { - _loggerBase = loggerBase; - _decoration = decoration; - } - - public void Log(LogLevel logLevel, string decoration, string format, params object[] arguments) => - _loggerBase.Log(logLevel, Compose(_decoration, decoration), format, arguments); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected static string Compose(string baseString, string optionalString) - { - var result = optionalString == null ? baseString : baseString + optionalString; - - if (result != null) - { - result = result.Replace("{", "{{").Replace("}", "}}"); - } - - return result; - } - } - - internal class LoggerDecorator : LoggerDecorator, ILogger - { - public LoggerDecorator(ILogger loggerBase, string decoration) : - base(loggerBase, decoration) - { - } - } - - internal class TypedLoggerDecorator : LoggerDecorator, ILogger - { - public TypedLoggerDecorator(ILogger loggerBase, string decoration = null) : - base(loggerBase, Compose(typeof(T).Name, decoration)) - { - } - } -} diff --git a/tests/MongoDB.Driver.Core.TestHelpers/Logging/NullLoggingService.cs b/tests/MongoDB.Driver.Core.TestHelpers/Logging/NullLoggingService.cs new file mode 100644 index 00000000000..9f171ab857b --- /dev/null +++ b/tests/MongoDB.Driver.Core.TestHelpers/Logging/NullLoggingService.cs @@ -0,0 +1,30 @@ +/* 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 Microsoft.Extensions.Logging.Abstractions; +using MongoDB.Driver.Core.Configuration; + +namespace MongoDB.Driver.Core.TestHelpers.Logging +{ + public sealed class NullLoggingService : ILoggingService + { + public static ILoggingService Instance = new NullLoggingService(); + + private NullLoggingService() { } + + public LoggingSettings LoggingSettings { get; } = new LoggingSettings(NullLoggerFactory.Instance); + public LogEntry[] Logs { get; } = new LogEntry[0]; + } +} diff --git a/tests/MongoDB.Driver.Core.TestHelpers/Logging/XUnitLogger.cs b/tests/MongoDB.Driver.Core.TestHelpers/Logging/XUnitLogger.cs new file mode 100644 index 00000000000..9051d9938a9 --- /dev/null +++ b/tests/MongoDB.Driver.Core.TestHelpers/Logging/XUnitLogger.cs @@ -0,0 +1,51 @@ +/* Copyright 2010e-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.Diagnostics; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions.Internal; +using MongoDB.Driver.Core.Misc; + +namespace MongoDB.Driver.Core.TestHelpers.Logging +{ + [DebuggerStepThrough] + internal sealed class XUnitLogger : ILogger + { + private readonly string _category; + private readonly XUnitOutputAccumulator _output; + + public XUnitLogger(string category, XUnitOutputAccumulator output) + { + _category = category; + _output = Ensure.IsNotNull(output, nameof(output)); + } + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + { + if (state is not IEnumerable> keyValuePairs) + { + throw new Exception($"Unsupported log state {state}"); + } + + var formatterObject = (Func)(Delegate)formatter; + _output.Log(logLevel, _category, keyValuePairs, exception, formatterObject); + } + + public bool IsEnabled(LogLevel logLevel) => true; + public IDisposable BeginScope(TState state) { return NullScope.Instance; } + } +} diff --git a/tests/MongoDB.Driver.Core.TestHelpers/Logging/XUnitLoggerFactory.cs b/tests/MongoDB.Driver.Core.TestHelpers/Logging/XUnitLoggerFactory.cs index 9cc888ef87e..61cacfececc 100644 --- a/tests/MongoDB.Driver.Core.TestHelpers/Logging/XUnitLoggerFactory.cs +++ b/tests/MongoDB.Driver.Core.TestHelpers/Logging/XUnitLoggerFactory.cs @@ -1,4 +1,4 @@ -/* Copyright 2021-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. @@ -14,31 +14,24 @@ */ using System.Diagnostics; +using Microsoft.Extensions.Logging; namespace MongoDB.Driver.Core.TestHelpers.Logging { [DebuggerStepThrough] internal sealed class XUnitLoggerFactory : ILoggerFactory { - private readonly ILogger _loggerBase; + private readonly XUnitOutputAccumulator _logsAccumulator; - public XUnitLoggerFactory(ILogger loggerBase) + public XUnitLoggerFactory(XUnitOutputAccumulator testOutput) { - _loggerBase = loggerBase; + _logsAccumulator = testOutput; } - public ILogger CreateLogger() => - new TypedLoggerDecorator(_loggerBase); - } - - public class EmptyLoggerFactory : ILoggerFactory - { - public static ILoggerFactory Instance { get; } = new EmptyLoggerFactory(); + public void AddProvider(ILoggerProvider provider) { } - private EmptyLoggerFactory() - { - } + public ILogger CreateLogger(string categoryName) => new XUnitLogger(categoryName, _logsAccumulator); - public ILogger CreateLogger() => null; + public void Dispose() { } } } diff --git a/tests/MongoDB.Driver.Core.TestHelpers/Logging/XUnitOutputAccumulator.cs b/tests/MongoDB.Driver.Core.TestHelpers/Logging/XUnitOutputAccumulator.cs new file mode 100644 index 00000000000..e9df568cf05 --- /dev/null +++ b/tests/MongoDB.Driver.Core.TestHelpers/Logging/XUnitOutputAccumulator.cs @@ -0,0 +1,68 @@ +/* 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 Microsoft.Extensions.Logging; + +namespace MongoDB.Driver.Core.TestHelpers.Logging +{ + internal sealed class XUnitOutputAccumulator + { + private readonly HashSet _categoriesToExclude; + private readonly List _logs; + + public XUnitOutputAccumulator(string[] logCategoriesToExclude) + { + _categoriesToExclude = logCategoriesToExclude?.Any() == true ? new HashSet(logCategoriesToExclude) : null; + _logs = new List(); + } + + public string[] CatergoriesToExclude => _categoriesToExclude?.ToArray(); + + public LogEntry[] Logs + { + get + { + LogEntry[] logsCloned = null; + + lock (_logs) + { + logsCloned = _logs.ToArray(); + } + + return logsCloned; + } + } + + public void Log(LogLevel logLevel, + string category, + IEnumerable> state, + Exception exception, + Func formatter) + { + if (logLevel <= LogLevel.Warning && _categoriesToExclude?.Contains(category) == true) + { + return; + } + + lock (_logs) + { + _logs.Add(new LogEntry(logLevel, category, state, exception, formatter)); + } + } + } +} diff --git a/tests/MongoDB.Driver.Core.TestHelpers/Logging/XunitLogger.cs b/tests/MongoDB.Driver.Core.TestHelpers/Logging/XunitLogger.cs deleted file mode 100644 index f26492f3914..00000000000 --- a/tests/MongoDB.Driver.Core.TestHelpers/Logging/XunitLogger.cs +++ /dev/null @@ -1,64 +0,0 @@ -/* Copyright 2021-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.Diagnostics; -using MongoDB.Driver.Core.Misc; -using Xunit.Abstractions; - -namespace MongoDB.Driver.Core.TestHelpers.Logging -{ - [DebuggerStepThrough] - internal sealed class XunitLogger : ILogger - { - private readonly ITestOutputHelper _output; - private readonly List<(DateTime, LogLevel, string, string, object[])> _logs; - - public XunitLogger(ITestOutputHelper output) - { - _output = Ensure.IsNotNull(output, nameof(output)); - _logs = new List<(DateTime, LogLevel, string, string, object[])>(); - } - - public void Log(LogLevel logLevel, string decorator, string format, params object[] arguments) - { - lock (_logs) - { - _logs.Add((DateTime.UtcNow, logLevel, decorator, format, arguments)); - } - } - - public void Flush(LogLevel? minLogLevel) - { - (DateTime, LogLevel, string, string, object[])[] logsCloned = null; - - lock (_logs) - { - logsCloned = _logs.ToArray(); - } - - var minLogLevelActual = minLogLevel ?? LogLevel.Trace; - - foreach (var (dateTime, logLevel, decorator, format, arguments) in logsCloned) - { - if (logLevel >= minLogLevelActual) - { - _output.WriteLine($"{dateTime.ToString("hh:mm:ss.FFF")}_{logLevel}<{decorator}> {format}", arguments); - } - } - } - } -} diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Helpers/MockClusterableServerFactory.cs b/tests/MongoDB.Driver.Core.TestHelpers/MockClusterableServerFactory.cs similarity index 93% rename from tests/MongoDB.Driver.Core.Tests/Core/Helpers/MockClusterableServerFactory.cs rename to tests/MongoDB.Driver.Core.TestHelpers/MockClusterableServerFactory.cs index d6acc5070a2..252251907cd 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Helpers/MockClusterableServerFactory.cs +++ b/tests/MongoDB.Driver.Core.TestHelpers/MockClusterableServerFactory.cs @@ -17,6 +17,7 @@ using System.Collections.Generic; using System.Net; using System.Threading; +using Microsoft.Extensions.Logging; using MongoDB.Bson; using MongoDB.Bson.TestHelpers; using MongoDB.Driver.Core.Clusters; @@ -24,21 +25,23 @@ using MongoDB.Driver.Core.ConnectionPools; using MongoDB.Driver.Core.Connections; using MongoDB.Driver.Core.Events; -using MongoDB.Driver.Core.Misc; +using MongoDB.Driver.Core.Logging; using MongoDB.Driver.Core.Servers; using Moq; -namespace MongoDB.Driver.Core.Helpers +namespace MongoDB.Driver.Core.TestHelpers { public class MockClusterableServerFactory : IClusterableServerFactory { private readonly Dictionary _servers; private readonly IEventSubscriber _eventSubscriber; + private readonly ILoggerFactory _loggerFactory; - public MockClusterableServerFactory(IEventSubscriber eventSubscriber = null) + public MockClusterableServerFactory(ILoggerFactory loggerFactory, IEventSubscriber eventSubscriber = null) { _servers = new Dictionary(); _eventSubscriber = eventSubscriber; + _loggerFactory = loggerFactory; } public IClusterableServer CreateServer(ClusterType clusterType, ClusterId clusterId, IClusterClock clusterClock, EndPoint endPoint) @@ -126,8 +129,8 @@ IClusterableServer CreateServer(ClusterType clusterType, IConnectionPoolFactory new ServerSettings(), endPoint, connectionPoolFactory, - _eventSubscriber, - serverApi: null); + serverApi: null, + _loggerFactory.CreateEventLogger(_eventSubscriber)); default: return new DefaultServer( clusterId, @@ -141,9 +144,9 @@ IClusterableServer CreateServer(ClusterType clusterType, IConnectionPoolFactory endPoint, connectionPoolFactory, serverMonitorFactory, - _eventSubscriber, - serverApi: null); - } + serverApi: null, + _loggerFactory.CreateEventLogger(_eventSubscriber)); + } } } diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Helpers/MockConnection.cs b/tests/MongoDB.Driver.Core.TestHelpers/MockConnection.cs similarity index 99% rename from tests/MongoDB.Driver.Core.Tests/Core/Helpers/MockConnection.cs rename to tests/MongoDB.Driver.Core.TestHelpers/MockConnection.cs index 895397da33a..b4d70359e4f 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Helpers/MockConnection.cs +++ b/tests/MongoDB.Driver.Core.TestHelpers/MockConnection.cs @@ -27,7 +27,7 @@ using MongoDB.Driver.Core.WireProtocol.Messages; using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; -namespace MongoDB.Driver.Core.Helpers +namespace MongoDB.Driver.Core.TestHelpers { public class MockConnection : IConnection { diff --git a/tests/MongoDB.Driver.Core.TestHelpers/MongoDB.Driver.Core.TestHelpers.csproj b/tests/MongoDB.Driver.Core.TestHelpers/MongoDB.Driver.Core.TestHelpers.csproj index 7d7272f4ff9..d53e0efd547 100644 --- a/tests/MongoDB.Driver.Core.TestHelpers/MongoDB.Driver.Core.TestHelpers.csproj +++ b/tests/MongoDB.Driver.Core.TestHelpers/MongoDB.Driver.Core.TestHelpers.csproj @@ -1,62 +1,22 @@ - - true - - - true - - - true - + - netstandard2.0;netstandard2.1;net472 - netstandard2.0;netstandard2.1 - 9 - true - - false + $(StandardTargetFrameworks) ..\..\MongoDBTest.ruleset MongoDB.Driver.Core.TestHelpers MongoDB.Driver.Core.TestHelpers - MongoDB Inc. - Copyright © 2010-present MongoDB Inc. Helper classes applicable to test projects that reference MongoDB.Driver.Core. - - 0.0.0-local - - - - TRACE - - - $(DefineConstants);WINDOWS - - - $(DefineConstants);LINUX - - - $(DefineConstants);MACOS - - - - - - - - - - diff --git a/tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/RequirePlatform.cs b/tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/RequirePlatform.cs index 12b93101a89..a9cd9082e5f 100644 --- a/tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/RequirePlatform.cs +++ b/tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/RequirePlatform.cs @@ -16,6 +16,7 @@ using System; using System.Linq; using Xunit; +using Xunit.Sdk; namespace MongoDB.Driver.TestHelpers { diff --git a/tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/RequireServer.cs b/tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/RequireServer.cs index 86298bcd651..b90c4c25f18 100644 --- a/tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/RequireServer.cs +++ b/tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/RequireServer.cs @@ -19,6 +19,7 @@ using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Misc; using Xunit; +using Xunit.Sdk; namespace MongoDB.Driver.Core.TestHelpers.XunitExtensions { diff --git a/tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/XunitExtensionsConsts.cs b/tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/XunitExtensionsConsts.cs deleted file mode 100644 index 518dd19a459..00000000000 --- a/tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/XunitExtensionsConsts.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace MongoDB.Driver.Core.TestHelpers.XunitExtensions -{ - public static class XunitExtensionsConsts - { - public const string TimeoutEnforcingXunitFramework = "MongoDB.Driver.Core.TestHelpers.XunitExtensions.TimeoutEnforcing.TimeoutEnforcingXunitTestFramework"; - public const string TimeoutEnforcingFrameworkAssembly = "MongoDB.Driver.Core.TestHelpers"; - } -} diff --git a/tests/MongoDB.Driver.Core.Tests/BatchTransformingAsyncCursorTests.cs b/tests/MongoDB.Driver.Core.Tests/BatchTransformingAsyncCursorTests.cs index b6118bfb4b0..894b156e2a2 100644 --- a/tests/MongoDB.Driver.Core.Tests/BatchTransformingAsyncCursorTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/BatchTransformingAsyncCursorTests.cs @@ -21,7 +21,7 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver diff --git a/tests/MongoDB.Driver.Core.Tests/ChangeStreamDocumentSerializerTests.cs b/tests/MongoDB.Driver.Core.Tests/ChangeStreamDocumentSerializerTests.cs index dd30ab81eed..a9fc19fc0b6 100644 --- a/tests/MongoDB.Driver.Core.Tests/ChangeStreamDocumentSerializerTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/ChangeStreamDocumentSerializerTests.cs @@ -36,11 +36,12 @@ public void constructor_should_initialize_instance() var result = new ChangeStreamDocumentSerializer(documentSerializer); result._documentSerializer().Should().BeSameAs(documentSerializer); - result._memberSerializationInfo().Count.Should().Be(13); + result._memberSerializationInfo().Count.Should().Be(14); AssertRegisteredMember(result, "ClusterTime", "clusterTime", BsonTimestampSerializer.Instance); AssertRegisteredMember(result, "CollectionNamespace", "ns", ChangeStreamDocumentCollectionNamespaceSerializer.Instance); AssertRegisteredMember(result, "CollectionUuid", "ui", GuidSerializer.StandardInstance); AssertRegisteredMember(result, "DatabaseNamespace", "ns", ChangeStreamDocumentDatabaseNamespaceSerializer.Instance); + AssertRegisteredMember(result, "DisambiguatedPaths", "disambiguatedPaths", BsonDocumentSerializer.Instance); AssertRegisteredMember(result, "DocumentKey", "documentKey", BsonDocumentSerializer.Instance); AssertRegisteredMember(result, "FullDocument", "fullDocument", documentSerializer); AssertRegisteredMember(result, "FullDocumentBeforeChange", "fullDocumentBeforeChange", documentSerializer); diff --git a/tests/MongoDB.Driver.Core.Tests/ChangeStreamDocumentTests.cs b/tests/MongoDB.Driver.Core.Tests/ChangeStreamDocumentTests.cs index 0f074db4ad6..ff4b634282a 100644 --- a/tests/MongoDB.Driver.Core.Tests/ChangeStreamDocumentTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/ChangeStreamDocumentTests.cs @@ -246,6 +246,29 @@ public void DatabaseNamespace_should_return_null_if_invalid(string databaseName) result.Should().BeNull(); } + [Fact] + public void DisambiguatedPaths_should_return_expected_result() + { + var value = new BsonDocument("a.0", new BsonArray {"a", "0"}); + var backingDocument = new BsonDocument { { "other", 1 }, { "disambiguatedPaths", value } }; + var subject = CreateSubject(backingDocument: backingDocument); + + var result = subject.DisambiguatedPaths; + + result.Should().Be(value); + } + + [Fact] + public void DisambiguatedPaths_should_return_null_if_not_present() + { + var backingDocument = new BsonDocument { { "other", 1 } }; + var subject = CreateSubject(backingDocument: backingDocument); + + var result = subject.DisambiguatedPaths; + + result.Should().BeNull(); + } + [Fact] public void DocumentKey_should_return_expected_result() { diff --git a/tests/MongoDB.Driver.Core.Tests/CollationTests.cs b/tests/MongoDB.Driver.Core.Tests/CollationTests.cs index 2d8cdc6ea7a..aecade5d9ae 100644 --- a/tests/MongoDB.Driver.Core.Tests/CollationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/CollationTests.cs @@ -21,7 +21,7 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.IO; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Authentication/AuthenticationHelperTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Authentication/AuthenticationHelperTests.cs index 67b9e450cf6..1d5d4ff401e 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Authentication/AuthenticationHelperTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Authentication/AuthenticationHelperTests.cs @@ -19,7 +19,7 @@ using System.Threading; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.Connections; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Authentication/CacheableCredentialsProviderTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Authentication/CacheableCredentialsProviderTests.cs new file mode 100644 index 00000000000..2feec20a532 --- /dev/null +++ b/tests/MongoDB.Driver.Core.Tests/Core/Authentication/CacheableCredentialsProviderTests.cs @@ -0,0 +1,108 @@ +/* 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.Threading; +using System.Threading.Tasks; +using FluentAssertions; +using MongoDB.Bson; +using MongoDB.TestHelpers.XunitExtensions; +using MongoDB.Driver.Core.Authentication.External; +using MongoDB.Driver.Core.Misc; +using Moq; +using Xunit; + +namespace MongoDB.Driver.Core.Tests.Core.Authentication +{ + public class CacheableCredentialsProviderTests + { + [Theory] + [ParameterAttributeData] + public async Task CreateCredentialsFromExternalSource_should_return_the_same_result_until_cache_valid([Values(false, true)] bool async) + { + bool isExpired = false; + Func isExpiredFunc = () => isExpired; + var subject = CreateSubject(isExpiredFunc); + + var creds1 = await CreateCredentials(subject, async); + var creds2 = await CreateCredentials(subject, async); + creds1.Should().BeSameAs(creds2); + + isExpired = true; + var creds3 = await CreateCredentials(subject, async); + creds2.Should().NotBeSameAs(creds3); + var creds4 = await CreateCredentials(subject, async); + creds3.Should().NotBeSameAs(creds4); + + isExpired = false; + var creds5 = await CreateCredentials(subject, async); + creds4.Should().BeSameAs(creds5); + var creds6 = await CreateCredentials(subject, async); + creds5.Should().BeSameAs(creds6); + } + + [Theory] + [ParameterAttributeData] + public async Task Cache_should_not_be_in_play_when_expiration_date_is_null([Values(false, true)] bool async) + { + DateTime? expiredDate = null; + Func expiredDateFunc = () => expiredDate; + var subject = CreateSubject(expirationDateFunc: expiredDateFunc); + await CreateCredentials(subject, async); + subject.Credentials.Should().BeNull(); + } + + private async Task CreateCredentials(CacheableCredentialsProvider subject, bool async) => + async ? (await subject.CreateCredentialsFromExternalSourceAsync()) : subject.CreateCredentialsFromExternalSource(); + + private CacheableCredentialsProvider CreateSubject(Func isExpiredFunc = null, Func expirationDateFunc = null) + { + isExpiredFunc = isExpiredFunc ?? (() => false); + expirationDateFunc = expirationDateFunc ?? (() => DateTime.UtcNow.AddDays(1)); // dummy value if not provided + + var externalCredentialsProviderMock = new Mock>(); + var setupSequence = externalCredentialsProviderMock + .Setup(c => c.CreateCredentialsFromExternalSource(It.IsAny())) + .Returns(() => new DummyCredentials(isExpiredFunc, expirationDateFunc)); + var setupSequenceAsync = externalCredentialsProviderMock + .Setup(c => c.CreateCredentialsFromExternalSourceAsync(It.IsAny())) + .ReturnsAsync(() => new DummyCredentials(isExpiredFunc, expirationDateFunc)); + + return new CacheableCredentialsProvider(externalCredentialsProviderMock.Object); + } + } + + public class DummyCredentials : IExternalCredentials + { + private readonly Func _expirationDateFunc; + private readonly Guid _id; + private readonly Func _isExpiredFunc; + + public DummyCredentials(Func isExpiredFunc, Func expirationDateFunc) + { + _expirationDateFunc = expirationDateFunc; // can be null + _id = Guid.NewGuid(); + _isExpiredFunc = Ensure.IsNotNull(isExpiredFunc, nameof(isExpiredFunc)); + } + + public DateTime? Expiration => _expirationDateFunc(); + + public bool ShouldBeRefreshed => _isExpiredFunc(); + + public Guid Id => _id; + + public BsonDocument GetKmsCredentials() => new BsonDocument("dummy", 1); + } +} diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Authentication/Libgssapi/GssapiSecurityCredentialTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Authentication/Libgssapi/GssapiSecurityCredentialTests.cs index a7cc1f0f3a9..85fe729cdce 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Authentication/Libgssapi/GssapiSecurityCredentialTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Authentication/Libgssapi/GssapiSecurityCredentialTests.cs @@ -15,7 +15,7 @@ using System; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Authentication.Libgssapi; using MongoDB.Driver.Core.Misc; using Xunit; @@ -40,7 +40,7 @@ public GssapiSecurityCredentialTests() } } - [SkippableFact] + [Fact] public void Should_acquire_gssapi_security_credential_with_username_and_password() { RequireEnvironment.Check().EnvironmentVariable("GSSAPI_TESTS_ENABLED"); @@ -50,7 +50,7 @@ public void Should_acquire_gssapi_security_credential_with_username_and_password credential.Should().NotBeNull(); } - [SkippableFact] + [Fact] public void Should_acquire_gssapi_security_credential_with_username_only() { RequireEnvironment.Check().EnvironmentVariable("GSSAPI_TESTS_ENABLED"); @@ -59,7 +59,7 @@ public void Should_acquire_gssapi_security_credential_with_username_only() credential.Should().NotBeNull(); } - [SkippableFact] + [Fact] public void Should_fail_to_acquire_gssapi_security_credential_with_username_and_bad_password() { RequireEnvironment.Check().EnvironmentVariable("GSSAPI_TESTS_ENABLED"); diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Authentication/Libgssapi/LibgssapiExceptionTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Authentication/Libgssapi/LibgssapiExceptionTests.cs index 8098ee54ce1..649be79fa5e 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Authentication/Libgssapi/LibgssapiExceptionTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Authentication/Libgssapi/LibgssapiExceptionTests.cs @@ -31,9 +31,11 @@ public void Serialization_should_work() var formatter = new BinaryFormatter(); using (var stream = new MemoryStream()) { +#pragma warning disable SYSLIB0011 // BinaryFormatter serialization is obsolete formatter.Serialize(stream, subject); stream.Position = 0; var rehydrated = (LibgssapiException)formatter.Deserialize(stream); +#pragma warning restore SYSLIB0011 // BinaryFormatter serialization is obsolete rehydrated.Message.Should().Be(subject.Message); } diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Authentication/MongoAWSAuthenticatorTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Authentication/MongoAWSAuthenticatorTests.cs index c6a52b8d290..bc98cfe7d25 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Authentication/MongoAWSAuthenticatorTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Authentication/MongoAWSAuthenticatorTests.cs @@ -19,13 +19,15 @@ using System.Threading; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Authentication; +using MongoDB.Driver.Core.Authentication.External; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Connections; using MongoDB.Driver.Core.Helpers; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; +using MongoDB.Driver.Core.TestHelpers; using Moq; using Xunit; @@ -102,7 +104,7 @@ public void Authenticate_should_have_expected_result( var saslContinueCommandResponse = MessageHelper.BuildCommandResponse(RawBsonDocumentHelper.FromJson( "{ conversationId : 1, done : true, payload : BinData(0,\"\"), ok : 1}")); - var subject = new MongoAWSAuthenticator(credential, null, mockRandomByteGenerator.Object, mockClock.Object, serverApi: null); + var subject = new MongoAWSAuthenticator(credential, null, mockRandomByteGenerator.Object, Mock.Of>(), mockClock.Object, serverApi: null); var connection = new MockConnection(__serverId); connection.EnqueueCommandResponseMessage(saslStartCommandResponse); @@ -181,7 +183,7 @@ public void Authenticate_should_send_serverApi_with_command_wire_protocol( var saslStartCommandResponseString = $"{{ conversationId : 1, done : false, payload : BinData(0,\"{ToBase64(serverFirstMessage.ToBson())}\"), ok : 1 }}"; var saslContinueCommandResponseString = "{ conversationId : 1, done : true, payload : BinData(0,\"\"), ok : 1}"; - var subject = new MongoAWSAuthenticator(credential, null, mockRandomByteGenerator.Object, mockClock.Object, serverApi); + var subject = new MongoAWSAuthenticator(credential, null, mockRandomByteGenerator.Object, Mock.Of>(), mockClock.Object, serverApi); var connection = new MockConnection(__serverId); var saslStartResponse = MessageHelper.BuildCommandResponse(RawBsonDocumentHelper.FromJson(saslStartCommandResponseString)); @@ -262,7 +264,7 @@ public void Authenticate_should_throw_when_server_provides_invalid_host( var saslContinueCommandResponse = MessageHelper.BuildCommandResponse(RawBsonDocumentHelper.FromJson( "{ conversationId : 1, done : true, payload : BinData(0,\"\"), ok : 1}")); - var subject = new MongoAWSAuthenticator(credential, null, mockRandomByteGenerator.Object, SystemClock.Instance, serverApi: null); + var subject = new MongoAWSAuthenticator(credential, null, mockRandomByteGenerator.Object, Mock.Of>(), SystemClock.Instance, serverApi: null); var connection = new MockConnection(__serverId); connection.EnqueueCommandResponseMessage(saslStartCommandResponse); @@ -305,7 +307,7 @@ public void Authenticate_should_throw_when_server_provides_invalid_nonce( var saslStartCommandResponse = MessageHelper.BuildCommandResponse(RawBsonDocumentHelper.FromJson( $"{{ conversationId : 1, done : false, payload : BinData(0,\"{ToBase64(serverFirstMessage.ToBson())}\"), ok : 1 }}")); - var subject = new MongoAWSAuthenticator(credential, null, mockRandomByteGenerator.Object, SystemClock.Instance, serverApi: null); + var subject = new MongoAWSAuthenticator(credential, null, mockRandomByteGenerator.Object, Mock.Of>(), SystemClock.Instance, serverApi: null); var connection = new MockConnection(__serverId); connection.EnqueueCommandResponseMessage(saslStartCommandResponse); @@ -350,7 +352,7 @@ public void Authenticate_should_throw_when_server_provides_unexpected_field( var saslContinueCommandResponse = MessageHelper.BuildCommandResponse(RawBsonDocumentHelper.FromJson( "{ conversationId : 1, done : true, payload : BinData(0,\"\"), ok : 1}")); - var subject = new MongoAWSAuthenticator(credential, null, mockRandomByteGenerator.Object, SystemClock.Instance, serverApi: null); + var subject = new MongoAWSAuthenticator(credential, null, mockRandomByteGenerator.Object, Mock.Of>(), SystemClock.Instance, serverApi: null); var connection = new MockConnection(__serverId); connection.EnqueueCommandResponseMessage(saslStartCommandResponse); @@ -422,7 +424,7 @@ public void Authenticate_with_session_token_should_have_expected_result( "{ conversationId : 1, done : true, payload : BinData(0,\"\"), ok : 1}")); var properties = new[] { new KeyValuePair("AWS_SESSION_TOKEN", sessionToken) }; - var subject = new MongoAWSAuthenticator(credential, properties, mockRandomByteGenerator.Object, mockClock.Object, serverApi: null); + var subject = new MongoAWSAuthenticator(credential, properties, mockRandomByteGenerator.Object, Mock.Of>(), mockClock.Object, serverApi: null); var connection = new MockConnection(__serverId); connection.EnqueueCommandResponseMessage(saslStartCommandResponse); diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Authentication/MongoDBCRAuthenticatorTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Authentication/MongoDBCRAuthenticatorTests.cs index be5015649ae..123e9ee099f 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Authentication/MongoDBCRAuthenticatorTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Authentication/MongoDBCRAuthenticatorTests.cs @@ -19,13 +19,14 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Driver.Core.Clusters; -using MongoDB.Driver.Core.Servers; +using MongoDB.Driver.Core.Connections; using MongoDB.Driver.Core.Helpers; +using MongoDB.Driver.Core.Misc; +using MongoDB.Driver.Core.Servers; +using MongoDB.Driver.Core.TestHelpers; using MongoDB.Driver.Core.WireProtocol.Messages; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; -using MongoDB.Driver.Core.Connections; -using MongoDB.Bson.TestHelpers.XunitExtensions; -using MongoDB.Driver.Core.Misc; namespace MongoDB.Driver.Core.Authentication { diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Authentication/MongoDBX509AuthenticatorTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Authentication/MongoDBX509AuthenticatorTests.cs index 38d864cf737..8590e42987c 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Authentication/MongoDBX509AuthenticatorTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Authentication/MongoDBX509AuthenticatorTests.cs @@ -19,13 +19,14 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Driver.Core.Clusters; -using MongoDB.Driver.Core.Servers; -using MongoDB.Driver.Core.Helpers; -using Xunit; using MongoDB.Driver.Core.Connections; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.Driver.Core.Helpers; using MongoDB.Driver.Core.Misc; +using MongoDB.Driver.Core.Servers; +using MongoDB.Driver.Core.TestHelpers; using MongoDB.Driver.Core.WireProtocol.Messages; +using MongoDB.TestHelpers.XunitExtensions; +using Xunit; namespace MongoDB.Driver.Core.Authentication { diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Authentication/PlainAuthenticatorTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Authentication/PlainAuthenticatorTests.cs index b124a5fd501..481ac1e05a5 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Authentication/PlainAuthenticatorTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Authentication/PlainAuthenticatorTests.cs @@ -19,13 +19,14 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Driver.Core.Clusters; -using MongoDB.Driver.Core.Servers; +using MongoDB.Driver.Core.Connections; using MongoDB.Driver.Core.Helpers; +using MongoDB.Driver.Core.Misc; +using MongoDB.Driver.Core.Servers; +using MongoDB.Driver.Core.TestHelpers; using MongoDB.Driver.Core.WireProtocol.Messages; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; -using MongoDB.Driver.Core.Connections; -using MongoDB.Bson.TestHelpers.XunitExtensions; -using MongoDB.Driver.Core.Misc; namespace MongoDB.Driver.Core.Authentication { diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Authentication/SaslPrepHelperTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Authentication/SaslPrepHelperTests.cs index a719fdf2742..d5c9781e8b0 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Authentication/SaslPrepHelperTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Authentication/SaslPrepHelperTests.cs @@ -15,7 +15,7 @@ using FluentAssertions; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using System; using Xunit; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Authentication/ScramSha1AuthenticatorTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Authentication/ScramSha1AuthenticatorTests.cs index f8205860cb5..0ab870448e6 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Authentication/ScramSha1AuthenticatorTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Authentication/ScramSha1AuthenticatorTests.cs @@ -25,7 +25,7 @@ using MongoDB.Driver.Core.Misc; using Xunit; using MongoDB.Driver.Core.Connections; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using System.Linq; using MongoDB.Driver.Core.TestHelpers; using MongoDB.Driver.Core.WireProtocol.Messages; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Authentication/ScramSha256AuthenticatorTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Authentication/ScramSha256AuthenticatorTests.cs index b14a14319e6..813da638541 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Authentication/ScramSha256AuthenticatorTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Authentication/ScramSha256AuthenticatorTests.cs @@ -27,7 +27,7 @@ using MongoDB.Driver.Core.WireProtocol.Messages; using Xunit; using MongoDB.Driver.Core.Connections; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using System.Linq; using MongoDB.Driver.Core.TestHelpers; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Authentication/Sspi/Win32ExceptionTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Authentication/Sspi/Win32ExceptionTests.cs index cc05a450278..ea6547a8626 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Authentication/Sspi/Win32ExceptionTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Authentication/Sspi/Win32ExceptionTests.cs @@ -31,9 +31,11 @@ public void Serialization_should_work() var formatter = new BinaryFormatter(); using (var stream = new MemoryStream()) { +#pragma warning disable SYSLIB0011 // BinaryFormatter serialization is obsolete formatter.Serialize(stream, subject); stream.Position = 0; var rehydrated = (Win32Exception)formatter.Deserialize(stream); +#pragma warning restore SYSLIB0011 // BinaryFormatter serialization is obsolete rehydrated.HResult.Should().Be(subject.HResult); rehydrated.Message.Should().Be(subject.Message); diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Bindings/ChannelChannelSourceTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Bindings/ChannelChannelSourceTests.cs index a3fdc367a5c..76a12582048 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Bindings/ChannelChannelSourceTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Bindings/ChannelChannelSourceTests.cs @@ -17,7 +17,7 @@ using System.Reflection; using System.Threading; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Servers; using Moq; using Xunit; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Bindings/ChannelReadBindingTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Bindings/ChannelReadBindingTests.cs index b28f7a525bf..9d5e6b99131 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Bindings/ChannelReadBindingTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Bindings/ChannelReadBindingTests.cs @@ -21,7 +21,7 @@ using System.Threading; using System.Threading.Tasks; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Servers; using Moq; using Xunit; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Bindings/ChannelReadWriteBindingTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Bindings/ChannelReadWriteBindingTests.cs index 3f5bbde5905..9d39d692f18 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Bindings/ChannelReadWriteBindingTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Bindings/ChannelReadWriteBindingTests.cs @@ -21,7 +21,7 @@ using System.Threading; using System.Threading.Tasks; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Servers; using Moq; using Xunit; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Bindings/ChannelSourceHandleTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Bindings/ChannelSourceHandleTests.cs index 9021c838836..212ca5f46d9 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Bindings/ChannelSourceHandleTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Bindings/ChannelSourceHandleTests.cs @@ -17,7 +17,7 @@ using System.Threading; using FluentAssertions; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Misc; using Moq; using Xunit; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Bindings/ChannelSourceReadWriteBindingTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Bindings/ChannelSourceReadWriteBindingTests.cs index 08a98eab850..d00f0151e47 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Bindings/ChannelSourceReadWriteBindingTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Bindings/ChannelSourceReadWriteBindingTests.cs @@ -16,7 +16,7 @@ using System; using System.Threading; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters; using Moq; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Bindings/CoreSessionOptionsTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Bindings/CoreSessionOptionsTests.cs index 44d32ced977..e9662a4b536 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Bindings/CoreSessionOptionsTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Bindings/CoreSessionOptionsTests.cs @@ -14,7 +14,7 @@ */ using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.Core.Bindings diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Bindings/CoreSessionTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Bindings/CoreSessionTests.cs index 56782fd21f9..4df1c661b3b 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Bindings/CoreSessionTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Bindings/CoreSessionTests.cs @@ -21,7 +21,7 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; @@ -226,7 +226,7 @@ public void Dispose_should_have_expected_result( Mock.Get(subject.ServerSession).Verify(m => m.Dispose(), Times.Once); } - [SkippableFact] + [Fact] public void StartTransaction_should_throw_when_write_concern_is_unacknowledged() { RequireServer.Check().ClusterType(ClusterType.ReplicaSet).Supports(Feature.Transactions); @@ -264,6 +264,18 @@ public void EnsureTransactionsAreSupported_should_throw_when_there_are_no_connec e.Message.Should().Be("StartTransaction cannot determine if transactions are supported because there are no connected servers."); } + [Theory] + [ParameterAttributeData] + public void EnsureTransactionsAreSupported_should_not_throw_when_there_are_no_connected_servers_with_LB( + [Values(0, 1, 2, 3)] int numberOfDisconnectedServers) // 0 - no servers at all + { + var clusterDescription = CreateClusterDescriptionWithDisconnectedServers(numberOfDisconnectedServers); + clusterDescription = clusterDescription.WithType(ClusterType.LoadBalanced); + var subject = CreateSubject(clusterDescription); + + subject.EnsureTransactionsAreSupported(); + } + // EnsureTransactionsAreSupported scenario codes // C = Connected, D = Disconnected // P = Primary, S = Secondary, A = Arbiter, R = ShardRouter, U = Unknown diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Bindings/CoreTransactionTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Bindings/CoreTransactionTests.cs index 0776dbdc19a..d43abf48d2c 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Bindings/CoreTransactionTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Bindings/CoreTransactionTests.cs @@ -19,7 +19,7 @@ using System.Text; using System.Threading.Tasks; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.Core.Bindings diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Bindings/NoCoreServerSessionTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Bindings/NoCoreServerSessionTests.cs index a8bb074f07f..d04ae632cd4 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Bindings/NoCoreServerSessionTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Bindings/NoCoreServerSessionTests.cs @@ -14,7 +14,7 @@ */ using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.Core.Bindings diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Bindings/NoCoreSessionTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Bindings/NoCoreSessionTests.cs index e223a7df144..ddfdf2e8946 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Bindings/NoCoreSessionTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Bindings/NoCoreSessionTests.cs @@ -17,7 +17,7 @@ using System.Threading; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.Core.Bindings diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Bindings/ReadBindingHandleTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Bindings/ReadBindingHandleTests.cs index 06096798231..d26b05ff749 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Bindings/ReadBindingHandleTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Bindings/ReadBindingHandleTests.cs @@ -16,7 +16,7 @@ using System; using System.Threading; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Moq; using Xunit; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Bindings/ReadPreferenceBindingTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Bindings/ReadPreferenceBindingTests.cs index dc4dd76fce0..6f6468e5dbc 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Bindings/ReadPreferenceBindingTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Bindings/ReadPreferenceBindingTests.cs @@ -19,7 +19,7 @@ using System.Threading; using System.Threading.Tasks; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Clusters.ServerSelectors; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Bindings/ReadWriteBindingHandleTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Bindings/ReadWriteBindingHandleTests.cs index 0f1345975d8..33f36397c1b 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Bindings/ReadWriteBindingHandleTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Bindings/ReadWriteBindingHandleTests.cs @@ -16,7 +16,7 @@ using System; using System.Threading; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; using Moq; using Xunit; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Bindings/ServerChannelSourceTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Bindings/ServerChannelSourceTests.cs index 1adddd53453..9b20a631e7b 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Bindings/ServerChannelSourceTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Bindings/ServerChannelSourceTests.cs @@ -26,7 +26,7 @@ using MongoDB.Driver.Core.Helpers; using Moq; using Xunit; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; namespace MongoDB.Driver.Core.Bindings { diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Bindings/SingleServerReadBindingTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Bindings/SingleServerReadBindingTests.cs index 9aee543a620..d731608be10 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Bindings/SingleServerReadBindingTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Bindings/SingleServerReadBindingTests.cs @@ -18,7 +18,7 @@ using System.Reflection; using System.Threading; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Servers; using Moq; using Xunit; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Bindings/SingleServerReadWriteBindingTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Bindings/SingleServerReadWriteBindingTests.cs index 4bfd753457c..5260dfb8cee 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Bindings/SingleServerReadWriteBindingTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Bindings/SingleServerReadWriteBindingTests.cs @@ -21,7 +21,7 @@ using System.Threading; using System.Threading.Tasks; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Servers; using Moq; using Xunit; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Bindings/WritableServerBindingTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Bindings/WritableServerBindingTests.cs index 5f2dcd0b8d6..9ee482c2f6a 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Bindings/WritableServerBindingTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Bindings/WritableServerBindingTests.cs @@ -20,7 +20,7 @@ using System.Threading; using System.Threading.Tasks; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Clusters.ServerSelectors; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Clusters/ClusterDescriptionTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Clusters/ClusterDescriptionTests.cs index 321733d4a47..0419ca12cc1 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Clusters/ClusterDescriptionTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Clusters/ClusterDescriptionTests.cs @@ -16,7 +16,7 @@ using System; using System.Net; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Helpers; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Clusters/ClusterTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Clusters/ClusterTests.cs index 03b7bc94977..2ffd86474a4 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Clusters/ClusterTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Clusters/ClusterTests.cs @@ -21,8 +21,9 @@ using System.Threading; using System.Threading.Tasks; using FluentAssertions; +using Microsoft.Extensions.Logging; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters.ServerSelectors; using MongoDB.Driver.Core.Configuration; @@ -30,18 +31,20 @@ using MongoDB.Driver.Core.Helpers; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; +using MongoDB.Driver.Core.TestHelpers.Logging; using Moq; using Xunit; +using Xunit.Abstractions; namespace MongoDB.Driver.Core.Clusters { - public class ClusterTests + public class ClusterTests : LoggableTestClass { private readonly EventCapturer _capturedEvents; private readonly Mock _mockServerFactory; private ClusterSettings _settings; - public ClusterTests() + public ClusterTests(ITestOutputHelper output) : base(output) { _settings = new ClusterSettings(serverSelectionTimeout: TimeSpan.FromSeconds(2)); _mockServerFactory = new Mock(); @@ -60,13 +63,13 @@ public void SupportedWireVersionRange_should_return_expected_result() { var result = Cluster.SupportedWireVersionRange; - result.Should().Be(new Range(6, 17)); + result.Should().Be(new Range(6, 19)); } [Fact] public void Constructor_should_throw_if_settings_is_null() { - Action act = () => new StubCluster(null, _mockServerFactory.Object, _capturedEvents); + Action act = () => new StubCluster(null, _mockServerFactory.Object, _capturedEvents, null); act.ShouldThrow(); } @@ -74,7 +77,7 @@ public void Constructor_should_throw_if_settings_is_null() [Fact] public void Constructor_should_throw_if_serverFactory_is_null() { - Action act = () => new StubCluster(_settings, null, _capturedEvents); + Action act = () => new StubCluster(_settings, null, _capturedEvents, null); act.ShouldThrow(); } @@ -82,7 +85,7 @@ public void Constructor_should_throw_if_serverFactory_is_null() [Fact] public void Constructor_should_throw_if_eventSubscriber_is_null() { - Action act = () => new StubCluster(_settings, _mockServerFactory.Object, null); + Action act = () => new StubCluster(_settings, _mockServerFactory.Object, null, null); act.ShouldThrow(); } @@ -325,8 +328,8 @@ public void SelectServer_should_throw_if_the_matched_server_cannot_be_found_and_ [Theory] [InlineData(0, 0, false)] [InlineData(0, 0, true)] - [InlineData(18, 19, false)] - [InlineData(18, 19, true)] + [InlineData(20, 21, false)] + [InlineData(20, 21, true)] public void SelectServer_should_throw_if_any_servers_are_incompatible(int min, int max, bool async) { var subject = CreateSubject(); @@ -456,7 +459,7 @@ public void SelectServer_should_apply_both_pre_and_post_server_selectors( preServerSelector: preSelector, postServerSelector: postSelector); - var subject = new StubCluster(settings, _mockServerFactory.Object, _capturedEvents); + var subject = new StubCluster(settings, _mockServerFactory.Object, _capturedEvents, LoggerFactory); subject.Initialize(); subject.SetServerDescriptions( @@ -496,7 +499,7 @@ public void SelectServer_should_call_custom_selector( }); var settings = _settings.With(postServerSelector: customServerSelector); - var subject = new StubCluster(settings, _mockServerFactory.Object, _capturedEvents); + var subject = new StubCluster(settings, _mockServerFactory.Object, _capturedEvents, LoggerFactory); subject.Initialize(); subject.SetServerDescriptions( @@ -545,7 +548,7 @@ private StubCluster CreateSubject(ClusterConnectionMode connectionMode = Cluster _settings = _settings.With(serverSelectionTimeout: serverSelectionTimeout.Value); } - return new StubCluster(_settings, _mockServerFactory.Object, _capturedEvents); + return new StubCluster(_settings, _mockServerFactory.Object, _capturedEvents, LoggerFactory); } private IServer SelectServerAttempt(Cluster cluster, IServerSelector operationSelector, bool async) @@ -568,8 +571,11 @@ private class StubCluster : Cluster { private Dictionary _servers = new Dictionary(); - public StubCluster(ClusterSettings settings, IClusterableServerFactory serverFactory, IEventSubscriber eventSubscriber) - : base(settings, serverFactory, eventSubscriber) + public StubCluster(ClusterSettings settings, + IClusterableServerFactory serverFactory, + IEventSubscriber eventSubscriber, + ILoggerFactory loggerFactory) + : base(settings, serverFactory, eventSubscriber, loggerFactory) { } diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Clusters/DnsMonitorFactoryTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Clusters/DnsMonitorFactoryTests.cs index e9045cbd676..edd93b62f33 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Clusters/DnsMonitorFactoryTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Clusters/DnsMonitorFactoryTests.cs @@ -32,7 +32,7 @@ public void constructor_should_initialize_instance() { var eventSubscriber = Mock.Of(); - var result = new DnsMonitorFactory(eventSubscriber); + var result = new DnsMonitorFactory(eventSubscriber, loggerFactory: null); result._eventSubscriber().Should().BeSameAs(eventSubscriber); } @@ -40,7 +40,7 @@ public void constructor_should_initialize_instance() [Fact] public void constructor_should_throw_when_eventSubscriber_is_null() { - var exception = Record.Exception(() => new DnsMonitorFactory(eventSubscriber: null)); + var exception = Record.Exception(() => new DnsMonitorFactory(eventSubscriber: null, loggerFactory: null)); var e = exception.Should().BeOfType().Subject; e.ParamName.Should().Be("eventSubscriber"); @@ -50,7 +50,7 @@ public void constructor_should_throw_when_eventSubscriber_is_null() public void CreateDnsMonitor_should_return_expected_result() { var mockEventSubscriber = new Mock(); - var subject = new DnsMonitorFactory(mockEventSubscriber.Object); + var subject = new DnsMonitorFactory(mockEventSubscriber.Object, loggerFactory: null); var cluster = Mock.Of(); var lookupDomainName = "a.b.com"; using var cancellationTokenSource = new CancellationTokenSource(); @@ -62,9 +62,6 @@ public void CreateDnsMonitor_should_return_expected_result() dnsMonitor._cluster().Should().BeSameAs(cluster); dnsMonitor._lookupDomainName().Should().Be(lookupDomainName); dnsMonitor._cancellationToken().Should().Be(cancellationToken); - - Action sdamInformationEventHandler; - mockEventSubscriber.Verify(m => m.TryGetEventHandler(out sdamInformationEventHandler), Times.Once); } } diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Clusters/DnsMonitorTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Clusters/DnsMonitorTests.cs index 636eae81734..e626f8cc5c5 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Clusters/DnsMonitorTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Clusters/DnsMonitorTests.cs @@ -75,7 +75,7 @@ public void constructor_should_initialize_instance() using var cancellationTokenSource = new CancellationTokenSource(); var cancellationToken = cancellationTokenSource.Token; - var subject = new DnsMonitor(cluster, dnsResolver, lookupDomainName, mockEventSubscriber.Object, cancellationToken); + var subject = new DnsMonitor(cluster, dnsResolver, lookupDomainName, mockEventSubscriber.Object, null, cancellationToken); subject.State.Should().Be(DnsMonitorState.Created); subject._cancellationToken().Should().Be(cancellationToken); @@ -83,7 +83,6 @@ public void constructor_should_initialize_instance() subject._dnsResolver().Should().BeSameAs(dnsResolver); subject._lookupDomainName().Should().Be("a.b.com"); subject._processDnsResultHasEverBeenCalled().Should().BeFalse(); - subject._sdamInformationEventHandler().Should().Be(sdamInformationEventHandler); subject._service().Should().Be("_mongodb._tcp.a.b.com"); subject._unhandledException().Should().BeNull(); } @@ -96,7 +95,7 @@ public void constructor_should_throw_when_cluster_is_null() using var cancellationTokenSource = new CancellationTokenSource(); var cancellationToken = cancellationTokenSource.Token; - var exception = Record.Exception(() => new DnsMonitor(null, dnsResolver, lookupDomainName, null, cancellationToken)); + var exception = Record.Exception(() => new DnsMonitor(null, dnsResolver, lookupDomainName, null, null, cancellationToken)); var e = exception.Should().BeOfType().Subject; e.ParamName.Should().Be("cluster"); @@ -110,7 +109,7 @@ public void constructor_should_throw_when_dnsResolver_is_null() using var cancellationTokenSource = new CancellationTokenSource(); var cancellationToken = cancellationTokenSource.Token; - var exception = Record.Exception(() => new DnsMonitor(cluster, null, lookupDomainName, null, cancellationToken)); + var exception = Record.Exception(() => new DnsMonitor(cluster, null, lookupDomainName, null, null, cancellationToken)); var e = exception.Should().BeOfType().Subject; e.ParamName.Should().Be("dnsResolver"); @@ -124,7 +123,7 @@ public void constructor_should_throw_when_lookupDomainName_is_null() using var cancellationTokenSource = new CancellationTokenSource(); var cancellationToken = cancellationTokenSource.Token; - var exception = Record.Exception(() => new DnsMonitor(cluster, dnsResolver, null, null, cancellationToken)); + var exception = Record.Exception(() => new DnsMonitor(cluster, dnsResolver, null, null, null, cancellationToken)); var e = exception.Should().BeOfType().Subject; e.ParamName.Should().Be("lookupDomainName"); @@ -141,7 +140,7 @@ public void constructor_should_throw_when_lookupDomainName_is_invalid(string loo using var cancellationTokenSource = new CancellationTokenSource(); var cancellationToken = cancellationTokenSource.Token; - var exception = Record.Exception(() => new DnsMonitor(cluster, dnsResolver, lookupDomainName, null, cancellationToken)); + var exception = Record.Exception(() => new DnsMonitor(cluster, dnsResolver, lookupDomainName, null, null, cancellationToken)); var e = exception.Should().BeOfType().Subject; e.ParamName.Should().Be("lookupDomainName"); @@ -507,7 +506,7 @@ private DnsMonitor CreateSubject( cluster = cluster ?? Mock.Of(); dnsResolver = dnsResolver ?? Mock.Of(); lookupDomainName = lookupDomainName ?? "a.b.c.com"; - return new DnsMonitor(cluster, dnsResolver, lookupDomainName, eventSubscriber, cancellationToken); + return new DnsMonitor(cluster, dnsResolver, lookupDomainName, eventSubscriber, null, cancellationToken); } } @@ -519,8 +518,7 @@ internal static class DnsMonitorReflector public static IDnsMonitoringCluster _cluster(this DnsMonitor obj) => (IDnsMonitoringCluster)Reflector.GetFieldValue(obj, nameof(_cluster)); public static IDnsResolver _dnsResolver(this DnsMonitor obj) => (IDnsResolver)Reflector.GetFieldValue(obj, nameof(_dnsResolver)); public static string _lookupDomainName(this DnsMonitor obj) => (string)Reflector.GetFieldValue(obj, nameof(_lookupDomainName)); - public static bool _processDnsResultHasEverBeenCalled(this DnsMonitor obj) => (bool)Reflector.GetFieldValue(obj, nameof(_processDnsResultHasEverBeenCalled)); - public static Action _sdamInformationEventHandler(this DnsMonitor obj) => (Action)Reflector.GetFieldValue(obj, nameof(_sdamInformationEventHandler)); + public static bool _processDnsResultHasEverBeenCalled(this DnsMonitor obj) => (bool)Reflector.GetFieldValue(obj, nameof(_processDnsResultHasEverBeenCalled)); public static string _service(this DnsMonitor obj) => (string)Reflector.GetFieldValue(obj, nameof(_service)); public static Exception _unhandledException(this DnsMonitor obj) => (Exception)Reflector.GetFieldValue(obj, nameof(_unhandledException)); diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Clusters/LoadBalancedClusterTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Clusters/LoadBalancedClusterTests.cs index a37ce56e81a..a5d0e6d6f2a 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Clusters/LoadBalancedClusterTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Clusters/LoadBalancedClusterTests.cs @@ -19,30 +19,32 @@ using System.Threading; using FluentAssertions; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Clusters.ServerSelectors; using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.Events; -using MongoDB.Driver.Core.Helpers; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; +using MongoDB.Driver.Core.TestHelpers; +using MongoDB.Driver.Core.TestHelpers.Logging; using Moq; using Xunit; +using Xunit.Abstractions; namespace MongoDB.Driver.Core.Tests.Core.Clusters { - public class LoadBalancedClusterTests + public class LoadBalancedClusterTests : LoggableTestClass { private EventCapturer _capturedEvents; private readonly MockClusterableServerFactory _mockServerFactory; private ClusterSettings _settings; private readonly EndPoint _endPoint = new DnsEndPoint("localhost", 27017); - public LoadBalancedClusterTests() + public LoadBalancedClusterTests(ITestOutputHelper output) : base(output) { _settings = new ClusterSettings().With(loadBalanced: true); - _mockServerFactory = new MockClusterableServerFactory(); + _mockServerFactory = new MockClusterableServerFactory(LoggerFactory); _capturedEvents = new EventCapturer(); } @@ -54,16 +56,10 @@ public void Constructor_should_initialize_instance() var mockEventSubscriber = new Mock(); var dnsMonitorFactory = Mock.Of(); - var result = new LoadBalancedCluster(settings, serverFactory, mockEventSubscriber.Object, dnsMonitorFactory); + var result = new LoadBalancedCluster(settings, serverFactory, mockEventSubscriber.Object, null, dnsMonitorFactory); result._dnsMonitorFactory().Should().BeSameAs(dnsMonitorFactory); - result._eventSubscriber().Should().BeSameAs(mockEventSubscriber.Object); result._state().Value.Should().Be(0); // State.Initial - AssertTryGetEventHandlerWasCalled(mockEventSubscriber); - AssertTryGetEventHandlerWasCalled(mockEventSubscriber); - AssertTryGetEventHandlerWasCalled(mockEventSubscriber); - AssertTryGetEventHandlerWasCalled(mockEventSubscriber); - AssertTryGetEventHandlerWasCalled(mockEventSubscriber); } [Theory] @@ -74,7 +70,7 @@ public void Constructor_should_handle_directConnection_correctly([Values(null, f _settings = _settings.With(connectionModeSwitch: ConnectionModeSwitch.UseDirectConnection, directConnection: directConnection); #pragma warning restore CS0618 // Type or member is obsolete - var exception = Record.Exception(() => new LoadBalancedCluster(_settings, _mockServerFactory, _capturedEvents)); + var exception = Record.Exception(() => new LoadBalancedCluster(_settings, _mockServerFactory, _capturedEvents, null)); if (directConnection.GetValueOrDefault()) { @@ -92,7 +88,7 @@ public void Constructor_should_handle_loadBalanced_correctly([Values(false, true { _settings = _settings.With(loadBalanced: loadBalanced); - var exception = Record.Exception(() => new LoadBalancedCluster(_settings, _mockServerFactory, _capturedEvents)); + var exception = Record.Exception(() => new LoadBalancedCluster(_settings, _mockServerFactory, _capturedEvents, LoggerFactory)); if (!loadBalanced) { @@ -114,7 +110,7 @@ public void Constructor_should_throw_when_ConnectionModeSwitch_is_invalid(Connec { _settings = _settings.With(connectionModeSwitch: connectionModeSwitch); - var exception = Record.Exception(() => new LoadBalancedCluster(_settings, _mockServerFactory, _capturedEvents)); + var exception = Record.Exception(() => new LoadBalancedCluster(_settings, _mockServerFactory, _capturedEvents, loggerFactory: null)); if (shouldThrow) { @@ -131,7 +127,7 @@ public void Constructor_should_throw_when_more_than_one_endpoint_is_specified() { _settings = _settings.With(endPoints: new[] { _endPoint, new DnsEndPoint("localhost", 27018) }); - var exception = Record.Exception(() => new LoadBalancedCluster(_settings, _mockServerFactory, _capturedEvents)); + var exception = Record.Exception(() => new LoadBalancedCluster(_settings, _mockServerFactory, _capturedEvents, loggerFactory: null)); exception.Should().BeOfType(); } @@ -141,7 +137,7 @@ public void Constructor_should_throw_when_replicaSetName_is_specified() { _settings = _settings.With(replicaSetName: "rs"); - var exception = Record.Exception(() => new LoadBalancedCluster(_settings, _mockServerFactory, _capturedEvents)); + var exception = Record.Exception(() => new LoadBalancedCluster(_settings, _mockServerFactory, _capturedEvents, loggerFactory: null)); exception.Should().BeOfType(); } @@ -151,7 +147,7 @@ public void Constructor_should_throw_if_srvMaxHosts_is_greater_than_zero() { _settings = _settings.With(srvMaxHosts: 2); - var exception = Record.Exception(() => new LoadBalancedCluster(_settings, _mockServerFactory, _capturedEvents)); + var exception = Record.Exception(() => new LoadBalancedCluster(_settings, _mockServerFactory, _capturedEvents, loggerFactory: null)); exception.Should().BeOfType(); } @@ -484,12 +480,6 @@ public void Server_should_be_assigned_immidiately_after_dns_response() } // private methods - private void AssertTryGetEventHandlerWasCalled(Mock mockEventSubscriber) - { - Action handler; - mockEventSubscriber.Verify(m => m.TryGetEventHandler(out handler), Times.Once); - } - private Mock CreateMockDnsMonitorFactory() { var mockDnsMonitorFactory = new Mock(); @@ -502,8 +492,8 @@ private Mock CreateMockDnsMonitorFactory() private LoadBalancedCluster CreateSubject(ClusterSettings settings = null, IDnsMonitorFactory dnsMonitorFactory = null) { return dnsMonitorFactory != null - ? new LoadBalancedCluster(settings ?? _settings, _mockServerFactory, _capturedEvents, dnsMonitorFactory) - : new LoadBalancedCluster(settings ?? _settings, _mockServerFactory, _capturedEvents); + ? new LoadBalancedCluster(settings ?? _settings, _mockServerFactory, _capturedEvents, LoggerFactory, dnsMonitorFactory) + : new LoadBalancedCluster(settings ?? _settings, _mockServerFactory, _capturedEvents, LoggerFactory); } private void PublishDnsException(IDnsMonitoringCluster cluster, Exception exception) @@ -533,7 +523,6 @@ internal static class LoadBalancedClusterReflector public static IClusterableServer InitializeServer(this LoadBalancedCluster cluster, IClusterableServer server) => (IClusterableServer)Reflector.Invoke(cluster, nameof(InitializeServer), server); public static IDnsMonitorFactory _dnsMonitorFactory(this LoadBalancedCluster cluster) => (IDnsMonitorFactory)Reflector.GetFieldValue(cluster, nameof(_dnsMonitorFactory)); public static Thread _dnsMonitorThread(this LoadBalancedCluster cluster) => (Thread)Reflector.GetFieldValue(cluster, nameof(_dnsMonitorThread)); - public static IEventSubscriber _eventSubscriber(this LoadBalancedCluster cluster) => (IEventSubscriber)Reflector.GetFieldValue(cluster, nameof(_eventSubscriber)); public static IClusterableServer _server(this LoadBalancedCluster cluster) => (IClusterableServer)Reflector.GetFieldValue(cluster, nameof(_server)); public static InterlockedInt32 _state(this LoadBalancedCluster cluster) => (InterlockedInt32)Reflector.GetFieldValue(cluster, nameof(_state)); } diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Clusters/MultiServerClusterTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Clusters/MultiServerClusterTests.cs index 387a3e06070..32e0e2feaa4 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Clusters/MultiServerClusterTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Clusters/MultiServerClusterTests.cs @@ -24,16 +24,18 @@ using MongoDB.Bson.TestHelpers; using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.Events; -using MongoDB.Driver.Core.Helpers; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; +using MongoDB.Driver.Core.TestHelpers; +using MongoDB.Driver.Core.TestHelpers.Logging; using MongoDB.Driver.Core.Tests.Core.Clusters; using Moq; using Xunit; +using Xunit.Abstractions; namespace MongoDB.Driver.Core.Clusters { - public class MultiServerClusterTests + public class MultiServerClusterTests : LoggableTestClass { private EventCapturer _capturedEvents; private MockClusterableServerFactory _serverFactory; @@ -43,10 +45,10 @@ public class MultiServerClusterTests private EndPoint _thirdEndPoint = new DnsEndPoint("localhost", 27019); private IEqualityComparer _serverDescriptionComparer = ServerDescriptionWithSimilarLastUpdateTimestampEqualityComparer.Instance; - public MultiServerClusterTests() + public MultiServerClusterTests(ITestOutputHelper output) : base(output) { _settings = new ClusterSettings(); - _serverFactory = new MockClusterableServerFactory(); + _serverFactory = new MockClusterableServerFactory(LoggerFactory); _capturedEvents = new EventCapturer(); } @@ -58,28 +60,18 @@ public void constructor_should_initialize_instance() var mockEventSubscriber = new Mock(); var dnsMonitorFactory = Mock.Of(); - var result = new MultiServerCluster(settings, serverFactory, mockEventSubscriber.Object, dnsMonitorFactory); + var result = new MultiServerCluster(settings, serverFactory, mockEventSubscriber.Object, null, dnsMonitorFactory); result._dnsMonitorFactory().Should().BeSameAs(dnsMonitorFactory); - result._eventSubscriber().Should().BeSameAs(mockEventSubscriber.Object); result._replicaSetName().Should().BeSameAs(settings.ReplicaSetName); result._state().Value.Should().Be(0); // State.Initial - AssertTryGetEventHandlerWasCalled(mockEventSubscriber); - AssertTryGetEventHandlerWasCalled(mockEventSubscriber); - AssertTryGetEventHandlerWasCalled(mockEventSubscriber); - AssertTryGetEventHandlerWasCalled(mockEventSubscriber); - AssertTryGetEventHandlerWasCalled(mockEventSubscriber); - AssertTryGetEventHandlerWasCalled(mockEventSubscriber); - AssertTryGetEventHandlerWasCalled(mockEventSubscriber); - AssertTryGetEventHandlerWasCalled(mockEventSubscriber); - AssertTryGetEventHandlerWasCalled(mockEventSubscriber); } [Fact] public void Constructor_should_throw_if_no_endpoints_are_specified() { var settings = new ClusterSettings(endPoints: new EndPoint[0]); - Action act = () => new MultiServerCluster(settings, _serverFactory, _capturedEvents); + Action act = () => new MultiServerCluster(settings, _serverFactory, _capturedEvents, loggerFactory: null); act.ShouldThrow(); } @@ -95,7 +87,7 @@ public void Constructor_should_throw_if_cluster_connection_mode_is_not_supported connectionModeSwitch: ConnectionModeSwitch.UseConnectionMode, #pragma warning restore CS0618 // Type or member is obsolete connectionMode: mode); - Action act = () => new MultiServerCluster(settings, _serverFactory, _capturedEvents); + Action act = () => new MultiServerCluster(settings, _serverFactory, _capturedEvents, loggerFactory: null); act.ShouldThrow(); } @@ -107,7 +99,7 @@ public void constructor_should_use_default_DnsMonitorFactory_when_dnsMonitorFact var serverFactory = Mock.Of(); var eventSubscriber = Mock.Of(); - var result = new MultiServerCluster(settings, serverFactory, eventSubscriber, dnsMonitorFactory: null); + var result = new MultiServerCluster(settings, serverFactory, eventSubscriber, loggerFactory: null, dnsMonitorFactory: null); var dnsMonitorFactory = result._dnsMonitorFactory().Should().BeOfType().Subject; dnsMonitorFactory._eventSubscriber().Should().BeSameAs(eventSubscriber); @@ -1166,12 +1158,6 @@ public void Should_call_dispose_on_servers_when_the_cluster_is_disposed() } // private methods - private void AssertTryGetEventHandlerWasCalled(Mock mockEventSubscriber) - { - Action handler; - mockEventSubscriber.Verify(m => m.TryGetEventHandler(out handler), Times.Once); - } - private Mock CreateMockDnsMonitorFactory() { var mockDnsMonitorFactory = new Mock(); @@ -1184,7 +1170,7 @@ private Mock CreateMockDnsMonitorFactory() private MultiServerCluster CreateSubject(ClusterSettings settings = null, IDnsMonitorFactory dnsMonitorFactory = null) { settings = settings ?? _settings; - return new MultiServerCluster(settings, _serverFactory, _capturedEvents, dnsMonitorFactory); + return new MultiServerCluster(settings, _serverFactory, _capturedEvents, LoggerFactory, dnsMonitorFactory); } private void TimeSpanShouldBeShort(TimeSpan value) @@ -1262,7 +1248,6 @@ internal static class MultiServerClusterReflector { public static IDnsMonitorFactory _dnsMonitorFactory(this MultiServerCluster cluster) => (IDnsMonitorFactory)Reflector.GetFieldValue(cluster, nameof(_dnsMonitorFactory)); public static Thread _dnsMonitorThread(this MultiServerCluster cluster) => (Thread)Reflector.GetFieldValue(cluster, nameof(_dnsMonitorThread)); - public static IEventSubscriber _eventSubscriber(this MultiServerCluster cluster) => (IEventSubscriber)Reflector.GetFieldValue(cluster, nameof(_eventSubscriber)); public static Task _monitorServersTask(this MultiServerCluster cluster) => (Task)Reflector.GetFieldValue(cluster, nameof(_monitorServersTask)); public static string _replicaSetName(this MultiServerCluster cluster) => (string)Reflector.GetFieldValue(cluster, nameof(_replicaSetName)); public static List _servers(this MultiServerCluster cluster) => (List)Reflector.GetFieldValue(cluster, nameof(_servers)); diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Clusters/SingleServerClusterTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Clusters/SingleServerClusterTests.cs index a20da4de89f..09b9ee76a3e 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Clusters/SingleServerClusterTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Clusters/SingleServerClusterTests.cs @@ -17,17 +17,19 @@ using System.Linq; using System.Net; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.Events; using MongoDB.Driver.Core.Servers; -using MongoDB.Driver.Core.Helpers; +using MongoDB.Driver.Core.TestHelpers; +using MongoDB.Driver.Core.TestHelpers.Logging; using Moq; using Xunit; +using Xunit.Abstractions; namespace MongoDB.Driver.Core.Clusters { - public class SingleServerClusterTests + public class SingleServerClusterTests : LoggableTestClass { private EventCapturer _capturedEvents; private MockClusterableServerFactory _mockServerFactory; @@ -35,10 +37,10 @@ public class SingleServerClusterTests private EndPoint _endPoint = new DnsEndPoint("localhost", 27017); - public SingleServerClusterTests() + public SingleServerClusterTests(ITestOutputHelper output) : base(output) { _settings = new ClusterSettings(); - _mockServerFactory = new MockClusterableServerFactory(); + _mockServerFactory = new MockClusterableServerFactory(LoggerFactory); _capturedEvents = new EventCapturer(); } @@ -46,7 +48,7 @@ public SingleServerClusterTests() public void Constructor_should_throw_if_more_than_one_endpoint_is_specified() { _settings = _settings.With(endPoints: new[] { _endPoint, new DnsEndPoint("localhost", 27018) }); - Action act = () => new SingleServerCluster(_settings, _mockServerFactory, _capturedEvents); + Action act = () => new SingleServerCluster(_settings, _mockServerFactory, _capturedEvents, loggerFactory: null); act.ShouldThrow(); } @@ -56,7 +58,7 @@ public void Constructor_should_throw_if_srvMaxHosts_is_greater_than_zero() { _settings = _settings.With(srvMaxHosts: 2); - var exception = Record.Exception(() => new SingleServerCluster(_settings, _mockServerFactory, _capturedEvents)); + var exception = Record.Exception(() => new SingleServerCluster(_settings, _mockServerFactory, _capturedEvents, loggerFactory: null)); exception.Should().BeOfType(); } @@ -193,7 +195,7 @@ public void ServerDescription_type_should_be_replaced_with_Unknown_when_legacy_h // private methods private SingleServerCluster CreateSubject() { - return new SingleServerCluster(_settings, _mockServerFactory, _capturedEvents); + return new SingleServerCluster(_settings, _mockServerFactory, _capturedEvents, LoggerFactory); } private void PublishDescription(EndPoint endPoint, ServerType serverType, ReplicaSetConfig replicaSetConfig = null) diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Compression/CompressorsTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Compression/CompressorsTests.cs index 2fe12d069d4..9f5e0529750 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Compression/CompressorsTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Compression/CompressorsTests.cs @@ -15,12 +15,12 @@ using System; using System.IO; +using System.Collections.Generic; using System.Text; using FluentAssertions; using MongoDB.Bson.IO; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Compression; -using MongoDB.Driver.TestHelpers; using SharpCompress.IO; using Xunit; @@ -28,15 +28,45 @@ namespace MongoDB.Driver.Core.Tests.Core.Compression { public class CompressorsTests { - private static string __testMessage = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"; + #region static + // private constants + private const string __testMessage = "abcdefghijklmnopqrstuvwxyz0123456789 abcdefghijklmnopqrstuvwxyz0123456789 abcdefghijklmnopqrstuvwxyz0123456789"; + private const string __testMessagePortion = @"Two households, both alike in dignity, + In fair Verona, where we lay our scene, + From ancient grudge break to new mutiny, + Where civil blood makes civil hands unclean. + From forth the fatal loins of these two foes + A pair of star-cross'd lovers take their life; + Whose misadventured piteous overthrows + Do with their death bury their parents' strife. + The fearful passage of their death-mark'd love, + And the continuance of their parents' rage, + Which, but their children's end, nought could remove, + Is now the two hours' traffic of our stage; + The which if you with patient ears attend, + What here shall miss, our toil shall strive to mend."; - [Theory] - [InlineData(CompressorType.Snappy)] - [InlineData(CompressorType.ZStandard)] - public void Compressor_should_read_the_previously_written_message(CompressorType compressorType) + // private static fields + private static readonly byte[] __bigMessage = GenerateBigMessage(135000); + + // private static methods + private static byte[] GenerateBigMessage(int size) + { + var resultBytes = new List(); + var messagePortionBytes = Encoding.ASCII.GetBytes(__testMessagePortion); + while (resultBytes.Count < size) + { + resultBytes.AddRange(messagePortionBytes); + } + return resultBytes.ToArray(); + } + #endregion + + [Fact] + public void Snappy_compressor_should_read_the_previously_written_message() { var bytes = Encoding.ASCII.GetBytes(__testMessage); - var compressor = GetCompressor(compressorType); + var compressor = GetCompressor(CompressorType.Snappy); Assert( bytes, (input, output) => @@ -73,7 +103,7 @@ public void Zlib_should_generate_expected_compressed_bytes() var result = string.Join(",", resultBytes); result .Should() - .Be("120,156,74,76,74,78,73,77,75,207,200,204,202,206,201,205,203,47,40,44,42,46,41,45,43,175,168,172,74,36,67,6,0,0,0,255,255,3,0,21,79,33,94"); + .Be("120,156,74,76,74,78,73,77,75,207,200,204,202,206,201,205,203,47,40,44,42,46,41,45,43,175,168,172,50,48,52,50,54,49,53,51,183,176,84,72,164,150,34,0,0,0,0,255,255,3,0,228,159,39,197"); }); } @@ -131,6 +161,61 @@ public void Zlib_should_throw_exception_if_the_level_is_out_of_range([Values(-2, e.ParamName.Should().Be("compressionLevel"); } + [Fact] + public void Zstandard_compress_should_throw_when_output_stream_is_null() + { + using (var input = new MemoryStream()) + { + var compressor = GetCompressor(CompressorType.ZStandard, 6); + var exception = Record.Exception(() => compressor.Compress(input, null)); + var e = exception.Should().BeOfType().Subject; + e.ParamName.Should().Be("stream"); + } + } + + [Fact] + public void Zstandard_compressed_size_with_low_compression_level_should_be_bigger_than_with_high() + { + var lengths = new List(); + // note: some close compression levels can give the same results for not huge text sizes + foreach (var compressionLevel in new[] { 1, 5, 10, 15, 22 }) + { + using (var input = new MemoryStream(__bigMessage)) + using (var output = new MemoryStream()) + { + var compressor = GetCompressor(CompressorType.ZStandard, compressionLevel); + compressor.Compress(input, output); + lengths.Add((int)output.Length); + } + } + lengths.Should().BeInDescendingOrder(); + } + + [Theory] + [ParameterAttributeData] + public void Zstandard_compressor_should_decompress_the_previously_compressed_message([Range(1, 22)] int compressionLevel) + { + var messageBytes = __bigMessage; + var compressor = GetCompressor(CompressorType.ZStandard, compressionLevel); + Assert( + messageBytes, + (input, output) => + { + compressor.Compress(input, output); + input.Length.Should().BeGreaterThan(output.Length); + input.Position = 0; + input.SetLength(0); + output.Position = 0; + compressor.Decompress(output, input); + }, + (input, output) => + { + input.Position = 0; + var resultBytes = input.ReadBytes((int)input.Length); + resultBytes.Should().Equal(messageBytes); + }); + } + private void Assert(byte[] bytes, Action test, Action assertResult = null) { using (var buffer = new ByteArrayBuffer(bytes)) @@ -155,7 +240,7 @@ private ICompressor GetCompressor(CompressorType compressorType, object option = case CompressorType.Zlib: return new ZlibCompressor((int)option); case CompressorType.ZStandard: - return new ZstandardCompressor(); + return new ZstandardCompressor((int)option); default: throw new NotSupportedException(); } diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Compression/SnappyNativeTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Compression/SnappyNativeTests.cs deleted file mode 100644 index cef936f51e4..00000000000 --- a/tests/MongoDB.Driver.Core.Tests/Core/Compression/SnappyNativeTests.cs +++ /dev/null @@ -1,243 +0,0 @@ -/* Original work: - * Copyright (c) 2016 - David Rouyer rouyer.david@gmail.com Copyright (c) 2011 - 2014 Robert Važan, Google Inc. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * - * * Neither the name of Robert Važan nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Modified work: - * Copyright 2020–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.IO; -using System.Linq; -using System.Text; -using FluentAssertions; -using MongoDB.Driver.Core.Compression.Snappy; -using Xunit; - -namespace MongoDB.Driver.Core.Tests.Core.Compression -{ - public class SnappyNativeTests - { - [Fact] - public void Compress_should_throw_if_parameter_null() - { - var exception = Record.Exception(() => SnappyCodec.Compress(null)); - exception.Should().BeOfType(); - } - - [Theory] - [InlineData("Hello")] - [InlineData("")] - [InlineData("!@#$%^&*()")] - public void Compress_should_uncompress_previously_compressed_message(string input) - { - var inputBytes = Encoding.ASCII.GetBytes(input); - var compressedBytes = SnappyCodec.Compress(inputBytes); - compressedBytes.Length.Should().BeGreaterThan(0); - - var uncompressedBytes = SnappyCodec.Uncompress(compressedBytes); - var outputString = Encoding.ASCII.GetString(uncompressedBytes); - outputString.Should().Be(input); - } - - [Theory] - [InlineData(null, 0, 3, 100, 0, typeof(ArgumentNullException))] - [InlineData(100, 0, 3, null, 0, typeof(ArgumentNullException))] - [InlineData(100, -1, 3, 100, 0, typeof(ArgumentOutOfRangeException))] - [InlineData(100, 0, -1, 100, 0, typeof(ArgumentOutOfRangeException))] - [InlineData(100, 90, 20, 100, 0, typeof(ArgumentOutOfRangeException))] - [InlineData(100, 0, 3, 100, -1, typeof(ArgumentOutOfRangeException))] - [InlineData(100, 0, 3, 100, 100, typeof(ArgumentOutOfRangeException))] - [InlineData(100, 0, 3, 100, 101, typeof(ArgumentOutOfRangeException))] - [InlineData(100, 0, 100, 3, 0, typeof(ArgumentOutOfRangeException))] - public void Compress_with_advanced_options_should_throw_if_parameters_incorrect(int? inputCount, int inputOffset, int inputLength, int? outputCount, int outputOffset, Type expectedException) - { - var input = inputCount.HasValue ? new byte[inputCount.Value] : null; - var output = outputCount.HasValue ? new byte[outputCount.Value] : null; - var outputLength = (output?.Length ?? 0) - outputOffset; - var exception = Record.Exception(() => SnappyCodec.Compress(input, inputOffset, inputLength, output, outputOffset, outputLength)); - exception.Should().BeOfType(expectedException); - } - - [Theory] - [InlineData(3, 5, 10, "Hello")] - [InlineData(0, 11, 0, "ByeHelloBye")] - public void Compress_with_advanced_options_should_work_as_expected(int inputOffset, int inputLength, int outputOffset, string expectedResult) - { - var input = Encoding.ASCII.GetBytes("ByeHelloBye"); - var output = new byte[100]; - - var outputLength = output.Length - outputOffset; - var compressedLength = SnappyCodec.Compress(input, inputOffset, inputLength, output, outputOffset, outputLength); - var compressedOutputWithoutOffset = output.Skip(outputOffset).Take(compressedLength).ToArray(); - - var uncompressed = SnappyCodec.Uncompress(compressedOutputWithoutOffset); - var uncompressedString = Encoding.ASCII.GetString(uncompressed); - uncompressedString.Should().Be(expectedResult); - } - - [Fact] - public void GetUncompressedLength_should_throw_if_parameter_null() - { - var exception = Record.Exception(() => SnappyCodec.GetUncompressedLength(null)); - exception.Should().BeOfType(); - } - - [Theory] - [InlineData(null, 0, 3, typeof(ArgumentNullException))] - [InlineData(true, -1, 20, typeof(ArgumentOutOfRangeException))] - [InlineData(true, 0, -1, typeof(ArgumentOutOfRangeException))] - [InlineData(true, 22 - 2, 4, typeof(ArgumentOutOfRangeException))] - [InlineData(true, 0, 0, typeof(InvalidDataException))] - [InlineData(true, 22, 0, typeof(InvalidDataException))] - [InlineData(false, 0, 10, typeof(InvalidDataException))] - public void GetUncompressedLength_with_advanced_options_should_throw_if_parameters_incorrect(bool? provideCorrectCompressedBytes, int inputOffset, int inputLength, Type expectedExceptionType) - { - var uncompressedBytes = Encoding.ASCII.GetBytes("Hello, hello, howdy?"); // 20 bytes - byte[] compressedBytes = null; - if (provideCorrectCompressedBytes.HasValue) - { - if (provideCorrectCompressedBytes.Value) - { - compressedBytes = SnappyCodec.Compress(uncompressedBytes); // 22 bytes - } - else - { - compressedBytes = Enumerable.Repeat((byte)0xff, 10).ToArray(); // 10 bytes - } - } - - var exception = Record.Exception(() => SnappyCodec.GetUncompressedLength(compressedBytes, inputOffset, inputLength)); - exception.Should().BeOfType(expectedExceptionType); - } - - [Fact] - public void Uncompress_should_throw_if_parameter_null() - { - var exception = Record.Exception(() => SnappyCodec.Uncompress(null)); - exception.Should().BeOfType(); - } - - [Theory] - [InlineData(null, 0, 3, 100, 0, typeof(ArgumentNullException))] - [InlineData(true, 0, 22, null, 0, typeof(ArgumentNullException))] - [InlineData(true, -1, 20, 100, 0, typeof(ArgumentOutOfRangeException))] - [InlineData(true, 0, -1, 100, 0, typeof(ArgumentOutOfRangeException))] - [InlineData(true, 22 - 2, 4, 100, 0, typeof(ArgumentOutOfRangeException))] - [InlineData(true, 0, 0, 100, 0, typeof(InvalidDataException))] - [InlineData(true, 22, 0, 100, 0, typeof(InvalidDataException))] - [InlineData(true, 0, 22, 100, -1, typeof(ArgumentOutOfRangeException))] - [InlineData(true, 0, 22, 100, 101, typeof(ArgumentOutOfRangeException))] - [InlineData(true, 0, 22, 100, 100, typeof(ArgumentOutOfRangeException))] - [InlineData(true, 0, 22, 100, 97, typeof(ArgumentOutOfRangeException))] - [InlineData(false, 0, 22, 100, 0, typeof(InvalidDataException))] - public void Uncompress_with_advanced_parameters_should_throw_if_parameters_incorrect(bool? provideCorrectCompressedBytes, int inputOffset, int inputLength, int? outputCount, int outputOffset, Type expectedException) - { - var uncompressedBytes = Encoding.ASCII.GetBytes("Hello, hello, howdy?"); // 20 bytes - byte[] compressedBytes = null; - if (provideCorrectCompressedBytes.HasValue) - { - if (provideCorrectCompressedBytes.Value) - { - compressedBytes = SnappyCodec.Compress(uncompressedBytes); - } - else - { - compressedBytes = new byte[inputLength]; - new Random(0).NextBytes(compressedBytes); - } - } - - var outputBytes = outputCount.HasValue ? new byte[outputCount.Value] : null; - - var outputLength = (outputBytes?.Length ?? 0) - outputOffset; - var exception = Record.Exception(() => SnappyCodec.Uncompress(compressedBytes, inputOffset, inputLength, outputBytes, outputOffset, outputLength)); - exception.Should().BeOfType(expectedException); - } - - [Fact] - public void Uncompress_with_advance_options_should_work_as_expected() - { - var uncompressedBytes1 = Encoding.ASCII.GetBytes("Howdy"); - var uncompressedBytes2 = Encoding.ASCII.GetBytes("Hello"); - - var compressedBytes2 = SnappyCodec.Compress(uncompressedBytes2); - - var padded = uncompressedBytes1.Take(3).Concat(compressedBytes2).Concat(uncompressedBytes1.Skip(3)).ToArray(); - - var output = new byte[100]; - - var outputLength = output.Length - 10; - var uncompressedLength = SnappyCodec.Uncompress(padded, 3, padded.Length - 5, output, 10, outputLength); - uncompressedLength.Should().Be(5); - - var outputBytes2 = output.Skip(10).Take(5).ToArray(); - var outputResult2 = Encoding.ASCII.GetString(outputBytes2); - outputResult2.Should().Be("Hello"); - } - - [Fact] - public void Validate_should_throw_if_parameter_null() - { - var exception = Record.Exception(() => SnappyCodec.Validate(null)); - exception.Should().BeOfType(); - } - - [Fact] - public void Validate_should_work_as_expected() - { - var uncompressedBytes = Encoding.ASCII.GetBytes("Hello, hello, howdy?"); - - var compressedBytes = SnappyCodec.Compress(uncompressedBytes); - var isValid = SnappyCodec.Validate(compressedBytes); - isValid.Should().BeTrue(); - - isValid = SnappyCodec.Validate(compressedBytes, 0, 0); - isValid.Should().BeFalse(); - - isValid = SnappyCodec.Validate(compressedBytes, compressedBytes.Length, 0); - isValid.Should().BeFalse(); - - var randomBytes = new byte[10]; - new Random(0).NextBytes(randomBytes); - - var isRandomBytesValid = SnappyCodec.Validate(randomBytes, 0, randomBytes.Length); - isRandomBytesValid.Should().BeFalse(); - } - - [Theory] - [InlineData(false, 0, 3, typeof(ArgumentNullException))] - [InlineData(true, -1, 20, typeof(ArgumentOutOfRangeException))] - [InlineData(true, 0, -1, typeof(ArgumentOutOfRangeException))] - [InlineData(true, 22, 4, typeof(ArgumentOutOfRangeException))] - public void Validate_with_advanced_options_should_throw_if_parameters_incorrect(bool provideCompressedBytes, int inputOffset, int inputLength, Type expectedExceptionType) - { - var uncompressedBytes = Encoding.ASCII.GetBytes("Hello, hello, howdy?"); // 20 bytes - var compressedBytes = provideCompressedBytes ? SnappyCodec.Compress(uncompressedBytes) : null; // 22 bytes - - var exception = Record.Exception(() => SnappyCodec.Validate(compressedBytes, inputOffset, inputLength)); - exception.Should().BeOfType(expectedExceptionType); - } - } -} diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Compression/ZstandardNativeTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Compression/ZstandardNativeTests.cs deleted file mode 100644 index 9d746d18079..00000000000 --- a/tests/MongoDB.Driver.Core.Tests/Core/Compression/ZstandardNativeTests.cs +++ /dev/null @@ -1,172 +0,0 @@ -/* Copyright 2020-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.IO; -using System.IO.Compression; -using System.Text; -using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; -using MongoDB.Driver.Core.Compression.Zstandard; -using MongoDB.Driver.Core.Misc; -using Xunit; - -namespace MongoDB.Driver.Core.Tests.Core.Compression -{ - public class ZstandardNativeTests - { - #region static - // private constants - private const string __testMessagePortion = @"Two households, both alike in dignity, - In fair Verona, where we lay our scene, - From ancient grudge break to new mutiny, - Where civil blood makes civil hands unclean. - From forth the fatal loins of these two foes - A pair of star-cross'd lovers take their life; - Whose misadventured piteous overthrows - Do with their death bury their parents' strife. - The fearful passage of their death-mark'd love, - And the continuance of their parents' rage, - Which, but their children's end, nought could remove, - Is now the two hours' traffic of our stage; - The which if you with patient ears attend, - What here shall miss, our toil shall strive to mend."; - - // private static fields - private static readonly byte[] __bigMessage = GenerateBigMessage(135000); // bigger than recommended size for one native operation - - // private static methods - private static byte[] GenerateBigMessage(int size) - { - var resultBytes = new List(); - var messagePortionBytes = Encoding.ASCII.GetBytes(__testMessagePortion); - while (resultBytes.Count < size) - { - resultBytes.AddRange(messagePortionBytes); - } - return resultBytes.ToArray(); - } - #endregion - - [Theory] - [ParameterAttributeData] - public void Compressor_should_decompress_the_previously_compressed_message([Range(1, 22)] int compressionLevel) - { - var messageBytes = __bigMessage; - - var compressedBytes = Compress(messageBytes, compressionLevel); - compressedBytes.Length.Should().BeLessThan(messageBytes.Length / 2); - - var decompressedBytes = Decompress(compressedBytes); - decompressedBytes.Should().Equal(messageBytes); - } - - [Theory] - [InlineData(1, "40,181,47,253,0,72,108,1,0,84,2,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,48,49,50,51,52,53,54,55,56,57,32,1,0,53,132,170,39,1,0,0")] - [InlineData(4, "40,181,47,253,0,88,108,1,0,84,2,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,48,49,50,51,52,53,54,55,56,57,32,1,0,53,132,170,39,1,0,0")] - [InlineData(15, "40,181,47,253,0,96,108,1,0,84,2,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,48,49,50,51,52,53,54,55,56,57,32,1,0,53,132,170,39,1,0,0")] - [InlineData(21, "40,181,47,253,0,128,108,1,0,84,2,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,48,49,50,51,52,53,54,55,56,57,32,1,0,53,132,170,39,1,0,0")] - public void Compress_should_generate_expected_bytes_for_different_compression_levels(int compressionLevel, string expectedBytes) - { - var testMessage = "abcdefghijklmnopqrstuvwxyz0123456789 abcdefghijklmnopqrstuvwxyz0123456789 abcdefghijklmnopqrstuvwxyz0123456789"; - var data = Encoding.ASCII.GetBytes(testMessage); - - var resultBytes = Compress(data, compressionLevel); - string.Join(",", resultBytes).Should().Be(expectedBytes); - } - - [Fact] - public void Compressed_size_with_low_compression_level_should_be_bigger_than_with_high() - { - var lengths = new List(); - // note: some close compression levels can give the same results for not huge text sizes - foreach (var compressionLevel in new[] { 1, 5, 10, 15, 22 }) - { - var compressedBytes = Compress(__bigMessage, compressionLevel); - lengths.Add(compressedBytes.Length); - } - lengths.Should().BeInDescendingOrder(); - } - - [Fact] - public void Constructor_should_throw_when_compressionMode_is_incorrect() - { - using (var memoryStream = new MemoryStream()) - { - var exception = Record.Exception(() => new ZstandardStream(memoryStream, (CompressionMode)2)); - var e = exception.Should().BeOfType().Subject; - e.ParamName.Should().Be("compressionMode"); - } - } - - [Fact] - public void Constructor_should_throw_when_compressedStream_is_null() - { - var exception = Record.Exception(() => new ZstandardStream(null, CompressionMode.Compress)); - var e = exception.Should().BeOfType().Subject; - e.ParamName.Should().Be("compressedStream"); - } - - [Fact] - public void Constructor_should_throw_when_compressionLevel_has_been_set_for_Decompress_mode() - { - using (var memoryStream = new MemoryStream()) - { - var exception = Record.Exception(() => new ZstandardStream(memoryStream, CompressionMode.Decompress, 1)); - var e = exception.Should().BeOfType().Subject; - e.ParamName.Should().Be("compressionLevel"); - } - } - - [Theory] - [ParameterAttributeData] - public void Constructor_should_throw_when_compressionLevel_is_out_of_range([Values(0, 23)] int compressionLevel) - { - using (var memoryStream = new MemoryStream()) - { - var exception = Record.Exception(() => new ZstandardStream(memoryStream, CompressionMode.Compress, compressionLevel)); - var e = exception.Should().BeOfType().Subject; - e.ParamName.Should().Be(nameof(compressionLevel)); - } - } - - // private methods - private byte[] Compress(byte[] data, int compressionLevel) - { - using (var inputStream = new MemoryStream(data)) - using (var outputStream = new MemoryStream()) - { - using (var zstandardStream = new ZstandardStream(outputStream, CompressionMode.Compress, compressionLevel)) - { - inputStream.EfficientCopyTo(zstandardStream); - zstandardStream.Flush(); - } - return outputStream.ToArray(); - } - } - - private byte[] Decompress(byte[] compressed) - { - using (var inputStream = new MemoryStream(compressed)) - using (var zstandardStream = new ZstandardStream(inputStream, CompressionMode.Decompress)) - using (var outputStream = new MemoryStream()) - { - zstandardStream.CopyTo(outputStream); - return outputStream.ToArray(); - } - } - } -} diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Configuration/ClusterSettingsTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Configuration/ClusterSettingsTests.cs index 7295d6f9a58..5cb824910ac 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Configuration/ClusterSettingsTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Configuration/ClusterSettingsTests.cs @@ -18,7 +18,7 @@ using System.Threading; using FluentAssertions; using MongoDB.Bson.TestHelpers.EqualityComparers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Misc; using Xunit; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Configuration/ConnectionPoolSettingsTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Configuration/ConnectionPoolSettingsTests.cs index 426c08c3d20..c5ba1d37e99 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Configuration/ConnectionPoolSettingsTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Configuration/ConnectionPoolSettingsTests.cs @@ -15,7 +15,7 @@ using System; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.Core.Configuration diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Configuration/ConnectionSettingsTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Configuration/ConnectionSettingsTests.cs index 19919b77599..764674b1504 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Configuration/ConnectionSettingsTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Configuration/ConnectionSettingsTests.cs @@ -15,7 +15,7 @@ using System; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Authentication; using MongoDB.Driver.Core.Compression; using Xunit; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Configuration/ConnectionStringTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Configuration/ConnectionStringTests.cs index 3581b1793fb..4b6055d1705 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Configuration/ConnectionStringTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Configuration/ConnectionStringTests.cs @@ -23,7 +23,7 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Compression; using Xunit; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Configuration/SslStreamSettingsTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Configuration/SslStreamSettingsTests.cs index dcc5a9d7e22..480471a1127 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Configuration/SslStreamSettingsTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Configuration/SslStreamSettingsTests.cs @@ -63,7 +63,8 @@ public void constructor_with_checkCertificateRevocation_should_initialize_instan [Fact] public void constructor_with_clientCertificates_should_initialize_instance() { - var clientCertificates = new[] { new X509Certificate() }; + var certificate = Array.Empty(); + var clientCertificates = new[] { new X509Certificate(certificate) }; var subject = new SslStreamSettings(clientCertificates: clientCertificates); @@ -135,8 +136,10 @@ public void With_checkCertificateRevocation_should_return_expected_result() [Fact] public void With_clientCertificates_should_return_expected_result() { - var oldClientCertificates = new[] { new X509Certificate() }; - var newClientCertificates = new[] { new X509Certificate() }; + var oldCertificate = Array.Empty(); + var newCertificate = Array.Empty(); + var oldClientCertificates = new[] { new X509Certificate(oldCertificate) }; + var newClientCertificates = new[] { new X509Certificate(newCertificate) }; var subject = new SslStreamSettings(clientCertificates: oldClientCertificates); var result = subject.With(clientCertificates: newClientCertificates); diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Configuration/TcpStreamSettingsTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Configuration/TcpStreamSettingsTests.cs index 8b791d0378d..d913c4ee2eb 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Configuration/TcpStreamSettingsTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Configuration/TcpStreamSettingsTests.cs @@ -17,7 +17,7 @@ using System.Net.Sockets; using System.Threading; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.Core.Configuration diff --git a/tests/MongoDB.Driver.Core.Tests/Core/ConnectionPools/ExclusiveConnectionPoolFactoryTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/ConnectionPools/ExclusiveConnectionPoolFactoryTests.cs index cea0d0fbb03..555b605fd7e 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/ConnectionPools/ExclusiveConnectionPoolFactoryTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/ConnectionPools/ExclusiveConnectionPoolFactoryTests.cs @@ -54,7 +54,7 @@ public ExclusiveConnectionPoolFactoryTests() [Fact] public void Constructor_should_throw_when_settings_is_null() { - Action act = () => new ExclusiveConnectionPoolFactory(null, _connectionFactory, _eventSubscriber); + Action act = () => new ExclusiveConnectionPoolFactory(null, _connectionFactory, _eventSubscriber, null); act.ShouldThrow(); } @@ -62,7 +62,7 @@ public void Constructor_should_throw_when_settings_is_null() [Fact] public void Constructor_should_throw_when_connectionFactory_is_null() { - Action act = () => new ExclusiveConnectionPoolFactory(_settings, null, _eventSubscriber); + Action act = () => new ExclusiveConnectionPoolFactory(_settings, null, _eventSubscriber, null); act.ShouldThrow(); } @@ -70,7 +70,7 @@ public void Constructor_should_throw_when_connectionFactory_is_null() [Fact] public void Constructor_should_throw_when_eventSubscriber_is_null() { - Action act = () => new ExclusiveConnectionPoolFactory(_settings, _connectionFactory, null); + Action act = () => new ExclusiveConnectionPoolFactory(_settings, _connectionFactory, null, null); act.ShouldThrow(); } @@ -78,7 +78,7 @@ public void Constructor_should_throw_when_eventSubscriber_is_null() [Fact] public void CreateConnectionPool_should_throw_when_serverId_is_null() { - var subject = new ExclusiveConnectionPoolFactory(_settings, _connectionFactory, _eventSubscriber); + var subject = new ExclusiveConnectionPoolFactory(_settings, _connectionFactory, _eventSubscriber, null); Action act = () => subject.CreateConnectionPool(null, _endPoint, _connectionExceptionHandler); act.ShouldThrow(); @@ -87,7 +87,7 @@ public void CreateConnectionPool_should_throw_when_serverId_is_null() [Fact] public void CreateConnectionPool_should_throw_when_endPoint_is_null() { - var subject = new ExclusiveConnectionPoolFactory(_settings, _connectionFactory, _eventSubscriber); + var subject = new ExclusiveConnectionPoolFactory(_settings, _connectionFactory, _eventSubscriber, null); Action act = () => subject.CreateConnectionPool(_serverId, null, _connectionExceptionHandler); act.ShouldThrow(); @@ -96,7 +96,7 @@ public void CreateConnectionPool_should_throw_when_endPoint_is_null() [Fact] public void CreateConnectionPool_should_throw_when_exception_handler_is_null() { - var subject = new ExclusiveConnectionPoolFactory(_settings, _connectionFactory, _eventSubscriber); + var subject = new ExclusiveConnectionPoolFactory(_settings, _connectionFactory, _eventSubscriber, null); Action act = () => subject.CreateConnectionPool(null, _endPoint, null); act.ShouldThrow(); @@ -105,7 +105,7 @@ public void CreateConnectionPool_should_throw_when_exception_handler_is_null() [Fact] public void CreateConnectionPool_should_return_a_ConnectionPool() { - var subject = new ExclusiveConnectionPoolFactory(_settings, _connectionFactory, _eventSubscriber); + var subject = new ExclusiveConnectionPoolFactory(_settings, _connectionFactory, _eventSubscriber, null); var result = subject.CreateConnectionPool(_serverId, _endPoint, _connectionExceptionHandler); diff --git a/tests/MongoDB.Driver.Core.Tests/Core/ConnectionPools/ExclusiveConnectionPoolTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/ConnectionPools/ExclusiveConnectionPoolTests.cs index c4718ff835b..56570e094f9 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/ConnectionPools/ExclusiveConnectionPoolTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/ConnectionPools/ExclusiveConnectionPoolTests.cs @@ -22,20 +22,20 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.Connections; using MongoDB.Driver.Core.Events; -using MongoDB.Driver.Core.Helpers; +using MongoDB.Driver.Core.Logging; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; +using MongoDB.Driver.Core.TestHelpers; using MongoDB.Driver.Core.TestHelpers.Logging; using Moq; using Xunit; using Xunit.Abstractions; using static MongoDB.Driver.Core.ConnectionPools.ExclusiveConnectionPool; -using static MongoDB.Driver.Core.Tests.Core.ConnectionPools.MaintenanceHelperTests; namespace MongoDB.Driver.Core.ConnectionPools { @@ -46,6 +46,7 @@ public class ExclusiveConnectionPoolTests : LoggableTestClass private Mock _mockConnectionExceptionHandler; private DnsEndPoint _endPoint; private EventCapturer _capturedEvents; + private EventLogger _eventLogger; private ServerId _serverId; private ConnectionPoolSettings _settings; private ExclusiveConnectionPool _subject; @@ -56,7 +57,10 @@ public ExclusiveConnectionPoolTests(ITestOutputHelper output) : base(output) _mockConnectionExceptionHandler = new Mock(); _endPoint = new DnsEndPoint("localhost", 27017); _capturedEvents = new EventCapturer(); + _eventLogger = _capturedEvents.ToEventLogger(); _serverId = new ServerId(new ClusterId(), _endPoint); + + _mockConnectionFactory.Setup(f => f.ConnectionSettings).Returns(() => new ConnectionSettings()); _mockConnectionFactory .Setup(c => c.CreateConnection(It.IsAny(), It.IsAny())) .Returns(() => @@ -76,7 +80,7 @@ public ExclusiveConnectionPoolTests(ITestOutputHelper output) : base(output) [Fact] public void Constructor_should_throw_when_serverId_is_null() { - Action act = () => new ExclusiveConnectionPool(null, _endPoint, _settings, _mockConnectionFactory.Object, _capturedEvents, _mockConnectionExceptionHandler.Object); + Action act = () => new ExclusiveConnectionPool(null, _endPoint, _settings, _mockConnectionFactory.Object, _mockConnectionExceptionHandler.Object, _eventLogger); act.ShouldThrow(); } @@ -84,7 +88,7 @@ public void Constructor_should_throw_when_serverId_is_null() [Fact] public void Constructor_should_throw_when_endPoint_is_null() { - Action act = () => new ExclusiveConnectionPool(_serverId, null, _settings, _mockConnectionFactory.Object, _capturedEvents, _mockConnectionExceptionHandler.Object); + Action act = () => new ExclusiveConnectionPool(_serverId, null, _settings, _mockConnectionFactory.Object, _mockConnectionExceptionHandler.Object, _eventLogger); act.ShouldThrow(); } @@ -92,7 +96,7 @@ public void Constructor_should_throw_when_endPoint_is_null() [Fact] public void Constructor_should_throw_when_settings_is_null() { - Action act = () => new ExclusiveConnectionPool(_serverId, _endPoint, null, _mockConnectionFactory.Object, _capturedEvents, _mockConnectionExceptionHandler.Object); + Action act = () => new ExclusiveConnectionPool(_serverId, _endPoint, null, _mockConnectionFactory.Object, _mockConnectionExceptionHandler.Object, _eventLogger); act.ShouldThrow(); } @@ -100,15 +104,15 @@ public void Constructor_should_throw_when_settings_is_null() [Fact] public void Constructor_should_throw_when_connectionFactory_is_null() { - Action act = () => new ExclusiveConnectionPool(_serverId, _endPoint, _settings, null, _capturedEvents, _mockConnectionExceptionHandler.Object); + Action act = () => new ExclusiveConnectionPool(_serverId, _endPoint, _settings, null, _mockConnectionExceptionHandler.Object, _eventLogger); act.ShouldThrow(); } [Fact] - public void Constructor_should_throw_when_eventSubscriber_is_null() + public void Constructor_should_throw_when_eventLogger_is_null() { - Action act = () => new ExclusiveConnectionPool(_serverId, _endPoint, _settings, _mockConnectionFactory.Object, null, _mockConnectionExceptionHandler.Object); + Action act = () => new ExclusiveConnectionPool(_serverId, _endPoint, _settings, _mockConnectionFactory.Object, _mockConnectionExceptionHandler.Object, null); act.ShouldThrow(); } @@ -131,6 +135,7 @@ public void AcquireConnection_should_iterate_over_all_dormant_connections() var syncRoot = new object(); var mockConnectionFactory = new Mock { DefaultValue = DefaultValue.Mock }; + mockConnectionFactory.Setup(f => f.ConnectionSettings).Returns(() => new ConnectionSettings()); mockConnectionFactory .Setup(f => f.CreateConnection(_serverId, _endPoint)) .Returns(() => @@ -204,7 +209,7 @@ public void AcquireConnection_should_iterate_over_all_dormant_connections() [Fact] public void Constructor_should_throw_when_exceptionHandler_is_null() { - Action act = () => new ExclusiveConnectionPool(_serverId, _endPoint, _settings, _mockConnectionFactory.Object, _capturedEvents, null); + Action act = () => new ExclusiveConnectionPool(_serverId, _endPoint, _settings, _mockConnectionFactory.Object, null, _eventLogger); act.ShouldThrow(); } @@ -311,10 +316,14 @@ public async Task AcquireConnection_should_invoke_error_handling_before_releasin .Callback(() => subject.AvailableCount.Should().Be(maxConnections - 1)); var mockConnectionFactory = new Mock { DefaultValue = DefaultValue.Mock }; + mockConnectionFactory.Setup(f => f.ConnectionSettings).Returns(() => new ConnectionSettings()); mockConnectionFactory .Setup(c => c.CreateConnection(It.IsAny(), It.IsAny())) .Returns(() => { + connectionMock + .Setup(c => c.ConnectionId) + .Returns(connectionId); connectionMock .Setup(c => c.Settings) .Returns(new ConnectionSettings()); @@ -363,9 +372,17 @@ internal void AcquireConnection_should_track_checked_out_reasons( { var subjectSettings = new ConnectionPoolSettings(minConnections: 0, maintenanceInterval: TimeSpan.FromDays(1)); - var mockConnectionFactory = Mock.Of(c => c.CreateConnection(_serverId, _endPoint) == Mock.Of()); + var connectionMock = new Mock(); + var connectionId = new ConnectionId(new ServerId(new ClusterId(), new DnsEndPoint("localhost", 1234))); + connectionMock.SetupGet(c => c.ConnectionId).Returns(connectionId); - var subject = CreateSubject(subjectSettings, connectionFactory: mockConnectionFactory); + var mockConnectionFactory = new Mock { DefaultValue = DefaultValue.Mock }; + mockConnectionFactory.Setup(f => f.ConnectionSettings).Returns(() => new ConnectionSettings()); + mockConnectionFactory + .Setup(c => c.CreateConnection(It.IsAny(), It.IsAny())) + .Returns(() => connectionMock.Object); + + var subject = CreateSubject(subjectSettings, connectionFactory: mockConnectionFactory.Object); InitializeAndWait(subject, subjectSettings); _capturedEvents.Clear(); @@ -612,6 +629,7 @@ public void AcquireConnection_should_timeout_when_non_sufficient_reused_connecti var establishingCount = new CountdownEvent(maxConnecting + initalAcquiredCount); var mockConnectionFactory = new Mock(); + mockConnectionFactory.Setup(f => f.ConnectionSettings).Returns(() => new ConnectionSettings()); mockConnectionFactory .Setup(c => c.CreateConnection(It.IsAny(), It.IsAny())) .Returns(() => @@ -786,6 +804,7 @@ public void Acquire_and_release_connection_stress_test( _capturedEvents.Clear(); var mockConnectionFactory = new Mock(); + mockConnectionFactory.Setup(f => f.ConnectionSettings).Returns(() => new ConnectionSettings()); mockConnectionFactory .Setup(c => c.CreateConnection(It.IsAny(), It.IsAny())) .Returns(() => @@ -926,6 +945,8 @@ public void Clear_with_serviceId_should_cause_only_expected_connections_to_be_ex { var serviceId = ObjectId.GenerateNewId(); var mockConnectionFactory = new Mock { DefaultValue = DefaultValue.Mock }; + mockConnectionFactory.Setup(f => f.ConnectionSettings).Returns(() => new ConnectionSettings()); + var connectionId = new ConnectionId(_serverId); var connectionMock = new Mock(); connectionMock .SetupGet(c => c.Description) @@ -933,6 +954,9 @@ public void Clear_with_serviceId_should_cause_only_expected_connections_to_be_ex new ConnectionDescription( new ConnectionId(_serverId), new HelloResult(new BsonDocument("serviceId", serviceId).Add("maxWireVersion", WireVersion.Server50)))); + connectionMock + .SetupGet(c => c.ConnectionId) + .Returns(connectionId); connectionMock .SetupGet(c => c.Settings) .Returns(new ConnectionSettings()); @@ -1114,12 +1138,14 @@ public void In_use_marker_should_work_as_expected( [Fact] public void Maintenance_should_call_connection_dispose_when_connection_authentication_fail() { - var authenticationException = new MongoAuthenticationException(new ConnectionId(_serverId), "test message"); + var connectionId = new ConnectionId(_serverId); + var authenticationException = new MongoAuthenticationException(connectionId, "test message"); var authenticationFailedConnection = new Mock(); authenticationFailedConnection .Setup(c => c.Open(It.IsAny())) // an authentication exception is thrown from _connectionInitializer.InitializeConnection // that in turn is called from OpenAsync .Throws(authenticationException); + authenticationFailedConnection.SetupGet(c => c.ConnectionId).Returns(connectionId); _mockConnectionExceptionHandler .Setup(handler => handler.HandleExceptionOnOpen(authenticationException)) @@ -1190,6 +1216,7 @@ public void MaxConnecting_queue_should_be_cleared_on_pool_clear( var blockEstablishmentEvent = new ManualResetEventSlim(false); var mockConnectionFactory = new Mock(); + mockConnectionFactory.Setup(f => f.ConnectionSettings).Returns(() => new ConnectionSettings()); mockConnectionFactory .Setup(c => c.CreateConnection(It.IsAny(), It.IsAny())) .Returns(() => @@ -1289,6 +1316,7 @@ public void Prune_should_respect_generation_when_closing_inUse_connections( waitQueueTimeout: TimeSpan.FromMinutes(1)); var mockConnectionFactory = new Mock { DefaultValue = DefaultValue.Mock }; + mockConnectionFactory.Setup(f => f.ConnectionSettings).Returns(() => new ConnectionSettings()); mockConnectionFactory .Setup(c => c.CreateConnection(It.IsAny(), It.IsAny())) .Returns(CreateConnection); @@ -1315,18 +1343,18 @@ public void Prune_should_respect_generation_when_closing_inUse_connections( subject.Clear(true); capturedEvents.WaitForOrThrowIfTimeout(events => events.Count() >= connectionsToBeRemovedCount, TimeSpan.FromSeconds(5)); - var removedConnections = new HashSet( + var removedConnections = new HashSet( capturedEvents .Events .OfType() - .Select(c => c.ConnectionId.LocalValue)); + .Select(c => c.ConnectionId.LongLocalValue)); foreach (var connection in allConnections) { connection.IsExpired.Should().BeTrue(); removedConnections - .Contains(connection.ConnectionId.LocalValue) + .Contains(connection.ConnectionId.LongLocalValue) .Should().Be(connection.Generation <= maxExpiredGeneration); } @@ -1358,6 +1386,7 @@ public void PrunePoolAsync_should_remove_all_expired_connections([RandomSeed] in var syncRoot = new object(); var mockConnectionFactory = new Mock { DefaultValue = DefaultValue.Mock }; + mockConnectionFactory.Setup(f => f.ConnectionSettings).Returns(() => new ConnectionSettings()); mockConnectionFactory .Setup(f => f.CreateConnection(_serverId, _endPoint)) .Returns(() => @@ -1417,14 +1446,14 @@ public void PrunePoolAsync_should_remove_all_expired_connections([RandomSeed] in _capturedEvents.Events .Take(connectionsCount * 2) .OfType() - .Select(e => e.ConnectionId.LocalValue) - .ShouldBeEquivalentTo(connectionsExpired.Select(c => c.LocalValue)); + .Select(e => e.ConnectionId.LongLocalValue) + .ShouldBeEquivalentTo(connectionsExpired.Select(c => c.LongLocalValue)); _capturedEvents.Events .Take(connectionsCount * 2) .OfType() - .Select(e => e.ConnectionId.LocalValue) - .ShouldAllBeEquivalentTo(connectionsExpired.Select(c => c.LocalValue)); + .Select(e => e.ConnectionId.LongLocalValue) + .ShouldAllBeEquivalentTo(connectionsExpired.Select(c => c.LongLocalValue)); } [Theory] @@ -1445,6 +1474,7 @@ public void WaitQueue_should_throw_when_full( var allAcquiringCountdownEvent = new CountdownEvent(waitQueueSize); var mockConnectionFactory = new Mock(); + mockConnectionFactory.Setup(f => f.ConnectionSettings).Returns(() => new ConnectionSettings()); mockConnectionFactory .Setup(c => c.CreateConnection(It.IsAny(), It.IsAny())) .Returns(() => @@ -1536,6 +1566,7 @@ public void WaitQueue_should_be_cleared_on_pool_clear( var blockEstablishmentEvent = new ManualResetEventSlim(false); var mockConnectionFactory = new Mock(); + mockConnectionFactory.Setup(f => f.ConnectionSettings).Returns(() => new ConnectionSettings()); mockConnectionFactory .Setup(c => c.CreateConnection(It.IsAny(), It.IsAny())) .Returns(() => @@ -1617,6 +1648,7 @@ public void WaitQueue_should_release_slot_after_connection_checkout( minConnections: 0); var mockConnectionFactory = new Mock(); + mockConnectionFactory.Setup(f => f.ConnectionSettings).Returns(() => new ConnectionSettings()); mockConnectionFactory .Setup(c => c.CreateConnection(It.IsAny(), It.IsAny())) .Returns(() => @@ -1675,8 +1707,8 @@ private ExclusiveConnectionPool CreateSubject( _endPoint, connectionPoolSettings ?? _settings, connectionFactory ?? _mockConnectionFactory.Object, - eventCapturer ?? _capturedEvents, - connectionExceptionHandler ?? _mockConnectionExceptionHandler.Object); + connectionExceptionHandler ?? _mockConnectionExceptionHandler.Object, + (eventCapturer ?? _capturedEvents).ToEventLogger()); } private void InitializeAndWait(ExclusiveConnectionPool pool = null, ConnectionPoolSettings poolSettings = null) diff --git a/tests/MongoDB.Driver.Core.Tests/Core/ConnectionPools/MaintenanceHelperTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/ConnectionPools/MaintenanceHelperTests.cs index 309ceafd91c..f8b7346b63f 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/ConnectionPools/MaintenanceHelperTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/ConnectionPools/MaintenanceHelperTests.cs @@ -20,14 +20,16 @@ using System.Threading; using FluentAssertions; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.ConnectionPools; using MongoDB.Driver.Core.Connections; using MongoDB.Driver.Core.Events; using MongoDB.Driver.Core.Helpers; +using MongoDB.Driver.Core.Logging; using MongoDB.Driver.Core.Servers; +using MongoDB.Driver.Core.TestHelpers; using Moq; using Xunit; @@ -158,7 +160,7 @@ public void Stop_should_trigger_immidiate_maintenace_call( var maitenanceInPlayTimeout = TimeSpan.FromMilliseconds(50); eventCapturer.WaitForEventOrThrowIfTimeout(maitenanceInPlayTimeout); - eventCapturer.Next().Should().BeOfType().Which.ConnectionId.LocalValue.Should().Be(1); // minPoolSize has been enrolled + eventCapturer.Next().Should().BeOfType().Which.ConnectionId.LongLocalValue.Should().Be(1); // minPoolSize has been enrolled eventCapturer.Any().Should().BeFalse(); SpinWait.SpinUntil(() => pool.ConnectionHolder._connections().Count > 0, TimeSpan.FromSeconds(1)).Should().BeTrue(); // wait until connection 1 has been returned to the pool after minPoolSize logic @@ -167,7 +169,7 @@ public void Stop_should_trigger_immidiate_maintenace_call( if (checkOutConnection) { acquiredConnection = pool.AcquireConnection(CancellationToken.None); - acquiredConnection.ConnectionId.LocalValue.Should().Be(1); + acquiredConnection.ConnectionId.LongLocalValue.Should().Be(1); } IncrementGeneration(pool); @@ -207,6 +209,7 @@ private ExclusiveConnectionPool CreatePool( Action> connectionFactoryConfigurator = null) { var mockConnectionFactory = new Mock(); + mockConnectionFactory.Setup(f => f.ConnectionSettings).Returns(() => new ConnectionSettings()); if (connectionFactoryConfigurator == null) { mockConnectionFactory @@ -223,8 +226,8 @@ private ExclusiveConnectionPool CreatePool( __endPoint, new ConnectionPoolSettings(maintenanceInterval: maintenanceInterval.GetValueOrDefault(defaultValue: __dummyInterval), minConnections: minPoolSize), mockConnectionFactory.Object, - eventCapturer ?? Mock.Of(), - Mock.Of()); + Mock.Of(), + eventCapturer.ToEventLogger()); exclusiveConnectionPool.Initialize(); exclusiveConnectionPool.SetReady(); // MaintenanceHelper is started diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Connections/BinaryConnectionFactoryTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Connections/BinaryConnectionFactoryTests.cs index 5581dc5e175..2483adf1922 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Connections/BinaryConnectionFactoryTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Connections/BinaryConnectionFactoryTests.cs @@ -38,7 +38,8 @@ public void Constructor_should_throw_an_ArgumentNullException_when_connectionSet settings: null, streamFactory, eventSubscriber, - serverApi: null); + serverApi: null, + loggerFactory: null); act.ShouldThrow(); } @@ -52,7 +53,8 @@ public void Constructor_should_throw_an_ArgumentNullException_when_streamFactory new ConnectionSettings(), streamFactory: null, eventSubscriber, - serverApi: null); + serverApi: null, + loggerFactory: null); act.ShouldThrow(); } @@ -66,7 +68,8 @@ public void CreateConnection_should_throw_an_ArgumentNullException_when_serverId new ConnectionSettings(), streamFactory, eventSubscriber, - serverApi: null); + serverApi: null, + loggerFactory: null); Action act = () => subject.CreateConnection(null, new DnsEndPoint("localhost", 27017)); act.ShouldThrow(); @@ -81,7 +84,8 @@ public void CreateConnection_should_throw_an_ArgumentNullException_when_endPoint new ConnectionSettings(), streamFactory, eventSubscriber, - serverApi: null); + serverApi: null, + loggerFactory: null); var serverId = new ServerId(new ClusterId(), new DnsEndPoint("localhost", 27017)); @@ -99,7 +103,8 @@ public void CreateConnection_should_return_a_BinaryConnection() new ConnectionSettings(), streamFactory, eventSubscriber, - serverApi); + serverApi, + loggerFactory: null); var serverId = new ServerId(new ClusterId(), new DnsEndPoint("localhost", 27017)); diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Connections/BinaryConnectionTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Connections/BinaryConnectionTests.cs index 8a54f112eb6..03481e8a2b1 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Connections/BinaryConnectionTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Connections/BinaryConnectionTests.cs @@ -14,9 +14,7 @@ */ using System; -using System.Collections.Generic; using System.IO; -using System.Linq; using System.Net; using System.Net.Sockets; using System.Threading; @@ -24,13 +22,14 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.Events; using MongoDB.Driver.Core.Helpers; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; +using MongoDB.Driver.Core.TestHelpers.Logging; using MongoDB.Driver.Core.WireProtocol.Messages; using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; using Moq; @@ -38,7 +37,7 @@ namespace MongoDB.Driver.Core.Connections { - public class BinaryConnectionTests + public class BinaryConnectionTests : LoggableTestClass { private Mock _mockConnectionInitializer; private ConnectionDescription _connectionDescription; @@ -48,7 +47,7 @@ public class BinaryConnectionTests private Mock _mockStreamFactory; private BinaryConnection _subject; - public BinaryConnectionTests() + public BinaryConnectionTests(Xunit.Abstractions.ITestOutputHelper output) : base(output) { _capturedEvents = new EventCapturer(); _mockStreamFactory = new Mock(); @@ -79,7 +78,8 @@ public BinaryConnectionTests() settings: new ConnectionSettings(), streamFactory: _mockStreamFactory.Object, connectionInitializer: _mockConnectionInitializer.Object, - eventSubscriber: _capturedEvents); + eventSubscriber: _capturedEvents, + LoggerFactory); } [Fact] diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Connections/BinaryConnection_CommandEventTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Connections/BinaryConnection_CommandEventTests.cs index 7305adaeced..5e3d5db477e 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Connections/BinaryConnection_CommandEventTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Connections/BinaryConnection_CommandEventTests.cs @@ -28,14 +28,16 @@ using MongoDB.Driver.Core.Helpers; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; +using MongoDB.Driver.Core.TestHelpers.Logging; using MongoDB.Driver.Core.WireProtocol.Messages; using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; using Moq; using Xunit; +using Xunit.Abstractions; namespace MongoDB.Driver.Core.Connections { - public class BinaryConnection_CommandEventTests : IDisposable + public class BinaryConnection_CommandEventTests : LoggableTestClass { private Mock _mockConnectionInitializer; private DnsEndPoint _endPoint; @@ -69,7 +71,7 @@ public static IEnumerable GetPotentiallyRedactedCommandTestCases() }; } - public BinaryConnection_CommandEventTests() + public BinaryConnection_CommandEventTests(ITestOutputHelper output) : base(output) { _capturedEvents = new EventCapturer() .Capture() @@ -97,7 +99,8 @@ public BinaryConnection_CommandEventTests() settings: new ConnectionSettings(), streamFactory: _mockStreamFactory.Object, connectionInitializer: _mockConnectionInitializer.Object, - eventSubscriber: _capturedEvents); + eventSubscriber: _capturedEvents, + LoggerFactory); _stream = new BlockingMemoryStream(); _mockStreamFactory.Setup(f => f.CreateStreamAsync(_endPoint, CancellationToken.None)) @@ -108,7 +111,7 @@ public BinaryConnection_CommandEventTests() _operationIdDisposer = EventContext.BeginOperation(); } - public void Dispose() + protected override void DisposeInternal() { _stream.Dispose(); _operationIdDisposer.Dispose(); diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Connections/ClientDocumentHelperTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Connections/ClientDocumentHelperTests.cs index d25a824553a..69c84c4b1ff 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Connections/ClientDocumentHelperTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Connections/ClientDocumentHelperTests.cs @@ -20,7 +20,7 @@ using System.Threading.Tasks; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.Core.Connections diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Connections/CommandEventHelperTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Connections/CommandEventHelperTests.cs index 8c59b69732c..cfbfbeaf262 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Connections/CommandEventHelperTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Connections/CommandEventHelperTests.cs @@ -14,9 +14,14 @@ */ using FluentAssertions; +using Microsoft.Extensions.Logging; using MongoDB.Bson; using MongoDB.Bson.TestHelpers; +using MongoDB.TestHelpers.XunitExtensions; +using MongoDB.Driver.Core.Events; +using MongoDB.Driver.Core.Logging; using MongoDB.Driver.Core.Misc; +using Moq; using Xunit; namespace MongoDB.Driver.Core.Connections @@ -46,10 +51,42 @@ public void ShouldRedactCommand_should_return_expected_result(string commandJson result.Should().Be(expectedResult); } + + [Theory] + [ParameterAttributeData] + public void ShouldTrackState_should_be_correct( + [Values(false, true)] bool logCommands, + [Values(false, true)] bool captureCommandSucceeded, + [Values(false, true)] bool captureCommandFailed) + { + var mockLogger = new Mock>(); + mockLogger.Setup(m => m.IsEnabled(LogLevel.Debug)).Returns(logCommands); + + var eventCapturer = new EventCapturer(); + // Capture unrelated event, so events filtering is enabled. + eventCapturer.Capture(); + if (captureCommandSucceeded) + { + eventCapturer.Capture(_ => true); + } + if (captureCommandFailed) + { + eventCapturer.Capture(_ => true); + } + + var eventLogger = new EventLogger(eventCapturer, mockLogger.Object); + var commandHelper = new CommandEventHelper(eventLogger); + + commandHelper._shouldTrackState().Should().Be(logCommands || captureCommandSucceeded || captureCommandFailed); + } } - public static class CommandEventHelperReflector + internal static class CommandEventHelperReflector { + public static bool _shouldTrackState(this CommandEventHelper commandEventHelper) => + (bool)Reflector.GetFieldValue(commandEventHelper, nameof(_shouldTrackState)); + + public static bool ShouldRedactCommand(BsonDocument command) => (bool)Reflector.InvokeStatic(typeof(CommandEventHelper), nameof(ShouldRedactCommand), command); } diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Connections/ConnectionIdTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Connections/ConnectionIdTests.cs index f2741514dcb..2f1158c5751 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Connections/ConnectionIdTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Connections/ConnectionIdTests.cs @@ -17,7 +17,6 @@ using System.Net; using FluentAssertions; using MongoDB.Driver.Core.Clusters; -using MongoDB.Driver.Core.Connections; using MongoDB.Driver.Core.Servers; using Xunit; @@ -74,30 +73,36 @@ public void Equals_should_return_expected_result( } [Fact] - public void LocalValues_of_2_ids_should_not_be_the_same_when_automically_constructed() + public void LongLocalValues_of_2_ids_should_not_be_the_same_when_automatically_constructed() { var subject = new ConnectionId(__serverId); var subject2 = new ConnectionId(__serverId); - subject.LocalValue.Should().NotBe(subject2.LocalValue); + subject.LongLocalValue.Should().NotBe(subject2.LongLocalValue); } - [Fact] - public void LocalValue_should_be_what_was_specified_in_the_constructor() + [Theory] + [InlineData(0)] + [InlineData(int.MaxValue)] + [InlineData((long)int.MaxValue+1)] + public void LongLocalValue_should_be_what_was_specified_in_the_constructor(long localValue) { - var subject = new ConnectionId(__serverId, 10); + var subject = new ConnectionId(__serverId, localValue); - subject.LocalValue.Should().Be(10); + subject.LongLocalValue.Should().Be(localValue); } - [Fact] - public void WithServerValue_should_set_the_server_value_and_leave_the_LocalValue_alone() + [Theory] + [InlineData(0)] + [InlineData(int.MaxValue)] + [InlineData((long)int.MaxValue+1)] + public void WithServerValue_should_set_the_server_value_and_leave_the_LocalValue_alone(long serverValue) { var subject = new ConnectionId(__serverId, 10) - .WithServerValue(11); + .WithServerValue(serverValue); - subject.LocalValue.Should().Be(10); - subject.ServerValue.Should().Be(11); + subject.LongLocalValue.Should().Be(10); + subject.LongServerValue.Should().Be(serverValue); } } } diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Connections/ConnectionInitializerTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Connections/ConnectionInitializerTests.cs index 4ac4febf8d0..d4c8ffcdde5 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Connections/ConnectionInitializerTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Connections/ConnectionInitializerTests.cs @@ -20,17 +20,18 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.TestHelpers; -using MongoDB.Driver.Core.Clusters; -using MongoDB.Driver.Core.Misc; -using MongoDB.Driver.Core.Servers; -using MongoDB.Driver.Core.Helpers; -using Xunit; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Authentication; +using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Compression; using MongoDB.Driver.Core.Configuration; +using MongoDB.Driver.Core.Helpers; +using MongoDB.Driver.Core.Misc; +using MongoDB.Driver.Core.Servers; +using MongoDB.Driver.Core.TestHelpers; using MongoDB.Driver.Core.WireProtocol.Messages; using Moq; +using Xunit; namespace MongoDB.Driver.Core.Connections { @@ -130,7 +131,7 @@ public void InitializeConnection_should_acquire_connectionId_from_hello_response var sentMessages = connection.GetSentMessages(); sentMessages.Should().HaveCount(1); - result.ConnectionId.ServerValue.Should().Be(1); + result.ConnectionId.LongServerValue.Should().Be(1); } [Theory] @@ -148,7 +149,7 @@ public void InitializeConnection_should_acquire_connectionId_from_legacy_hello_r var sentMessages = connection.GetSentMessages(); sentMessages.Should().HaveCount(1); - result.ConnectionId.ServerValue.Should().Be(1); + result.ConnectionId.LongServerValue.Should().Be(1); } [Theory] @@ -204,7 +205,7 @@ public void InitializeConnection_with_serverApi_should_send_hello([Values(false, var result = InitializeConnection(subject, connection, async, CancellationToken.None); - result.ConnectionId.ServerValue.Should().Be(1); + result.ConnectionId.LongServerValue.Should().Be(1); SpinWait.SpinUntil(() => connection.GetSentMessages().Count >= 1, TimeSpan.FromSeconds(5)).Should().BeTrue(); @@ -231,7 +232,7 @@ public void InitializeConnection_without_serverApi_should_send_legacy_hello([Val var result = InitializeConnection(subject, connection, async, CancellationToken.None); - result.ConnectionId.ServerValue.Should().Be(1); + result.ConnectionId.LongServerValue.Should().Be(1); SpinWait.SpinUntil(() => connection.GetSentMessages().Count >= 1, TimeSpan.FromSeconds(5)).Should().BeTrue(); @@ -264,7 +265,7 @@ public void InitializeConnection_should_build_the_ConnectionDescription_correctl var result = InitializeConnection(subject, connection, async, CancellationToken.None); result.MaxWireVersion.Should().Be(6); - result.ConnectionId.ServerValue.Should().Be(10); + result.ConnectionId.LongServerValue.Should().Be(10); result.AvailableCompressors.Count.Should().Be(1); result.AvailableCompressors.Should().Contain(ToCompressorTypeEnum(compressorType)); diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Connections/HelloHelperTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Connections/HelloHelperTests.cs index 0e2d28a8e07..8a9755cfcca 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Connections/HelloHelperTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Connections/HelloHelperTests.cs @@ -18,7 +18,7 @@ using MongoDB.Bson; using MongoDB.Bson.TestHelpers; using Xunit; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Compression; using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.Misc; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Connections/HelloResultTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Connections/HelloResultTests.cs index 25f33d73411..0d0ac28af79 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Connections/HelloResultTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Connections/HelloResultTests.cs @@ -17,7 +17,7 @@ using System.Net; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Compression; using MongoDB.Driver.Core.Misc; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Connections/TcpStreamFactoryTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Connections/TcpStreamFactoryTests.cs index 0e3f51642a0..014f1b6a523 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Connections/TcpStreamFactoryTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Connections/TcpStreamFactoryTests.cs @@ -23,7 +23,7 @@ using System.Threading.Tasks; using System.Reflection; using System.IO; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; using MongoDB.Bson.TestHelpers; @@ -137,7 +137,7 @@ public void CreateStream_should_throw_when_connect_timeout_has_expired( action.ShouldThrow(); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void CreateStream_should_call_the_socketConfigurator( [Values(false, true)] @@ -162,7 +162,7 @@ public void CreateStream_should_call_the_socketConfigurator( socketConfiguratorWasCalled.Should().BeTrue(); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void CreateStream_should_connect_to_a_running_server_and_return_a_non_null_stream( [Values(false, true)] @@ -185,7 +185,7 @@ public void CreateStream_should_connect_to_a_running_server_and_return_a_non_nul stream.Should().NotBeNull(); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void SocketConfigurator_can_be_used_to_set_keepAlive( [Values(false, true)] @@ -207,7 +207,7 @@ public void SocketConfigurator_can_be_used_to_set_keepAlive( stream = subject.CreateStream(endPoint, CancellationToken.None); } - var socketProperty = typeof(NetworkStream).GetProperty("Socket", BindingFlags.NonPublic | BindingFlags.Instance); + var socketProperty = typeof(NetworkStream).GetProperty("Socket", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); var socket = (Socket)socketProperty.GetValue(stream); var keepAlive = (int)socket.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive); keepAlive.Should().NotBe(0); // .NET returns 1 but Mono returns 8 @@ -222,14 +222,6 @@ public TestSocket(AddressFamily addressFamily, SocketType socketType, ProtocolTy { } - public TestSocket(SocketType socketType, ProtocolType protocolType) : base(socketType, protocolType) - { - } - - public TestSocket(SocketInformation socketInformation) : base(socketInformation) - { - } - protected override void Dispose(bool disposing) { base.Dispose(disposing); diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Events/EventPublisherTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Events/EventPublisherTests.cs new file mode 100644 index 00000000000..c54f41023d5 --- /dev/null +++ b/tests/MongoDB.Driver.Core.Tests/Core/Events/EventPublisherTests.cs @@ -0,0 +1,105 @@ +/* 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 FluentAssertions; +using MongoDB.TestHelpers.XunitExtensions; +using Moq; +using Xunit; + +namespace MongoDB.Driver.Core.Events +{ + public class EventPublisherTests + { + [Theory] + [MemberData(nameof(EventsData))] + internal void Publish_should_publish_event_for_registered_handlers(TEvent @event) where TEvent : struct, IEvent + { + var eventCapturer = new EventCapturer(); + eventCapturer.Capture(); + var eventPublisher = new EventPublisher(eventCapturer); + + eventPublisher.Publish(@event); + + eventCapturer.Events.Should().HaveCount(1); + eventCapturer.Events[0].Should().Be(@event); + } + + [Theory] + [MemberData(nameof(EventsData))] + internal void Publish_should_not_publish_event_for_not_registered_handlers(TEvent @event) where TEvent : struct, IEvent + { + var eventCapturer = new EventCapturer(); + eventCapturer.Capture(); + var eventPublisher = new EventPublisher(eventCapturer); + + eventPublisher.Publish(@event); + + eventCapturer.Events.Should().HaveCount(0); + } + + [Theory] + [ParameterAttributeData] + public void Publish_should_not_try_get_handler_twice([Values(false, true)] bool isHandlerRegistered) + { + var eventsSubscriber = new Mock(); + + bool handlerWasCalled = false; + Action eventHandler = e => { handlerWasCalled = true; }; + if (isHandlerRegistered) + { + eventsSubscriber + .Setup(s => s.TryGetEventHandler(out eventHandler)) + .Returns(true); + } + + var @event = new ClusterAddedServerEvent(); + var eventPublisher = new EventPublisher(eventsSubscriber.Object); + + eventPublisher.Publish(@event); + eventPublisher.Publish(@event); + + var anyDelegate = It.IsAny>(); + eventsSubscriber.Verify(s => s.TryGetEventHandler(out eventHandler), Times.Once()); + eventsSubscriber.Verify(s => s.TryGetEventHandler(out anyDelegate), Times.Once()); + + handlerWasCalled.Should().Be(isHandlerRegistered); + } + + [Theory] + [ParameterAttributeData] + public void IsEventTracked_should_return_correct_value([Values(false, true)] bool isEventTracked) + { + var eventsSubscriber = new Mock(); + if (isEventTracked) + { + Action eventHandler = e => { }; + eventsSubscriber + .Setup(s => s.TryGetEventHandler(out eventHandler)) + .Returns(true); + } + + var eventPublisher = new EventPublisher(eventsSubscriber.Object); + eventPublisher.IsEventTracked().Should().Be(isEventTracked); + } + + private static IEnumerable EventsData() + { + yield return new object[] { new ClusterAddedServerEvent(null, TimeSpan.FromSeconds(1)) }; + yield return new object[] { new ConnectionCreatedEvent(null, null, 1) }; + } + } +} diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Events/StructuredLogTemplateProvidersTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Events/StructuredLogTemplateProvidersTests.cs new file mode 100644 index 00000000000..3d3bdca9dd9 --- /dev/null +++ b/tests/MongoDB.Driver.Core.Tests/Core/Events/StructuredLogTemplateProvidersTests.cs @@ -0,0 +1,39 @@ +/* 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.Driver.Core.Logging; +using Xunit; + +namespace MongoDB.Driver.Core.Events +{ + public class StructuredLogTemplateProvidersTests + { + [Fact] + public void All_events_should_have_template() + { + foreach (var eventType in Enum.GetValues(typeof(EventType)).Cast()) + { + var template = StructuredLogTemplateProviders.GetTemplateProvider(eventType); + + template.Templates.Count().Should().BeGreaterThan(0); + template.Templates.First().Should().NotBeNull("Missing template for {0}", eventType); + template.ParametersExtractor.Should().NotBeNull("Missing template for {0}", eventType); + } + } + } +} diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Events/TraceSourceSdamEventSubscriberTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Events/TraceSourceSdamEventSubscriberTests.cs index f52e1c57698..3372ef1f5c5 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Events/TraceSourceSdamEventSubscriberTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Events/TraceSourceSdamEventSubscriberTests.cs @@ -13,6 +13,10 @@ * limitations under the License. */ +using System; +using System.Diagnostics; +using System.IO; +using System.Net; using FluentAssertions; using MongoDB.Bson.TestHelpers; using MongoDB.Driver.Core.Clusters; @@ -20,10 +24,6 @@ using MongoDB.Driver.Core.Connections; using MongoDB.Driver.Core.Events.Diagnostics; using MongoDB.Driver.Core.Servers; -using System; -using System.Diagnostics; -using System.IO; -using System.Net; using Xunit; namespace MongoDB.Driver.Core.Events @@ -217,7 +217,7 @@ public void Handle_with_SdamInformationEvent_should_trace_event() var traceSource = CreateTraceSource(logFileName, logFileName); var subject = new TraceSourceSdamEventSubscriber(traceSource); - subject.Handle(new SdamInformationEvent(() => expectedLogMessage)); + subject.Handle(new SdamInformationEvent(expectedLogMessage)); var log = ReadLog(traceSource, logFileName); log.Should().Contain(expectedLogMessage); diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Logging/EventLoggerTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Logging/EventLoggerTests.cs new file mode 100644 index 00000000000..de25568d2dd --- /dev/null +++ b/tests/MongoDB.Driver.Core.Tests/Core/Logging/EventLoggerTests.cs @@ -0,0 +1,109 @@ +/* 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.Net; +using FluentAssertions; +using Microsoft.Extensions.Logging; +using MongoDB.Driver.Core.Clusters; +using MongoDB.Driver.Core.Connections; +using MongoDB.Driver.Core.Events; +using MongoDB.Driver.Core.Servers; +using Moq; +using Xunit; + +namespace MongoDB.Driver.Core.Logging +{ + public class EventLoggerTests + { + [Theory] + [MemberData(nameof(EventsData))] + internal void LogAndPublish_should_log_and_publish( + TEventCategory eventCategory, + TEvent @event, + bool isHandlerRegistered, + bool isLoggingEnabled) + where TEventCategory : LogCategories.EventCategory + where TEvent : struct, IEvent + { + object eventCaptured = null; + Mock eventSubscriber = new Mock(); + Action eventHandler = e => eventCaptured = e; + if (isHandlerRegistered) + { + eventSubscriber + .Setup(s => s.TryGetEventHandler(out eventHandler)) + .Returns(true); + } + + Mock> logger = null; + + if (isLoggingEnabled) + { + logger = new Mock>(); + logger.Setup(l => l.IsEnabled(LogLevel.Debug)).Returns(true); + } + + var eventLogger = new EventLogger(eventSubscriber.Object, logger?.Object); + eventLogger.LogAndPublish(@event); + + eventSubscriber.Verify(s => s.TryGetEventHandler(out eventHandler), Times.Once); + + if (isHandlerRegistered) + { + eventCaptured.Should().Be(@event); + } + else + { + eventCaptured.Should().BeNull(); + } + + if (isLoggingEnabled) + { + logger.Verify(l => l.Log(LogLevel.Debug, It.IsAny(), It.IsNotNull(), null, It.IsNotNull>()), Times.Once); + } + + eventLogger.IsEventTracked().Should().Be(isLoggingEnabled || isHandlerRegistered); + } + + private static IEnumerable EventsData() + { + var clusterId = new ClusterId(1); + var endPoint = new DnsEndPoint("localhost", 27017); + var serverId = new ServerId(clusterId, endPoint); + var connectionId = new ConnectionId(serverId, 1); + var clusterDescription = new ClusterDescription(clusterId, false, default, default, default); + + var eventsData = new (object, object)[] + { + (new LogCategories.Command(), new CommandStartedEvent("test", new Bson.BsonDocument(), new DatabaseNamespace("test"), 1, 1, connectionId)), + (new LogCategories.Connection(), new ConnectionCreatedEvent(connectionId, null, 1)), + (new LogCategories.SDAM(), new ServerHeartbeatStartedEvent(connectionId, true)), + (new LogCategories.ServerSelection(), new ClusterSelectingServerEvent(clusterDescription, default, default)) + }; + + var booleanValues = new[] { true, false }; + + var result = from isHandlerRegistered in booleanValues + from isLoggingEnabled in booleanValues + from eventData in eventsData + select new object[] { eventData.Item1, eventData.Item2, isHandlerRegistered, isLoggingEnabled }; + + return result; + } + } +} diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Logging/LoggerFactoryCategoryDecoratorTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Logging/LoggerFactoryCategoryDecoratorTests.cs new file mode 100644 index 00000000000..c84d9b18ca3 --- /dev/null +++ b/tests/MongoDB.Driver.Core.Tests/Core/Logging/LoggerFactoryCategoryDecoratorTests.cs @@ -0,0 +1,53 @@ +/* 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 Microsoft.Extensions.Logging; +using MongoDB.Driver.Core.Configuration; +using Moq; +using Xunit; + +namespace MongoDB.Driver.Core.Logging +{ + public class LoggerFactoryCategoryDecoratorTests + { + [Theory] + [InlineData("MongoDB.Driver.Core.Logging.LogCategories.Command", "MongoDB.Command")] + [InlineData("MongoDB.Driver.Core.Logging.LogCategories.NewCategory", "MongoDB.NewCategory")] + [InlineData("MongoDB.Bson.SomeType", "MongoDB.Internal.SomeType")] + [InlineData("MongoDB.Bson.Namespace.SomeType", "MongoDB.Internal.SomeType")] + [InlineData("MongoDB.Driver.SomeType", "MongoDB.Internal.SomeType")] + [InlineData("MongoDB.Driver.Namespace.SomeType", "MongoDB.Internal.SomeType")] + [InlineData("MongoDB.Driver.Core.SomeType", "MongoDB.Internal.SomeType")] + [InlineData("MongoDB.Driver.Core.Namespace.SomeType", "MongoDB.Internal.SomeType")] + [InlineData("MongoDB.Bson.Tests.SomeType", "MongoDB.Tests.SomeType")] + [InlineData("MongoDB.Bson.Tests.Namespace.SomeType", "MongoDB.Tests.SomeType")] + [InlineData("MongoDB.Driver.Tests.Namespace.SomeType", "MongoDB.Tests.SomeType")] + [InlineData("MongoDB.Driver.Core.Tests.SomeType", "MongoDB.Tests.SomeType")] + [InlineData("MongoDB.Driver.Core.Tests.NameSpace.SomeType", "MongoDB.Tests.SomeType")] + [InlineData("MongoDB.Driver.Core.TestHelpers.SomeType", "MongoDB.Tests.SomeType")] + [InlineData("MongoDB.Driver.Core.TestHelpers.NameSpace.SomeType", "MongoDB.Tests.SomeType")] + [InlineData("MongoDB", "MongoDB")] + [InlineData("Random", "Random")] + internal void DecorateCategories_should_return_correct_category(string providedCategory, string expectedCatergory) + { + var underlyingFactory = new Mock(); + var loggingSettings = new LoggingSettings(underlyingFactory.Object); + var decoratedFactory = loggingSettings.ToInternalLoggerFactory(); + + decoratedFactory.CreateLogger(providedCategory); + underlyingFactory.Verify(f => f.CreateLogger(expectedCatergory), Times.Once); + } + } +} diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Misc/BatchableSourceTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Misc/BatchableSourceTests.cs index f12a391f800..68a51d4acb9 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Misc/BatchableSourceTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Misc/BatchableSourceTests.cs @@ -17,7 +17,7 @@ using System.Collections.Generic; using System.Linq; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.Core.Misc diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Misc/SemaphoreSlimSignalableTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Misc/SemaphoreSlimSignalableTests.cs index 7dff7723e8a..74397ba133b 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Misc/SemaphoreSlimSignalableTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Misc/SemaphoreSlimSignalableTests.cs @@ -19,7 +19,7 @@ using System.Threading.Tasks; using FluentAssertions; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.Core.Misc diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Misc/StreamExtensionMethodsTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Misc/StreamExtensionMethodsTests.cs index 7a507ef3ef9..bf17fa539c6 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Misc/StreamExtensionMethodsTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Misc/StreamExtensionMethodsTests.cs @@ -22,7 +22,7 @@ using System.Threading.Tasks; using FluentAssertions; using MongoDB.Bson.IO; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Moq; using Xunit; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Misc/WireVersionTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Misc/WireVersionTests.cs index 44bb51f9607..d381b78ce6b 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Misc/WireVersionTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Misc/WireVersionTests.cs @@ -23,7 +23,7 @@ namespace MongoDB.Driver.Core.Tests.Core.Misc { public class WireVersionTests { - [SkippableFact] + [Fact] public void Server_maxWireVersion_should_be_in_supported_range() { RequireServer.Check().StableServer(stable: true); @@ -32,7 +32,7 @@ public void Server_maxWireVersion_should_be_in_supported_range() var isOverlaped = WireVersion.SupportedWireVersionRange.Overlaps(new Range(serverMaxWireVersion, serverMaxWireVersion)); - isOverlaped.Should().BeTrue(); + isOverlaped.Should().BeTrue($"Server MaxWireVersion: {serverMaxWireVersion} is not in supported range for the driver: {WireVersion.SupportedWireVersionRange}"); } [Theory] @@ -46,7 +46,7 @@ public void GetServerVersionForErrorMessage_should_return_expected_serverVersion [Fact] public void SupportedWireRange_should_be_correct() { - WireVersion.SupportedWireVersionRange.Should().Be(new Range(6, 17)); + WireVersion.SupportedWireVersionRange.Should().Be(new Range(6, 19)); } [Fact] @@ -59,8 +59,9 @@ public void ToServerVersion_should_throw_if_wireVersion_less_than_0() [Theory] [InlineData(99, null, null)] - [InlineData(19, null, null)] - [InlineData(18, null, null)] + [InlineData(20, null, null)] + [InlineData(19, 6, 2)] + [InlineData(18, 6, 1)] [InlineData(17, 6, 0)] [InlineData(16, 5, 3)] [InlineData(15, 5, 2)] diff --git a/tests/MongoDB.Driver.Core.Tests/Core/NativeLibraryLoader/NativeLibraryLoaderTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/NativeLibraryLoader/NativeLibraryLoaderTests.cs deleted file mode 100644 index a267ee43f43..00000000000 --- a/tests/MongoDB.Driver.Core.Tests/Core/NativeLibraryLoader/NativeLibraryLoaderTests.cs +++ /dev/null @@ -1,104 +0,0 @@ -/* Copyright 2021-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.IO; -using FluentAssertions; -using MongoDB.Driver.Core.Misc; -using MongoDB.Driver.Core.NativeLibraryLoader; -using MongoDB.Driver.TestHelpers; -using Xunit; - -namespace MongoDB.Driver.Core.Tests.Core.NativeLibraryLoader -{ - public class NativeLibraryLoaderTests - { - [Fact] - public void GetLibraryBasePath_should_get_correct_paths_for_assembly_based_path() - { - var subject = new TestRelativeLibraryLocator(mockedAssemblyUri: null); - - var result = subject.GetBaseAssemblyDirectory(); - - // Ideally the root folder for expectedResult should be mongo-csharp-driver, - // but since it's not mocked logic it limits us where we can run our tests from. Avoid it by - // making a test assertation less straight - var expectedResult = GetCommonTestAssemblyFolderEnding(); - result.Should().EndWith(expectedResult); - } - - [Theory] - [InlineData("mongo-csharp-driver", "mongo-csharp-driver")] - [InlineData("mongo csharp driver", "mongo csharp driver")] - [InlineData("&mongo$csharp@driver%", "&mongo$csharp@driver%")] - public void GetLibraryBasePath_should_get_correct_paths_with_mocking(string rootTestFolder, string expectedRootTestFolder) - { - var assemblyCodeBase = Path.Combine( - RequirePlatform.GetCurrentOperatingSystem() == SupportedOperatingSystem.Windows ? "C:/" : @"\\data", - rootTestFolder, - GetCommonTestAssemblyFolderEnding(), - "MongoDB.Driver.Core.dll"); - var testAssemblyCodeBaseUri = new Uri(assemblyCodeBase).ToString(); - var subject = new TestRelativeLibraryLocator(mockedAssemblyUri: testAssemblyCodeBaseUri); - - var result = subject.GetBaseAssemblyDirectory(); - - var expectedResult = Path.Combine(expectedRootTestFolder, GetCommonTestAssemblyFolderEnding()); - result.Should().EndWith(expectedResult); - } - - // private methods - private string GetCommonTestAssemblyFolderEnding() => - Path.Combine( - "tests", - "MongoDB.Driver.Core.Tests", - "bin", - GetConfigurationName(), - GetTargetFrameworkMonikerName()); - - private string GetConfigurationName() => -#if DEBUG - "Debug"; -#else - "Release"; -#endif - - private string GetTargetFrameworkMonikerName() => -#if NETCOREAPP2_1 - "netcoreapp2.1"; -#elif NETCOREAPP3_1 - "netcoreapp3.1"; -#elif NET472 - "net472"; -#endif - - // nested types - private class TestRelativeLibraryLocator : RelativeLibraryLocatorBase - { - private readonly string _mockedAssemblyUri; - - public TestRelativeLibraryLocator(string mockedAssemblyUri) - { - _mockedAssemblyUri = mockedAssemblyUri; // can be null - } - - public override string LibraryName => "TestLibraryLocator"; - public override string GetBaseAssemblyUri() => _mockedAssemblyUri ?? base.GetBaseAssemblyUri(); - - // not required for these tests yet - public override string GetLibraryFileName(OperatingSystemPlatform currentPlatform) => throw new NotImplementedException(); - } - } -} diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/AggregateExplainOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/AggregateExplainOperationTests.cs index 104028d760b..5dd0be14c98 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/AggregateExplainOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/AggregateExplainOperationTests.cs @@ -17,7 +17,7 @@ using System.Threading; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers; @@ -162,7 +162,7 @@ public void CreateCommand_should_return_expected_result() result.Should().Be(expectedResult); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void CreateCommand_should_return_expected_result_when_AllowDiskUse_is_set( [Values(false, true)] @@ -185,7 +185,7 @@ public void CreateCommand_should_return_expected_result_when_AllowDiskUse_is_set result.Should().Be(expectedResult); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void CreateCommand_should_return_expected_result_when_Collation_is_set( [Values("en_US", "fr_CA")] @@ -283,7 +283,7 @@ public void CreateCommand_should_return_expected_result_when_MaxTime_is_set(long result["maxTimeMS"].BsonType.Should().Be(BsonType.Int32); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result( [Values(false, true)] @@ -297,7 +297,7 @@ public void Execute_should_return_expected_result( result.Should().NotBeNull(); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_AllowDiskUse_is_set( [Values(false, true)] @@ -314,7 +314,7 @@ public void Execute_should_return_expected_result_when_AllowDiskUse_is_set( result.Should().NotBeNull(); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_Collation_is_set( [Values(false, true)] @@ -331,7 +331,7 @@ public void Execute_should_return_expected_result_when_Collation_is_set( result.Should().NotBeNull(); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_throw_when_maxTime_is_exceeded( [Values(false, true)] bool async) @@ -348,7 +348,7 @@ public void Execute_should_throw_when_maxTime_is_exceeded( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_Comment_is_set( [Values(false, true)] @@ -372,7 +372,7 @@ public void Execute_should_return_expected_result_when_Comment_is_set( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_Hint_is_set( [Values(false, true)] @@ -389,7 +389,7 @@ public void Execute_should_return_expected_result_when_Hint_is_set( result.Should().NotBeNull(); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_MaxTime_is_set( [Values(false, true)] @@ -406,7 +406,7 @@ public void Execute_should_return_expected_result_when_MaxTime_is_set( result.Should().NotBeNull(); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_send_session_id_when_supported( [Values(false, true)] bool async) diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/AggregateOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/AggregateOperationTests.cs index f18aeb17565..d7c4a3273fb 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/AggregateOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/AggregateOperationTests.cs @@ -19,7 +19,7 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; using Xunit; @@ -566,7 +566,7 @@ public void CreateCommand_should_return_the_expected_result_when_using_causal_co result.Should().Be(expectedResult); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result( [Values(false, true)] @@ -605,7 +605,7 @@ public void Execute_should_throw_when_binding_is_null( argumentNullException.ParamName.Should().Be("binding"); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_throw_when_maxTime_is_exceeded( [Values(false, true)] bool async) @@ -639,7 +639,7 @@ public void Execute_should_throw_when_pipeline_ends_with_out_or_merge( argumentException.ParamName.Should().Be("pipeline"); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_AllowDiskUse_is_set( [Values(null, false, true)] @@ -661,7 +661,7 @@ public void Execute_should_return_expected_result_when_AllowDiskUse_is_set( result.Should().HaveCount(1); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_BatchSize_is_set( [Values(null, 1, 10)] @@ -685,7 +685,7 @@ public void Execute_should_return_expected_result_when_BatchSize_is_set( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_Collation_is_set( [Values(false, true)] @@ -708,7 +708,7 @@ public void Execute_should_return_expected_result_when_Collation_is_set( result.Should().HaveCount(caseSensitive ? 1 : 2); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_Comment_is_set( [Values(false, true)] @@ -735,7 +735,7 @@ public void Execute_should_return_expected_result_when_Comment_is_set( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_Hint_is_set( [Values(false, true)] @@ -754,7 +754,7 @@ public void Execute_should_return_expected_result_when_Hint_is_set( result.Should().NotBeNull(); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_Let_is_set_with_match_expression( [Values(false, true)] @@ -777,7 +777,7 @@ public void Execute_should_return_expected_result_when_Let_is_set_with_match_exp }); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_Let_is_set_with_project( [Values(false, true)] @@ -801,7 +801,7 @@ public void Execute_should_return_expected_result_when_Let_is_set_with_project( }); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_MaxAwaitTime_is_set( [Values(null, 1000)] @@ -825,7 +825,7 @@ public void Execute_should_return_expected_result_when_MaxAwaitTime_is_set( cursorMaxTime.Should().Be(maxAwaitTime); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_MaxTime_is_set( [Values(null, 1000)] @@ -848,7 +848,7 @@ public void Execute_should_return_expected_result_when_MaxTime_is_set( result.Should().HaveCount(1); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_ReadConcern_is_set( [Values(null, ReadConcernLevel.Local)] @@ -871,7 +871,7 @@ public void Execute_should_return_expected_result_when_ReadConcern_is_set( result.Should().HaveCount(1); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_UseCursor_is_set( [Values(null, false, true)] @@ -895,7 +895,7 @@ public void Execute_should_return_expected_result_when_UseCursor_is_set( result.Should().HaveCount(1); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_send_session_id_when_supported( [Values(false, true)] bool async) diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/AggregateToCollectionOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/AggregateToCollectionOperationTests.cs index a5e49047b0f..535243d003d 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/AggregateToCollectionOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/AggregateToCollectionOperationTests.cs @@ -19,7 +19,7 @@ using System.Net; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; @@ -550,7 +550,7 @@ public void CreateCommand_should_return_expected_result_when_WriteConcern_is_set result.Should().Be(expectedResult); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result( [Values("$out", "$merge")] string lastStageName, @@ -606,7 +606,7 @@ public void Execute_should_return_expected_result( result.Should().HaveCount(1); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_AllowDiskUse_is_set( [Values(null, false, true)] @@ -628,7 +628,7 @@ public void Execute_should_return_expected_result_when_AllowDiskUse_is_set( result.Should().HaveCount(1); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_BypassDocumentValidation_is_set( [Values(null, false, true)] @@ -650,7 +650,7 @@ public void Execute_should_return_expected_result_when_BypassDocumentValidation_ result.Should().HaveCount(1); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_Collation_is_set( [Values(false, true)] @@ -678,7 +678,7 @@ public void Execute_should_return_expected_result_when_Collation_is_set( result.Should().HaveCount(caseSensitive ? 1 : 2); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_throw_when_maxTime_is_exceeded( [Values(false, true)] bool async) @@ -702,7 +702,7 @@ public void Execute_should_throw_when_maxTime_is_exceeded( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_Comment_is_set( [Values(false, true)] @@ -728,7 +728,7 @@ public void Execute_should_return_expected_result_when_Comment_is_set( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_Hint_is_set( [Values(false, true)] @@ -747,7 +747,7 @@ public void Execute_should_return_expected_result_when_Hint_is_set( result.Should().NotBeNull(); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_Let_is_set_with_match_expression( [Values(false, true)] @@ -774,7 +774,7 @@ public void Execute_should_return_expected_result_when_Let_is_set_with_match_exp }); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_Let_is_set_with_project( [Values(false, true)] @@ -802,7 +802,7 @@ public void Execute_should_return_expected_result_when_Let_is_set_with_project( }); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_MaxTime_is_set( [Values(null, 1000)] @@ -825,7 +825,7 @@ public void Execute_should_return_expected_result_when_MaxTime_is_set( result.Should().HaveCount(1); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_throw_when_a_write_concern_error_occurs( [Values(false, true)] @@ -843,7 +843,7 @@ public void Execute_should_throw_when_a_write_concern_error_occurs( exception.Should().BeOfType(); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_send_session_id_when_supported( [Values(false, true)] bool async) diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/AsyncCursorSourceEnumerableAdapterTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/AsyncCursorSourceEnumerableAdapterTests.cs index 08325787bce..7cf809b3a0a 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/AsyncCursorSourceEnumerableAdapterTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/AsyncCursorSourceEnumerableAdapterTests.cs @@ -17,7 +17,7 @@ using System.Threading; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Moq; using Xunit; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/AsyncCursorTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/AsyncCursorTests.cs index 469a8f7c530..72b038c09e7 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/AsyncCursorTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/AsyncCursorTests.cs @@ -25,7 +25,7 @@ using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Connections; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/BulkDeleteOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/BulkDeleteOperationTests.cs index 0c5919d7744..86c6c110e9b 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/BulkDeleteOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/BulkDeleteOperationTests.cs @@ -17,7 +17,7 @@ using System.Collections.Generic; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Misc; using Xunit; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/BulkMixedWriteOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/BulkMixedWriteOperationTests.cs index b23a7e58713..22911a37c02 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/BulkMixedWriteOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/BulkMixedWriteOperationTests.cs @@ -20,7 +20,7 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Events; using MongoDB.Driver.Core.Misc; @@ -281,7 +281,7 @@ public void Execute_with_collation_should_throw_when_collation_is_not_supported( exception.Should().BeNull(); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_one_delete_and_let([Values(false, true)] bool async) { @@ -310,7 +310,7 @@ public void Execute_with_one_delete_and_let([Values(false, true)] bool async) list.Should().HaveCount(5); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_one_delete_against_a_matching_document( [Values(false, true)] @@ -338,7 +338,7 @@ public void Execute_with_one_delete_against_a_matching_document( list.Should().HaveCount(5); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_one_delete_against_a_matching_document_with_multi( [Values(false, true)] @@ -366,7 +366,7 @@ public void Execute_with_one_delete_against_a_matching_document_with_multi( list.Should().HaveCount(3); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_one_delete_without_matching_a_document( [Values(false, true)] @@ -394,7 +394,7 @@ public void Execute_with_one_delete_without_matching_a_document( list.Should().HaveCount(6); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_multiple_deletes( [Values(false, true)] @@ -426,7 +426,7 @@ public void Execute_with_multiple_deletes( list.Should().HaveCount(4); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_multiple_deletes_and_let([Values(false, true)] bool async) { @@ -463,7 +463,7 @@ public void Execute_with_multiple_deletes_and_let([Values(false, true)] bool asy list.Should().HaveCount(4); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_fewer_deletes_than_maxBatchCount( [Values(false, true)] @@ -499,7 +499,7 @@ public void Execute_with_fewer_deletes_than_maxBatchCount( list.Should().HaveCount(3); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_more_deletes_than_maxBatchCount( [Values(false, true)] @@ -535,7 +535,7 @@ public void Execute_with_more_deletes_than_maxBatchCount( list.Should().HaveCount(3); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_one_insert( [Values(false, true)] @@ -563,7 +563,7 @@ public void Execute_with_one_insert( list.Should().HaveCount(1); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_fewer_inserts_than_maxBatchCount( [Values(false, true)] @@ -598,7 +598,7 @@ public void Execute_with_fewer_inserts_than_maxBatchCount( list.Should().HaveCount(2); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_more_inserts_than_maxBatchCount( [Values(false, true)] @@ -635,7 +635,7 @@ public void Execute_with_more_inserts_than_maxBatchCount( list.Should().HaveCount(4); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_one_update_against_a_matching_document( [Values(false, true)] @@ -666,7 +666,7 @@ public void Execute_with_one_update_against_a_matching_document( list.Should().HaveCount(6); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_one_update_and_let( [Values(false, true)] bool async) @@ -703,7 +703,7 @@ public void Execute_with_one_update_and_let( list.Should().HaveCount(6); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_one_update_against_a_matching_document_with_multi( [Values(false, true)] @@ -731,7 +731,7 @@ public void Execute_with_one_update_against_a_matching_document_with_multi( list.Should().HaveCount(6); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_one_update_without_matching_a_document( [Values(false, true)] @@ -759,7 +759,7 @@ public void Execute_with_one_update_without_matching_a_document( list.Should().HaveCount(6); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_fewer_updates_than_maxBatchCount( [Values(false, true)] @@ -794,7 +794,7 @@ public void Execute_with_fewer_updates_than_maxBatchCount( list.Should().HaveCount(6); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_more_updates_than_maxBatchCount( [Values(false, true)] @@ -831,7 +831,7 @@ public void Execute_with_more_updates_than_maxBatchCount( list.Should().HaveCount(6); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_a_very_large_upsert( [Values(false, true)] @@ -865,7 +865,7 @@ public void Execute_with_a_very_large_upsert( list.Should().HaveCount(7); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_an_upsert_matching_multiple_documents( [Values(false, true)] @@ -896,7 +896,7 @@ public void Execute_with_an_upsert_matching_multiple_documents( list.Should().HaveCount(6); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_an_upsert_matching_no_documents( [Values(false, true)] @@ -927,7 +927,7 @@ public void Execute_with_an_upsert_matching_no_documents( list.Should().HaveCount(7); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_an_upsert_matching_one_document( [Values(false, true)] @@ -958,7 +958,7 @@ public void Execute_with_an_upsert_matching_one_document( list.Should().HaveCount(6); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_mixed_requests_and_ordered_is_false( [Values(false, true)] @@ -995,7 +995,7 @@ public void Execute_with_mixed_requests_and_ordered_is_false( list.Should().HaveCount(6); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_mixed_requests_and_let( [Values(false, true)] bool async) @@ -1041,7 +1041,7 @@ public void Execute_with_mixed_requests_and_let( list[4].Should().Be("{ _id : 6, x : 3 }"); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_mixed_requests_and_ordered_is_true( [Values(false, true)] @@ -1078,7 +1078,7 @@ public void Execute_with_mixed_requests_and_ordered_is_true( list.Should().HaveCount(6); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_mixed_upserts_and_ordered_is_false( [Values(false, true)] @@ -1115,7 +1115,7 @@ public void Execute_with_mixed_upserts_and_ordered_is_false( list.Should().HaveCount(8); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_mixed_upserts_and_ordered_is_true( [Values(false, true)] @@ -1152,7 +1152,7 @@ public void Execute_with_mixed_upserts_and_ordered_is_true( list.Should().HaveCount(8); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_an_error_in_the_first_batch_and_ordered_is_false( [Values(false, true)] @@ -1193,7 +1193,7 @@ public void Execute_with_an_error_in_the_first_batch_and_ordered_is_false( list.Should().HaveCount(3); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_an_error_in_the_first_batch_and_ordered_is_true( [Values(false, true)] @@ -1240,7 +1240,7 @@ public void Execute_with_an_error_in_the_first_batch_and_ordered_is_true( list.Should().HaveCount(1); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_an_error_in_the_second_batch_and_ordered_is_false( [Values(false, true)] @@ -1282,7 +1282,7 @@ public void Execute_with_an_error_in_the_second_batch_and_ordered_is_false( list.Should().HaveCount(4); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_an_error_in_the_second_batch_and_ordered_is_true( [Values(false, true)] @@ -1324,7 +1324,7 @@ public void Execute_with_an_error_in_the_second_batch_and_ordered_is_true( list.Should().HaveCount(3); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_unacknowledged_with_an_error_in_the_first_batch_and_ordered_is_false( [Values(false, true)] @@ -1365,7 +1365,7 @@ public void Execute_unacknowledged_with_an_error_in_the_first_batch_and_ordered_ } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_unacknowledged_with_an_error_in_the_first_batch_and_ordered_is_true( [Values(false, true)] @@ -1411,7 +1411,7 @@ public void Execute_unacknowledged_with_an_error_in_the_first_batch_and_ordered_ } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_unacknowledged_with_an_error_in_the_second_batch_and_ordered_is_false( [Values(false, true)] @@ -1452,7 +1452,7 @@ public void Execute_unacknowledged_with_an_error_in_the_second_batch_and_ordered } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_unacknowledged_with_an_error_in_the_second_batch_and_ordered_is_true( [Values(false, true)] @@ -1493,7 +1493,7 @@ public void Execute_unacknowledged_with_an_error_in_the_second_batch_and_ordered } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_delete_should_not_send_session_id_when_unacknowledged_writes( [Values(false, true)] bool retryRequested, @@ -1514,7 +1514,7 @@ public void Execute_with_delete_should_not_send_session_id_when_unacknowledged_w VerifySessionIdWasNotSentIfUnacknowledgedWrite(subject, "delete", async, useImplicitSession); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_delete_should_send_session_id_when_supported( [Values(false, true)] bool async) @@ -1527,7 +1527,7 @@ public void Execute_with_delete_should_send_session_id_when_supported( } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_insert_should_not_send_session_id_when_unacknowledged_writes( [Values(false, true)] bool retryRequested, @@ -1548,7 +1548,7 @@ public void Execute_with_insert_should_not_send_session_id_when_unacknowledged_w VerifySessionIdWasNotSentIfUnacknowledgedWrite(subject, "insert", async, useImplicitSession); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_insert_should_send_session_id_when_supported( [Values(false, true)] bool async) @@ -1561,7 +1561,7 @@ public void Execute_with_insert_should_send_session_id_when_supported( VerifySessionIdWasSentWhenSupported(subject, "insert", async); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_update_should_not_send_session_id_when_unacknowledged_writes( [Values(false, true)] bool retryRequested, @@ -1582,7 +1582,7 @@ public void Execute_with_update_should_not_send_session_id_when_unacknowledged_w VerifySessionIdWasNotSentIfUnacknowledgedWrite(subject, "update", async, useImplicitSession); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_update_should_send_session_id_when_supported( [Values(false, true)] bool async) @@ -1594,7 +1594,7 @@ public void Execute_with_update_should_send_session_id_when_supported( VerifySessionIdWasSentWhenSupported(subject, "update", async); } - [SkippableTheory] + [Theory] [InlineData(new[] { 1 }, new[] { 1 }, false)] [InlineData(new[] { 1, 1 }, new[] { 2 }, false)] [InlineData(new[] { 10000000, 10000000, 10000000, 10000000 }, new[] { 4 }, false)] @@ -1632,7 +1632,7 @@ public void Execute_with_multiple_deletes_should_split_batches_as_expected_when_ } } - [SkippableTheory] + [Theory] [InlineData(new[] { 1 }, new[] { 1 }, false)] [InlineData(new[] { 1, 1 }, new[] { 2 }, false)] [InlineData(new[] { 10000000, 10000000, 10000000, 10000000 }, new[] { 4 }, false)] @@ -1672,7 +1672,7 @@ public void Execute_with_multiple_inserts_should_split_batches_as_expected_when_ } } - [SkippableTheory] + [Theory] [InlineData(new[] { 1 }, new[] { 1 }, false)] [InlineData(new[] { 1, 1 }, new[] { 2 }, false)] [InlineData(new[] { 10000000, 10000000, 10000000, 10000000 }, new[] { 4 }, false)] diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/BulkUpdateOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/BulkUpdateOperationTests.cs index f8e03922e52..2f2e95fc935 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/BulkUpdateOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/BulkUpdateOperationTests.cs @@ -17,7 +17,7 @@ using System.Collections.Generic; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Misc; using Xunit; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/ChangeStreamCursorTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/ChangeStreamCursorTests.cs index 517f8a217a2..f2661693749 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/ChangeStreamCursorTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/ChangeStreamCursorTests.cs @@ -19,7 +19,7 @@ using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.TestHelpers; using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; @@ -272,7 +272,7 @@ public void GetResumeToken_should_return_expected_result( subject.GetResumeToken().Should().Be(expectedResult); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void GetResumeToken_should_return_expected_results_when_batch_is_empty_or_fully_iterated( [Values(false, true)] bool async, @@ -336,7 +336,7 @@ public void GetResumeToken_should_return_expected_results_when_batch_is_empty_or } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void GetResumeToken_should_return_expected_results_when_batch_is_not_empty_and_has_not_been_iterated( [Values(false, true)] bool async) diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/ChangeStreamOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/ChangeStreamOperationTests.cs index 68ac25aa2e1..b368fdea6a2 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/ChangeStreamOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/ChangeStreamOperationTests.cs @@ -22,7 +22,7 @@ using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Misc; @@ -375,7 +375,7 @@ public void RetryRequested_get_and_set_should_work( result.Should().Be(value); } - [SkippableTheory] + [Theory] [InlineData(null)] [InlineData("{ '_data' : 'testValue' }")] public void StartAfter_get_and_set_should_work(string startAfter) @@ -402,7 +402,7 @@ public void StartAtOperationTime_get_and_set_should_work(int? t, int? i) result.Should().Be(value); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_results_for_drop_collection( [Values(false, true)] bool async) @@ -434,7 +434,7 @@ public void Execute_should_return_expected_results_for_drop_collection( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_results_for_deletes( [Values(false, true)] bool async) @@ -466,7 +466,7 @@ public void Execute_should_return_expected_results_for_deletes( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_results_for_inserts( [Values(false, true)] bool async) @@ -499,7 +499,7 @@ public void Execute_should_return_expected_results_for_inserts( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_results_for_pre_post_images( [Values(false, true)] bool async, @@ -547,7 +547,7 @@ public void Execute_should_return_expected_results_for_pre_post_images( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_results_for_large_batch( [Values(1, 2, 3)] int numberOfChunks, @@ -591,7 +591,7 @@ public void Execute_should_return_expected_results_for_large_batch( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_results_for_updates( [Values(ChangeStreamFullDocumentOption.Default, ChangeStreamFullDocumentOption.UpdateLookup)] ChangeStreamFullDocumentOption fullDocument, diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/CommandOperationBaseTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/CommandOperationBaseTests.cs index d0546bcde94..9a0faaff797 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/CommandOperationBaseTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/CommandOperationBaseTests.cs @@ -20,7 +20,7 @@ using MongoDB.Bson.IO; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Servers; using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/CompositeWriteOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/CompositeWriteOperationTests.cs index 01fd85affd5..18c67618a5c 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/CompositeWriteOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/CompositeWriteOperationTests.cs @@ -19,7 +19,7 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Operations; using Moq; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/CountDocumentsOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/CountDocumentsOperationTests.cs index adfd1999a1f..50b575806c7 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/CountDocumentsOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/CountDocumentsOperationTests.cs @@ -19,7 +19,7 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers; @@ -195,7 +195,7 @@ public void Skip_get_and_set_should_work( result.Should().Be(value); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result( [Values(false, true)] @@ -210,7 +210,7 @@ public void Execute_should_return_expected_result( result.Should().Be(2); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_no_documents_match( [Values(false, true)] @@ -228,7 +228,7 @@ public void Execute_should_return_expected_result_when_no_documents_match( result.Should().Be(0); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_Collation_is_set( [Values(false, true)] @@ -249,7 +249,7 @@ public void Execute_should_return_expected_result_when_Collation_is_set( result.Should().Be(caseSensitive ? 1 : 2); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_throw_when_maxTime_is_exceeded( [Values(false, true)] bool async) @@ -265,7 +265,7 @@ public void Execute_should_throw_when_maxTime_is_exceeded( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_Filter_is_set( [Values(false, true)] @@ -283,7 +283,7 @@ public void Execute_should_return_expected_result_when_Filter_is_set( result.Should().Be(1); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_Hint_is_set( [Values(false, true)] @@ -301,7 +301,7 @@ public void Execute_should_return_expected_result_when_Hint_is_set( result.Should().Be(2); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_Limit_is_set( [Values(null, 1L, 2L)] @@ -321,7 +321,7 @@ public void Execute_should_return_expected_result_when_Limit_is_set( result.Should().Be(limit ?? 2); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_MaxTime_is_set( [Values(null, 1000L)] @@ -342,7 +342,7 @@ public void Execute_should_return_expected_result_when_MaxTime_is_set( result.Should().Be(2); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_ReadConcern_is_set( [Values(null, ReadConcernLevel.Local)] @@ -363,7 +363,7 @@ public void Execute_should_return_expected_result_when_ReadConcern_is_set( result.Should().Be(2); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_Skip_is_set( [Values(null, 1L, 2L)] @@ -383,7 +383,7 @@ public void Execute_should_return_expected_result_when_Skip_is_set( result.Should().Be(2 - (skip ?? 0)); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_send_session_id_when_supported( [Values(false, true)] bool async) diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/CountOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/CountOperationTests.cs index 47c6df019a1..07e21d944a7 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/CountOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/CountOperationTests.cs @@ -16,7 +16,7 @@ using System; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.TestHelpers; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; @@ -437,7 +437,7 @@ public void CreateCommand_should_return_expected_result_when_Skip_is_set( result.Should().Be(expectedResult); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result( [Values(false, true)] @@ -452,7 +452,7 @@ public void Execute_should_return_expected_result( result.Should().Be(2); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_Collation_is_set( [Values(false, true)] @@ -473,7 +473,7 @@ public void Execute_should_return_expected_result_when_Collation_is_set( result.Should().Be(caseSensitive ? 1 : 2); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_throw_when_maxTime_is_exceeded( [Values(false, true)] bool async) @@ -489,7 +489,7 @@ public void Execute_should_throw_when_maxTime_is_exceeded( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_Filter_is_set( [Values(false, true)] @@ -507,7 +507,7 @@ public void Execute_should_return_expected_result_when_Filter_is_set( result.Should().Be(1); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_Hint_is_set( [Values(false, true)] @@ -525,7 +525,7 @@ public void Execute_should_return_expected_result_when_Hint_is_set( result.Should().Be(2); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_Limit_is_set( [Values(null, 1L, 2L)] @@ -545,7 +545,7 @@ public void Execute_should_return_expected_result_when_Limit_is_set( result.Should().Be(limit ?? 2); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_MaxTime_is_set( [Values(null, 1000L)] @@ -566,7 +566,7 @@ public void Execute_should_return_expected_result_when_MaxTime_is_set( result.Should().Be(2); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_ReadConcern_is_set( [Values(null, ReadConcernLevel.Local)] @@ -587,7 +587,7 @@ public void Execute_should_return_expected_result_when_ReadConcern_is_set( result.Should().Be(2); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_Skip_is_set( [Values(null, 1L, 2L)] @@ -607,7 +607,7 @@ public void Execute_should_return_expected_result_when_Skip_is_set( result.Should().Be(2 - (skip ?? 0)); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_send_session_id_when_supported( [Values(false, true)] bool async) diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/CreateCollectionOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/CreateCollectionOperationTests.cs index 295c4fdc39f..e5fdf203572 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/CreateCollectionOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/CreateCollectionOperationTests.cs @@ -18,7 +18,7 @@ using System.Threading; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; @@ -594,7 +594,7 @@ string GetExpectedCollectionName(string[] array) } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_create_collection( [Values(false, true)] @@ -614,7 +614,7 @@ public void Execute_should_create_collection( info["name"].AsString.Should().Be(_collectionNamespace.CollectionName); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_create_collection_when_AutoIndexId_is_set( [Values(false, true)] @@ -641,7 +641,7 @@ public void Execute_should_create_collection_when_AutoIndexId_is_set( info["options"]["autoIndexId"].ToBoolean().Should().Be(autoIndexId); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_create_collection_when_Capped_is_set( [Values(false, true)] @@ -672,7 +672,7 @@ public void Execute_should_create_collection_when_Capped_is_set( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_create_collection_when_Collation_is_set( [Values(false, true)] @@ -695,7 +695,7 @@ public void Execute_should_create_collection_when_Collation_is_set( info["options"]["collation"]["locale"].AsString.Should().Be("en_US"); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_create_collection_when_IndexOptionDefaults_is_set( [Values(false, true)] @@ -723,7 +723,7 @@ public void Execute_should_create_collection_when_IndexOptionDefaults_is_set( info["options"]["indexOptionDefaults"].Should().Be(indexOptionDefaults); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_create_collection_when_MaxDocuments_is_set( [Values(1L, 2L)] @@ -750,7 +750,7 @@ public void Execute_should_create_collection_when_MaxDocuments_is_set( info["options"]["max"].ToInt64().Should().Be(maxDocuments); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_create_collection_when_MaxSize_is_set( [Values(10000L, 20000L)] @@ -776,7 +776,7 @@ public void Execute_should_create_collection_when_MaxSize_is_set( info["options"]["size"].ToInt64().Should().BeGreaterOrEqualTo(maxSize); // server rounds maxSize up } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_create_collection_when_NoPadding_is_set( [Values(false, true)] @@ -801,7 +801,7 @@ public void Execute_should_create_collection_when_NoPadding_is_set( info["options"]["flags"].Should().Be(noPadding ? 2 : 0); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_create_collection_when_StorageEngine_is_set( [Values("abc", "def")] @@ -830,7 +830,7 @@ public void Execute_should_create_collection_when_StorageEngine_is_set( info["options"]["storageEngine"].Should().Be(storageEngine); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_create_collection_when_UsePowerOf2Sizes_is_set( [Values(false, true)] @@ -855,7 +855,7 @@ public void Execute_should_create_collection_when_UsePowerOf2Sizes_is_set( info["options"]["flags"].Should().Be(usePowerOf2Sizes ? 1 : 0); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_create_collection_when_Validator_is_set( [Values(false, true)] @@ -883,7 +883,7 @@ public void Execute_should_create_collection_when_Validator_is_set( info["options"]["validationAction"].AsString.Should().Be("error"); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_throw_when_a_write_concern_error_occurs( [Values(false, true)] @@ -907,7 +907,7 @@ public void Execute_should_throw_when_a_write_concern_error_occurs( exception.Should().BeOfType(); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_send_session_id_when_supported( [Values(false, true)] bool async) diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/CreateIndexRequestTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/CreateIndexRequestTests.cs index 5714eb88d8e..94b72ed0e35 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/CreateIndexRequestTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/CreateIndexRequestTests.cs @@ -16,7 +16,7 @@ using System; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Misc; using Xunit; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/CreateIndexesOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/CreateIndexesOperationTests.cs index cf38de58d75..55e5f4fa61d 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/CreateIndexesOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/CreateIndexesOperationTests.cs @@ -18,7 +18,7 @@ using System.Linq; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers; @@ -69,7 +69,7 @@ public void constructor_should_throw_when_messageEncoderSettings_is_null() argumentNullException.ParamName.Should().Be("messageEncoderSettings"); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void CommitQuorum_get_and_set_should_work( [Values(null, 1, 2)] int? w) @@ -245,7 +245,7 @@ public void CreateCommand_should_throw_when_commitQuorum_is_specified_and_not_su exception.Should().BeOfType(); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_throw_when_maxTime_is_exceeded( [Values(false, true)] bool async) @@ -262,7 +262,7 @@ public void Execute_should_throw_when_maxTime_is_exceeded( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_work_when_background_is_true( [Values(false, true)] @@ -280,7 +280,7 @@ public void Execute_should_work_when_background_is_true( index["background"].ToBoolean().Should().BeTrue(); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_work_when_commitQuorum_is_specified( [Values(1, "majority", "votingMembers")] object commitQuorumCase, @@ -315,7 +315,7 @@ public void Execute_should_work_when_commitQuorum_is_specified( indexes.Select(index => index["name"].AsString).Should().BeEquivalentTo(new[] { "_id_", "x_1" }); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_work_when_creating_one_index( [Values(false, true)] @@ -332,7 +332,7 @@ public void Execute_should_work_when_creating_one_index( indexes.Select(index => index["name"].AsString).Should().BeEquivalentTo(new[] { "_id_", "x_1" }); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_work_when_creating_two_indexes( [Values(false, true)] @@ -353,7 +353,7 @@ public void Execute_should_work_when_creating_two_indexes( indexes.Select(index => index["name"].AsString).Should().BeEquivalentTo(new[] { "_id_", "x_1", "y_1" }); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_work_when_partialFilterExpression_has_value( [Values(false, true)] @@ -371,7 +371,7 @@ public void Execute_should_work_when_partialFilterExpression_has_value( index["partialFilterExpression"].AsBsonDocument.Should().Be(requests[0].PartialFilterExpression); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_work_when_sparse_is_true( [Values(false, true)] @@ -389,7 +389,7 @@ public void Execute_should_work_when_sparse_is_true( index["sparse"].ToBoolean().Should().BeTrue(); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_work_when_Collation_has_value( [Values("en_US", "fr_CA")] @@ -410,7 +410,7 @@ public void Execute_should_work_when_Collation_has_value( index["collation"]["locale"].AsString.Should().Be(locale); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_work_when_expireAfter_has_value( [Values(false, true)] @@ -429,7 +429,7 @@ public void Execute_should_work_when_expireAfter_has_value( index["expireAfterSeconds"].ToDouble().Should().Be(expireAfter.TotalSeconds); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_work_when_hidden_has_value( [Values(null, false, true)] bool? hidden, @@ -454,7 +454,7 @@ public void Execute_should_work_when_hidden_has_value( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_work_when_unique_is_true( [Values(false, true)] @@ -472,7 +472,7 @@ public void Execute_should_work_when_unique_is_true( index["unique"].ToBoolean().Should().BeTrue(); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_throw_when_a_write_concern_error_occurs( [Values(false, true)] @@ -491,7 +491,7 @@ public void Execute_should_throw_when_a_write_concern_error_occurs( exception.Should().BeOfType(); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_send_session_id_when_supported( [Values(false, true)] bool async) diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/CreateViewOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/CreateViewOperationTests.cs index a600ae0b3e2..ef72d20e5fa 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/CreateViewOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/CreateViewOperationTests.cs @@ -21,7 +21,7 @@ using System.Threading.Tasks; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Misc; @@ -128,7 +128,7 @@ public void WriteConcern_get_and_set_should_work( result.Should().BeSameAs(value); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_create_view( [Values("a", "b")] @@ -152,7 +152,7 @@ public void Execute_should_create_view( options["pipeline"].AsBsonArray.Cast().Should().Equal(_pipeline); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_create_view_when_Collation_is_set( [Values(null, "en_US")] @@ -188,7 +188,7 @@ public void Execute_should_create_view_when_Collation_is_set( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_create_view_when_WriteConcern_is_set( [Values("a", "b")] @@ -215,7 +215,7 @@ public void Execute_should_create_view_when_WriteConcern_is_set( options["pipeline"].AsBsonArray.Cast().Should().Equal(_pipeline); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_throw_when_a_write_concern_error_occurs( [Values(false, true)] @@ -233,7 +233,7 @@ public void Execute_should_throw_when_a_write_concern_error_occurs( exception.Should().BeOfType(); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_send_session_id_when_supported( [Values(false, true)] bool async) diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/DatabaseExistsOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/DatabaseExistsOperationTests.cs index 82c11374b98..9b57d46dd17 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/DatabaseExistsOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/DatabaseExistsOperationTests.cs @@ -17,7 +17,7 @@ using System.Threading.Tasks; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; using Xunit; @@ -58,7 +58,7 @@ public void Constructor_should_initialize_subject() subject.RetryRequested.Should().BeFalse(); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_true_when_database_exists( [Values(false, true)] @@ -81,7 +81,7 @@ public void Execute_should_return_true_when_database_exists( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_false_when_database_does_not_exist( [Values(false, true)] diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/DistinctOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/DistinctOperationTests.cs index 1c134276458..2771d516b83 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/DistinctOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/DistinctOperationTests.cs @@ -18,7 +18,7 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers; @@ -332,7 +332,7 @@ public void CreateCommand_should_return_the_expected_result_when_using_causal_co result.Should().Be(expectedResult); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result( [Values(false, true)] @@ -350,7 +350,7 @@ public void Execute_should_return_expected_result( result.Should().Contain(new[] { 1, 2, 3 }); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_Collation_is_set( [Values(false, true)] @@ -372,7 +372,7 @@ public void Execute_should_return_expected_result_when_Collation_is_set( result.Should().Contain(new[] { 2, 3 }); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_Filter_is_set( [Values(false, true)] @@ -393,7 +393,7 @@ public void Execute_should_return_expected_result_when_Filter_is_set( result.Should().Contain(new[] { 2, 3 }); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_MaxTime_is_set( [Values(false, true)] @@ -414,7 +414,7 @@ public void Execute_should_return_expected_result_when_MaxTime_is_set( result.Should().Contain(new[] { 1, 2, 3 }); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_ReadConcern_is_set( [Values(false, true)] @@ -435,7 +435,7 @@ public void Execute_should_return_expected_result_when_ReadConcern_is_set( result.Should().Contain(new[] { 1, 2, 3 }); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_throw_when_maxTime_is_exceeded( [Values(false, true)] bool async) @@ -451,7 +451,7 @@ public void Execute_should_throw_when_maxTime_is_exceeded( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_send_session_id_when_supported( [Values(false, true)] bool async) diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/DropCollectionOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/DropCollectionOperationTests.cs index 429b2dd375d..cde5d1f0f60 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/DropCollectionOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/DropCollectionOperationTests.cs @@ -17,7 +17,7 @@ using System.Linq; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Tests.Core.Operations; @@ -196,7 +196,7 @@ string GetExpectedCollectionName(string[] array) } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_not_throw_when_collection_does_not_exist( [Values(false, true)] @@ -209,7 +209,7 @@ public void Execute_should_not_throw_when_collection_does_not_exist( ExecuteOperation(subject, async); // this will throw if we have a problem... } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result( [Values(false, true)] @@ -228,7 +228,7 @@ public void Execute_should_return_expected_result( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_throw_when_a_write_concern_error_occurs( [Values(false, true)] @@ -246,7 +246,7 @@ public void Execute_should_throw_when_a_write_concern_error_occurs( exception.Should().BeOfType(); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_send_session_id_when_supported( [Values(false, true)] bool async) diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/DropDatabaseOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/DropDatabaseOperationTests.cs index 864a71781cf..7839d217a1e 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/DropDatabaseOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/DropDatabaseOperationTests.cs @@ -18,7 +18,7 @@ using System.Threading.Tasks; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Misc; @@ -86,7 +86,7 @@ public void CreateCommand_should_return_expected_result_when_WriteConcern_is_set result.Should().Be(expectedResult); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result( [Values(false, true)] @@ -118,7 +118,7 @@ public void Execute_should_throw_when_binding_is_null( action.ShouldThrow().And.ParamName.Should().Be("binding"); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_throw_when_a_write_concern_error_occurs( [Values(false, true)] @@ -141,7 +141,7 @@ public void Execute_should_throw_when_a_write_concern_error_occurs( exception.Should().BeOfType(); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_send_session_id_when_supported( [Values(false, true)] bool async) diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/DropIndexOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/DropIndexOperationTests.cs index 27c04d992b2..b2071cfa907 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/DropIndexOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/DropIndexOperationTests.cs @@ -19,7 +19,7 @@ using System.Threading.Tasks; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers; @@ -197,7 +197,7 @@ public void CreateCommand_should_return_expectedResult_when_WriteConcern_is_set( result.Should().Be(expectedResult); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_not_throw_when_collection_does_not_exist( [Values(false, true)] @@ -215,7 +215,7 @@ public void Execute_should_not_throw_when_collection_does_not_exist( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result( [Values(false, true)] @@ -246,7 +246,7 @@ public void Execute_should_throw_when_binding_is_null( ex.ParamName.Should().Be("binding"); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_throw_when_maxTime_is_exceeded( [Values(false, true)] bool async) @@ -263,7 +263,7 @@ public void Execute_should_throw_when_maxTime_is_exceeded( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_throw_when_a_write_concern_error_occurs( [Values(false, true)] @@ -282,7 +282,7 @@ public void Execute_should_throw_when_a_write_concern_error_occurs( exception.Should().BeOfType(); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_send_session_id_when_supported( [Values(false, true)] bool async) diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/EndTransactionOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/EndTransactionOperationTests.cs index 673ce134fe9..ab50605e5f9 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/EndTransactionOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/EndTransactionOperationTests.cs @@ -18,7 +18,7 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; using Xunit; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/EstimatedDocumentCountOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/EstimatedDocumentCountOperationTests.cs index ba47034319b..9208df77be6 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/EstimatedDocumentCountOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/EstimatedDocumentCountOperationTests.cs @@ -17,7 +17,7 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Connections; @@ -143,7 +143,7 @@ public void RetryRequested_get_and_set_should_work([Values(false, true)] bool va result.Should().Be(value); } - [SkippableFact] + [Fact] public void CreateCommand_should_return_expected_result() { var subject = new EstimatedDocumentCountOperation(_collectionNamespace, _messageEncoderSettings); @@ -217,7 +217,7 @@ public void CreateCommand_should_return_the_expected_result_when_using_causal_co AssertCommandDocument(result, readConcern: expectedReadConcernDocument); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result([Values(false, true)] bool async) { @@ -231,7 +231,7 @@ public void Execute_should_return_expected_result([Values(false, true)] bool asy result.Should().Be(2); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_throw_when_maxTime_is_exceeded([Values(false, true)] bool async) { @@ -247,7 +247,7 @@ public void Execute_should_throw_when_maxTime_is_exceeded([Values(false, true)] } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_MaxTime_is_set( [Values(null, 1000L)] long? milliseconds, @@ -266,7 +266,7 @@ public void Execute_should_return_expected_result_when_MaxTime_is_set( result.Should().Be(2); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_ReadConcern_is_set( [Values(null, ReadConcernLevel.Local)] ReadConcernLevel? level, @@ -286,7 +286,7 @@ public void Execute_should_return_expected_result_when_ReadConcern_is_set( result.Should().Be(2); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_send_session_id_when_supported([Values(false, true)] bool async) { diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/EvalOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/EvalOperationTests.cs index ba90a67780a..64080a96efa 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/EvalOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/EvalOperationTests.cs @@ -18,7 +18,7 @@ using System.Threading.Tasks; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Misc; @@ -160,7 +160,7 @@ public void CreateCommand_should_return_expected_result_when_noLock_is_provided( result.Should().Be(expectedResult); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result( [Values(false, true)] @@ -175,7 +175,7 @@ public void Execute_should_return_expected_result( result.Should().Be(1.0); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_args_are_provided( [Values(false, true)] @@ -200,7 +200,7 @@ public void Execute_should_return_expected_result_when_maxTime_is_provided( // TODO: implement EvalOperation MaxTime test } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_noLock_is_provided( [Values(false, true)] @@ -230,7 +230,7 @@ public void Execute_should_throw_when_binding_isNull( action.ShouldThrow(); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_throw_when_maxTime_is_exceeded( [Values(false, true)] bool async) diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/ExplainOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/ExplainOperationTests.cs index a93f1279d2f..3ca20744825 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/ExplainOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/ExplainOperationTests.cs @@ -19,7 +19,7 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Connections; @@ -108,7 +108,7 @@ public void CreateCommand_should_return_expected_result(ExplainVerbosity verbosi result.Should().Be(expectedResult); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_not_throw_when_collection_does_not_exist( [Values(false, true)] diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/FindOneAndDeleteOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/FindOneAndDeleteOperationTests.cs index 6c42624e79c..7e87965a88d 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/FindOneAndDeleteOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/FindOneAndDeleteOperationTests.cs @@ -18,7 +18,7 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers; @@ -132,7 +132,7 @@ public void CreateCommand_should_return_expected_result_when_Hint_is_set( result.Should().Be(expectedResult); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_hint_should_throw_when_hint_is_not_supported( [Values(0, 1)] int w, @@ -427,7 +427,7 @@ public void CreateCommand_should_return_the_expected_result_when_WriteConcern_is result.Should().Be(expectedResult); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_throw_when_maxTime_is_exceeded( [Values(false, true)] bool async) @@ -445,7 +445,7 @@ public void Execute_should_throw_when_maxTime_is_exceeded( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result( [Values(false, true)] @@ -462,7 +462,7 @@ public void Execute_should_return_expected_result( ReadAllFromCollection().Should().HaveCount(1); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_Collation_is_set( [Values(false, true)] @@ -483,7 +483,7 @@ public void Execute_should_return_expected_result_when_Collation_is_set( ReadAllFromCollection().Should().HaveCount(1); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_Let_is_set( [Values(false, true)] bool async) @@ -503,7 +503,7 @@ public void Execute_should_return_expected_result_when_Let_is_set( ReadAllFromCollection().Should().HaveCount(1); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_throw_when_there_is_a_write_concern_error( [Values(false, true)] bool async) @@ -526,7 +526,7 @@ public void Execute_should_throw_when_there_is_a_write_concern_error( ReadAllFromCollection().Should().HaveCount(1); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_when_document_does_not_exist( [Values(false, true)] diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/FindOneAndReplaceOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/FindOneAndReplaceOperationTests.cs index 7bf1169c745..4eb8534848e 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/FindOneAndReplaceOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/FindOneAndReplaceOperationTests.cs @@ -18,7 +18,7 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers; @@ -525,7 +525,7 @@ public void CreateCommand_should_return_expected_result_when_WriteConcern_is_set result.Should().Be(expectedResult); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_against_an_existing_document_returning_the_original( [Values(false, true)] @@ -547,7 +547,7 @@ public void Execute_against_an_existing_document_returning_the_original( ); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_against_an_existing_document_returning_the_replacement( [Values(false, true)] @@ -569,7 +569,7 @@ public void Execute_against_an_existing_document_returning_the_replacement( ); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_against_a_non_existing_document_returning_the_original( [Values(false, true)] @@ -592,7 +592,7 @@ public void Execute_against_a_non_existing_document_returning_the_original( ); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_against_a_non_existing_document_returning_the_replacement( [Values(false, true)] @@ -615,7 +615,7 @@ public void Execute_against_a_non_existing_document_returning_the_replacement( ); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_against_a_non_existing_document_returning_the_original_with_upsert( [Values(false, true)] @@ -641,7 +641,7 @@ public void Execute_against_a_non_existing_document_returning_the_original_with_ ); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_against_a_non_existing_document_returning_the_replacement_with_upsert( [Values(false, true)] @@ -667,7 +667,7 @@ public void Execute_against_a_non_existing_document_returning_the_replacement_wi ); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_throw_when_maxTime_is_exceeded( [Values(false, true)] bool async) @@ -685,7 +685,7 @@ public void Execute_should_throw_when_maxTime_is_exceeded( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_throw_when_there_is_a_write_concern_error( [Values(false, true)] @@ -710,7 +710,7 @@ public void Execute_should_throw_when_there_is_a_write_concern_error( ); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_hint_should_throw_when_hint_is_not_supported( [Values(0, 1)] int w, @@ -744,7 +744,7 @@ public void Execute_with_hint_should_throw_when_hint_is_not_supported( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_let_should_return_correct_results( [Values(false, true)] bool async) diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/FindOneAndUpdateOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/FindOneAndUpdateOperationTests.cs index e7185115867..715abe1a344 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/FindOneAndUpdateOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/FindOneAndUpdateOperationTests.cs @@ -18,7 +18,7 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers; @@ -519,7 +519,7 @@ public void CreateCommand_should_return_expected_result_when_WriteConcern_is_set result.Should().Be(expectedResult); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_against_an_existing_document_returning_the_original( [Values(false, true)] @@ -540,7 +540,7 @@ public void Execute_against_an_existing_document_returning_the_original( BsonDocument.Parse("{ _id : 11, x : 2, y : 'A' }")); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_against_an_existing_document_returning_the_updated( [Values(false, true)] @@ -561,7 +561,7 @@ public void Execute_against_an_existing_document_returning_the_updated( BsonDocument.Parse("{ _id : 11, x : 2, y : 'A' }")); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_against_a_non_existing_document_returning_the_original( [Values(false, true)] @@ -583,7 +583,7 @@ public void Execute_against_a_non_existing_document_returning_the_original( BsonDocument.Parse("{ _id : 11, x : 2, y : 'A' }")); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_against_a_non_existing_document_returning_the_updated( [Values(false, true)] @@ -605,7 +605,7 @@ public void Execute_against_a_non_existing_document_returning_the_updated( BsonDocument.Parse("{ _id : 11, x : 2, y : 'A' }")); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_against_a_non_existing_document_returning_the_original_with_upsert( [Values(false, true)] @@ -629,7 +629,7 @@ public void Execute_against_a_non_existing_document_returning_the_original_with_ BsonDocument.Parse("{ _id : 12, x : 0 }")); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_against_a_non_existing_document_returning_the_updated_with_upsert( [Values(false, true)] @@ -653,7 +653,7 @@ public void Execute_against_a_non_existing_document_returning_the_updated_with_u BsonDocument.Parse("{ _id : 12, x : 0 }")); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_Collation_is_set( [Values(false, true)] @@ -677,7 +677,7 @@ public void Execute_should_return_expected_result_when_Collation_is_set( BsonDocument.Parse("{ _id : 11, x : 0, y : 'A' }")); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_throw_when_maxTime_is_exceeded( [Values(false, true)] bool async) @@ -697,7 +697,7 @@ public void Execute_should_throw_when_maxTime_is_exceeded( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_throw_when_there_is_a_write_concern_error( [Values(false, true)] @@ -722,7 +722,7 @@ public void Execute_should_throw_when_there_is_a_write_concern_error( BsonDocument.Parse("{ _id : 11, x : 2, y : 'A' }")); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_with_hint_should_throw_when_hint_is_not_supported( [Values(0, 1)] int w, @@ -756,7 +756,7 @@ public void Execute_with_hint_should_throw_when_hint_is_not_supported( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_Let_is_set( [Values(false, true)] bool async) diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/FindOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/FindOperationTests.cs index 8370bb844ae..42a4d960b01 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/FindOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/FindOperationTests.cs @@ -19,7 +19,7 @@ using MongoDB.Bson.IO; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Servers; @@ -788,7 +788,7 @@ public void CreateCommand_should_return_the_expected_result_when_using_causal_co result.Should().Be(expectedResult); } - [SkippableFact] + [Fact] public void CreateCursor_should_use_ns_field_instead_of_namespace_passed_in_constructor() { var subject = new FindOperation(_collectionNamespace, BsonDocumentSerializer.Instance, _messageEncoderSettings); @@ -832,7 +832,7 @@ public void CursorType_get_and_set_should_work( result.Should().Be(value); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_find_all_the_documents_matching_the_query( [Values(false, true)] @@ -848,7 +848,7 @@ public void Execute_should_find_all_the_documents_matching_the_query( result.Should().HaveCount(5); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_find_all_the_documents_matching_the_query_when_split_across_batches( [Values(false, true)] @@ -869,7 +869,7 @@ public void Execute_should_find_all_the_documents_matching_the_query_when_split_ } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_find_documents_matching_options( [Values(false, true)] bool withLet, @@ -906,7 +906,7 @@ public void Execute_should_find_documents_matching_options( result.Should().HaveCount(1); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_Collation_is_set( [Values(false, true)] @@ -926,7 +926,7 @@ public void Execute_should_return_expected_result_when_Collation_is_set( result.Should().HaveCount(2); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_throw_when_binding_is_null( [Values(false, true)] @@ -940,7 +940,7 @@ public void Execute_should_throw_when_binding_is_null( argumentNullException.ParamName.Should().Be("binding"); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_throw_when_maxTime_is_exceeded( [Values(false, true)] bool async) @@ -956,7 +956,7 @@ public void Execute_should_throw_when_maxTime_is_exceeded( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_send_session_id_when_supported( [Values(false, true)] bool async) diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/GeoNearOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/GeoNearOperationTests.cs index b1b598c526b..341fa47e97b 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/GeoNearOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/GeoNearOperationTests.cs @@ -19,7 +19,7 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers; @@ -563,7 +563,7 @@ public void CreateCommand_should_return_the_expected_result_when_using_causal_co result.Should().Be(expectedResult); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result( [Values(false, true)] @@ -579,7 +579,7 @@ public void Execute_should_return_expected_result( result["results"].AsBsonArray.Select(i => i["dis"].ToDouble()).Should().BeInAscendingOrder(); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_Collation_is_set( [Values(false, true)] @@ -603,7 +603,7 @@ public void Execute_should_return_expected_result_when_Collation_is_set( result["results"].AsBsonArray.Select(i => i["dis"].ToDouble()).Should().BeInAscendingOrder(); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_send_session_id_when_supported( [Values(false, true)] bool async) @@ -615,7 +615,7 @@ public void Execute_should_send_session_id_when_supported( VerifySessionIdWasSentWhenSupported(subject, "geoNear", async); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_throw_when_maxTime_is_exceeded( [Values(false, true)] bool async) diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/GeoSearchOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/GeoSearchOperationTests.cs index a8eb1cc828c..bbc1ee3dec3 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/GeoSearchOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/GeoSearchOperationTests.cs @@ -18,7 +18,7 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers; @@ -159,7 +159,7 @@ public void CreateCommand_should_return_expected_result_when_MaxTime_is_set(long result["maxTimeMS"].BsonType.Should().Be(BsonType.Int32); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result( [Values(false, true)] @@ -182,7 +182,7 @@ public void Execute_should_return_expected_result( result["results"].Should().NotBeNull(); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_send_session_id_when_supported( [Values(false, true)] bool async) @@ -202,7 +202,7 @@ public void Execute_should_send_session_id_when_supported( VerifySessionIdWasSentWhenSupported(subject, "geoSearch", async); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_throw_when_maxTime_is_exceeded( [Values(false, true)] bool async) diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/GroupOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/GroupOperationTests.cs index 9290baaad15..4a7d7d4de0a 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/GroupOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/GroupOperationTests.cs @@ -18,7 +18,7 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers; @@ -361,7 +361,7 @@ public void CreateCommand_should_return_expected_result_when_MaxTime_is_set(long result["maxTimeMS"].BsonType.Should().Be(BsonType.Int32); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_key_is_used( [Values(false, true)] @@ -379,7 +379,7 @@ public void Execute_should_return_expected_result_when_key_is_used( BsonDocument.Parse("{ x : 3, count : 3 }")); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_keyFunction_is_used( [Values(false, true)] @@ -397,7 +397,7 @@ public void Execute_should_return_expected_result_when_keyFunction_is_used( BsonDocument.Parse("{ x : 3, count : 3 }")); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_Collation_is_set( [Values(false, true)] @@ -437,7 +437,7 @@ public void Execute_should_return_expected_result_when_Collation_is_set( result.Should().Equal(expectedResult); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_FinalizeFunction_is_set( [Values(false, true)] @@ -458,7 +458,7 @@ public void Execute_should_return_expected_result_when_FinalizeFunction_is_set( BsonDocument.Parse("{ x : 3, count : -3 }")); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_MaxTime_is_used( [Values(null, 1000)] @@ -483,7 +483,7 @@ public void Execute_should_return_expected_result_when_MaxTime_is_used( BsonDocument.Parse("{ x : 3, count : 3 }")); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_ResultSerializer_is_used( [Values(false, true)] @@ -502,7 +502,7 @@ public void Execute_should_return_expected_result_when_ResultSerializer_is_used( result.Should().Equal(1, 2, 3); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_throw_when_binding_is_null( [Values(false, true)] @@ -526,7 +526,7 @@ public void Execute_should_throw_when_binding_is_null( argumentNullException.ParamName.Should().Be("binding"); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_send_session_id_when_supported( [Values(false, true)] bool async) @@ -538,7 +538,7 @@ public void Execute_should_send_session_id_when_supported( VerifySessionIdWasSentWhenSupported(subject, "group", async); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_throw_when_maxTime_is_exceeded( [Values(false, true)] bool async) diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/IndexNameHelperTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/IndexNameHelperTests.cs index 9408e62064f..e1de914f99a 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/IndexNameHelperTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/IndexNameHelperTests.cs @@ -15,7 +15,7 @@ using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.Core.Operations diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/InsertOpcodeOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/InsertOpcodeOperationTests.cs index 56c66ed2bd9..6a1ecf438b3 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/InsertOpcodeOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/InsertOpcodeOperationTests.cs @@ -18,7 +18,7 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; using Xunit; @@ -142,7 +142,7 @@ public void WriteConcern_should_work() subject.WriteConcern.Should().Be(WriteConcern.W2); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_insert_a_single_document( [Values(false, true)] @@ -159,7 +159,7 @@ public void Execute_should_insert_a_single_document( list.Should().HaveCount(1); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_insert_multiple_documents( [Values(false, true)] diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/ListCollectionsOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/ListCollectionsOperationTests.cs index f0a8be4bb0c..aed03af9ff9 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/ListCollectionsOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/ListCollectionsOperationTests.cs @@ -19,7 +19,7 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; using Xunit; @@ -123,7 +123,7 @@ public void RetryRequested_get_and_set_should_work( result.Should().Be(value); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_the_expected_result( [Values(false, true)] @@ -141,7 +141,7 @@ public void Execute_should_return_the_expected_result( list.Select(c => c["name"].AsString).Where(n => n != "system.indexes").Should().BeEquivalentTo(expectedNames); } - [SkippableTheory] + [Theory] [InlineData("{ name : \"regular\" }", "regular", false)] [InlineData("{ name : \"regular\" }", "regular", true)] [InlineData("{ \"options.capped\" : true }", "capped", false)] @@ -163,7 +163,7 @@ public void Execute_should_return_the_expected_result_when_filter_is_used(string list[0]["name"].AsString.Should().Be(expectedName); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_the_expected_result_when_batchSize_is_used([Values(false, true)] bool async) { @@ -181,7 +181,7 @@ public void Execute_should_return_the_expected_result_when_batchSize_is_used([Va } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_the_expected_result_when_the_database_does_not_exist( [Values(false, true)] @@ -197,7 +197,7 @@ public void Execute_should_return_the_expected_result_when_the_database_does_not list.Should().HaveCount(0); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_send_session_id_when_supported( [Values(false, true)] bool async) diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/ListDatabasesOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/ListDatabasesOperationTests.cs index 2b426e332de..4767339e37e 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/ListDatabasesOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/ListDatabasesOperationTests.cs @@ -16,7 +16,7 @@ using System; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; @@ -128,7 +128,7 @@ public void CreateCommand_should_return_expected_result( result.Should().Be(expectedResult); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result( [Values(false, true)] bool async) @@ -143,7 +143,7 @@ public void Execute_should_return_expected_result( list.Should().Contain(x => x["name"] == _databaseNamespace.DatabaseName); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_the_expected_result_when_filter_is_used( [Values(false, true)] bool async) @@ -162,7 +162,7 @@ public void Execute_should_return_the_expected_result_when_filter_is_used( databases[0]["name"].AsString.Should().Be(_databaseNamespace.DatabaseName); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_the_expected_result_when_nameOnly_is_used( [Values(false, true)] bool nameOnly, diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/ListIndexesOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/ListIndexesOperationTests.cs index 1c9f6f2ad20..d90ec1d86c3 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/ListIndexesOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/ListIndexesOperationTests.cs @@ -17,7 +17,7 @@ using System.Linq; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; @@ -81,7 +81,7 @@ public void RetryRequested_get_should_return_expected_result( result.Should().Be(value); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result( [Values(false, true)] @@ -97,7 +97,7 @@ public void Execute_should_return_expected_result( list.Select(index => index["name"].AsString).Should().BeEquivalentTo("_id_"); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_the_expected_result_when_batchSize_is_used([Values(false, true)] bool async) { @@ -115,7 +115,7 @@ public void Execute_should_return_the_expected_result_when_batchSize_is_used([Va } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_collection_does_not_exist( [Values(false, true)] @@ -131,7 +131,7 @@ public void Execute_should_return_expected_result_when_collection_does_not_exist list.Count.Should().Be(0); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_database_does_not_exist( [Values(false, true)] @@ -161,7 +161,7 @@ public void Execute_should_throw_when_binding_is_null( action.ShouldThrow().And.ParamName.Should().Be("binding"); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_send_session_id_when_supported( [Values(false, true)] bool async) diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/ListIndexesUsingCommandOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/ListIndexesUsingCommandOperationTests.cs index 3c5650dbff3..48384169736 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/ListIndexesUsingCommandOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/ListIndexesUsingCommandOperationTests.cs @@ -17,7 +17,7 @@ using System.Linq; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; @@ -68,7 +68,7 @@ public void BatchSize_get_and_set_should_work() result.Should().Be(batchSize); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result( [Values(false, true)] @@ -84,7 +84,7 @@ public void Execute_should_return_expected_result( list.Select(index => index["name"].AsString).Should().BeEquivalentTo("_id_"); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_the_expected_result_when_batchSize_is_used([Values(false, true)] bool async) { @@ -102,7 +102,7 @@ public void Execute_should_return_the_expected_result_when_batchSize_is_used([Va } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_collection_does_not_exist( [Values(false, true)] @@ -118,7 +118,7 @@ public void Execute_should_return_expected_result_when_collection_does_not_exist list.Count.Should().Be(0); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_database_does_not_exist( [Values(false, true)] @@ -148,7 +148,7 @@ public void Execute_should_throw_when_binding_is_null( action.ShouldThrow().And.ParamName.Should().Be("binding"); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_send_session_id_when_supported( [Values(false, true)] bool async) diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/MapReduceLegacyOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/MapReduceLegacyOperationTests.cs index f07b034479a..8c29e308409 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/MapReduceLegacyOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/MapReduceLegacyOperationTests.cs @@ -20,7 +20,7 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; @@ -64,7 +64,7 @@ public void CreateOutputOptions_should_return_expected_result() result.Should().Be(expectedResult); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_results( [Values(false, true)] @@ -90,7 +90,7 @@ public void Execute_should_return_expected_results( results.Should().BeEquivalentTo(new BsonArray(expectedResults)); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_send_session_id_when_supported( [Values(false, true)] bool async) diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/MapReduceOperationBaseTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/MapReduceOperationBaseTests.cs index 5e4b6dd13ae..4213fdeffb4 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/MapReduceOperationBaseTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/MapReduceOperationBaseTests.cs @@ -16,7 +16,7 @@ using System; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; using Xunit; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/MapReduceOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/MapReduceOperationTests.cs index dfc8e0f7076..153bee61319 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/MapReduceOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/MapReduceOperationTests.cs @@ -19,7 +19,7 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Misc; @@ -138,7 +138,7 @@ public void CreateOutputOptions_should_return_expected_result() result.Should().Be("{ inline : 1 }"); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_results( [Values(false, true)] @@ -158,7 +158,7 @@ public void Execute_should_return_expected_results( BsonDocument.Parse("{ _id : 2, value : 4 }")); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_results_when_Collation_is_set( [Values(false, true)] @@ -201,7 +201,7 @@ public void Execute_should_return_expected_results_when_Collation_is_set( results.Should().BeEquivalentTo(expectedResults); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_results_when_Filter_is_set( [Values(false, true)] @@ -225,7 +225,7 @@ public void Execute_should_return_expected_results_when_Filter_is_set( BsonDocument.Parse("{ _id : 2, value : 4 }")); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_results_when_FinalizeFunction_is_set( [Values(false, true)] @@ -251,7 +251,7 @@ public void Execute_should_return_expected_results_when_FinalizeFunction_is_set( // TODO: figure out why test fails when JavaScriptMode = true (server bug?) - //[SkippableTheory] + //[Theory] //[ParameterAttributeData] //public void Execute_should_return_expected_results_when_JavaScriptMode_is_set( // [Values(null, false, true)] @@ -275,7 +275,7 @@ public void Execute_should_return_expected_results_when_FinalizeFunction_is_set( // BsonDocument.Parse("{ _id : 2, value : 4 }")); //} - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_results_when_Limit_is_set( [Values(1, 2)] @@ -302,7 +302,7 @@ public void Execute_should_return_expected_results_when_Limit_is_set( results.Should().Equal(expectedResults); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_results_when_MaxTime_is_set( [Values(null, 1000)] @@ -329,7 +329,7 @@ public void Execute_should_return_expected_results_when_MaxTime_is_set( BsonDocument.Parse("{ _id : 2, value : 4 }")); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_results_when_ReadConcern_is_set( [Values(null, ReadConcernLevel.Local)] // only use values that are valid on StandAlone servers @@ -356,7 +356,7 @@ public void Execute_should_return_expected_results_when_ReadConcern_is_set( BsonDocument.Parse("{ _id : 2, value : 4 }")); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_results_when_ResultSerializer_is_set( [Values(false, true)] @@ -376,7 +376,7 @@ public void Execute_should_return_expected_results_when_ResultSerializer_is_set( results.Should().Equal(3, 4); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_results_when_Scope_is_set( [Values(false, true)] @@ -402,7 +402,7 @@ public void Execute_should_return_expected_results_when_Scope_is_set( BsonDocument.Parse("{ _id : 2, value : 4 }")); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_results_when_Sort_is_set( [Values(1, -1)] @@ -459,7 +459,7 @@ public void Execute_should_throw_when_binding_is_null( argumentNullException.ParamName.Should().Be("binding"); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_throw_when_maxTime_is_exceeded( [Values(false, true)] bool async) @@ -478,7 +478,7 @@ public void Execute_should_throw_when_maxTime_is_exceeded( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_send_session_id_when_supported( [Values(false, true)] bool async) @@ -492,7 +492,7 @@ public void Execute_should_send_session_id_when_supported( VerifySessionIdWasSentWhenSupported(subject, "mapReduce", async); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void CreateCommand_should_return_expected_result_when_ReadConcern_is_set( [Values(null, ReadConcernLevel.Linearizable, ReadConcernLevel.Local)] @@ -521,7 +521,7 @@ public void CreateCommand_should_return_expected_result_when_ReadConcern_is_set( result.Should().Be(expectedResult); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void CreateCommand_should_return_the_expected_result_when_using_causal_consistency( [Values(null, ReadConcernLevel.Linearizable, ReadConcernLevel.Local)] diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/MapReduceOutputToCollectionOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/MapReduceOutputToCollectionOperationTests.cs index 2c72232cd52..571b743bee2 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/MapReduceOutputToCollectionOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/MapReduceOutputToCollectionOperationTests.cs @@ -17,7 +17,7 @@ using System.Reflection; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Connections; @@ -328,7 +328,7 @@ public void CreateOutputOptions_should_return_expected_result_when_NonAtomicOutp result.Should().Be(expectedResult); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result( [Values(false, true)] @@ -347,7 +347,7 @@ public void Execute_should_return_expected_result( BsonDocument.Parse("{ _id : 2, value : 4 }")); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_results_when_Collation_is_set( [Values(false, true)] @@ -389,7 +389,7 @@ public void Execute_should_return_expected_results_when_Collation_is_set( ReadAllFromCollection(_outputCollectionNamespace).Should().BeEquivalentTo(expectedResults); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_results_when_Filter_is_set( [Values(false, true)] @@ -412,7 +412,7 @@ public void Execute_should_return_expected_results_when_Filter_is_set( BsonDocument.Parse("{ _id : 2, value : 4 }")); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_results_when_FinalizeFunction_is_set( [Values(false, true)] @@ -437,7 +437,7 @@ public void Execute_should_return_expected_results_when_FinalizeFunction_is_set( // TODO: figure out why test fails when JavaScriptMode = true (server bug?) - //[SkippableTheory] + //[Theory] //[ParameterAttributeData] //public void Execute_should_return_expected_results_when_JavaScriptMode_is_set( // [Values(null, false, true)] @@ -460,7 +460,7 @@ public void Execute_should_return_expected_results_when_FinalizeFunction_is_set( // BsonDocument.Parse("{ _id : 2, value : 4 }")); //} - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_results_when_Limit_is_set( [Values(1, 2)] @@ -486,7 +486,7 @@ public void Execute_should_return_expected_results_when_Limit_is_set( ReadAllFromCollection(_outputCollectionNamespace).Should().Equal(expectedResults); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_results_when_MaxTime_is_set( [Values(null, 1000)] @@ -512,7 +512,7 @@ public void Execute_should_return_expected_results_when_MaxTime_is_set( BsonDocument.Parse("{ _id : 2, value : 4 }")); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_results_when_Scope_is_set( [Values(false, true)] @@ -537,7 +537,7 @@ public void Execute_should_return_expected_results_when_Scope_is_set( BsonDocument.Parse("{ _id : 2, value : 4 }")); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_results_when_Sort_is_set( [Values(1, -1)] @@ -593,7 +593,7 @@ public void Execute_should_throw_when_binding_is_null( argumentNullException.ParamName.Should().Be("binding"); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_throw_when_a_write_concern_error_occurs( [Values(false, true)] @@ -612,7 +612,7 @@ public void Execute_should_throw_when_a_write_concern_error_occurs( exception.Should().BeOfType(); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_send_session_id_when_supported( [Values(false, true)] bool async) diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/MaxTimeHelperTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/MaxTimeHelperTests.cs index 9583d341b80..9187c83d198 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/MaxTimeHelperTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/MaxTimeHelperTests.cs @@ -15,7 +15,7 @@ using System; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Misc; using Xunit; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/MongoBulkWriteOperationExceptionTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/MongoBulkWriteOperationExceptionTests.cs index ae3649d5b8c..50ef47ce3dd 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/MongoBulkWriteOperationExceptionTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/MongoBulkWriteOperationExceptionTests.cs @@ -69,9 +69,11 @@ public void Serialization_should_work() var formatter = new BinaryFormatter(); using (var stream = new MemoryStream()) { +#pragma warning disable SYSLIB0011 // BinaryFormatter serialization is obsolete formatter.Serialize(stream, subject); stream.Position = 0; var rehydrated = (MongoBulkWriteOperationException)formatter.Deserialize(stream); +#pragma warning restore SYSLIB0011 // BinaryFormatter serialization is obsolete rehydrated.ConnectionId.Should().Be(subject.ConnectionId); rehydrated.ErrorLabels.Should().BeEquivalentTo(subject.ErrorLabels); diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/ReIndexOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/ReIndexOperationTests.cs index fcfb046228d..0f508037d9d 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/ReIndexOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/ReIndexOperationTests.cs @@ -17,7 +17,7 @@ using System.Threading; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; @@ -71,7 +71,7 @@ public void WriteConcern_get_and_set_should_work( result.Should().BeSameAs(value); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result( [Values(false, true)] @@ -107,7 +107,7 @@ public void Execute_should_throw_when_binding_is_null( }); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_send_session_id_when_supported( [Values(false, true)] bool async) diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/ReadCommandOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/ReadCommandOperationTests.cs index 9ecb6ad267e..e66a2fa67d1 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/ReadCommandOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/ReadCommandOperationTests.cs @@ -22,7 +22,7 @@ using MongoDB.Bson.IO; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Servers; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/RenameCollectionOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/RenameCollectionOperationTests.cs index af0363bfff7..ce97884bfed 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/RenameCollectionOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/RenameCollectionOperationTests.cs @@ -18,7 +18,7 @@ using System.Threading.Tasks; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Misc; @@ -160,7 +160,7 @@ public void DropTarget_should_work( result.Should().Be(dropTarget); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result( [Values(false, true)] @@ -176,7 +176,7 @@ public void Execute_should_return_expected_result( result["ok"].ToBoolean().Should().BeTrue(); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_expected_result_when_dropTarget_is_true_and_newCollectionNamespace_exists( [Values(false, true)] @@ -195,7 +195,7 @@ public void Execute_should_return_expected_result_when_dropTarget_is_true_and_ne result["ok"].ToBoolean().Should().BeTrue(); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_throw_when_dropTarget_is_false_and_newCollectionNamespace_exists( [Values(false, true)] @@ -227,7 +227,7 @@ public void Execute_should_throw_when_binding_is_null( action.ShouldThrow().And.ParamName.Should().Be("binding"); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_throw_when_a_write_concern_error_occurs( [Values(false, true)] @@ -246,7 +246,7 @@ public void Execute_should_throw_when_a_write_concern_error_occurs( exception.Should().BeOfType(); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_send_session_id_when_supported( [Values(false, true)] bool async) diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/RetryabilityHelperTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/RetryabilityHelperTests.cs index 1ffea68d12a..779c694e354 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/RetryabilityHelperTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/RetryabilityHelperTests.cs @@ -17,7 +17,7 @@ using System.IO; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers; using Xunit; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/RetryableDeleteCommandOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/RetryableDeleteCommandOperationTests.cs index ce7babac785..e77602db114 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/RetryableDeleteCommandOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/RetryableDeleteCommandOperationTests.cs @@ -17,7 +17,7 @@ using System.Collections.Generic; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Misc; using Xunit; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/RetryableUpdateCommandOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/RetryableUpdateCommandOperationTests.cs index f8d07ec8498..44f0eb9ae20 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/RetryableUpdateCommandOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/RetryableUpdateCommandOperationTests.cs @@ -17,7 +17,7 @@ using System.Collections.Generic; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Misc; using Xunit; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/UpdateOpcodeOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/UpdateOpcodeOperationTests.cs index 27a82145366..150eaa56d90 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/UpdateOpcodeOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/UpdateOpcodeOperationTests.cs @@ -16,7 +16,7 @@ using System; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Misc; using Xunit; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Operations/WriteCommandOperationTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Operations/WriteCommandOperationTests.cs index 8cbb3a60b09..6da9926706f 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Operations/WriteCommandOperationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Operations/WriteCommandOperationTests.cs @@ -22,7 +22,7 @@ using MongoDB.Bson.IO; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Servers; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Servers/HeartbeatDelayTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Servers/HeartbeatDelayTests.cs index de8709375b7..8c2e9014e08 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Servers/HeartbeatDelayTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Servers/HeartbeatDelayTests.cs @@ -18,7 +18,7 @@ using System.Threading; using System.Threading.Tasks; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.Core.Servers diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Servers/LoadBalancedServerTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Servers/LoadBalancedServerTests.cs index 18f817a8df9..6a663956ebe 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Servers/LoadBalancedServerTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Servers/LoadBalancedServerTests.cs @@ -22,20 +22,23 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.ConnectionPools; using MongoDB.Driver.Core.Connections; using MongoDB.Driver.Core.Events; +using MongoDB.Driver.Core.Logging; using MongoDB.Driver.Core.TestHelpers; +using MongoDB.Driver.Core.TestHelpers.Logging; using Moq; using Xunit; +using Xunit.Abstractions; namespace MongoDB.Driver.Core.Servers { - public class LoadBalancedTests + public class LoadBalancedTests : LoggableTestClass { private IClusterClock _clusterClock; private ClusterId _clusterId; @@ -44,11 +47,12 @@ public class LoadBalancedTests private Mock _mockConnectionPoolFactory; private EndPoint _endPoint; private EventCapturer _capturedEvents; + private EventLogger _eventLogger; private ServerApi _serverApi; private ServerSettings _settings; private LoadBalancedServer _subject; - public LoadBalancedTests() + public LoadBalancedTests(ITestOutputHelper output) : base(output) { _clusterId = new ClusterId(); _endPoint = new DnsEndPoint("localhost", 27017); @@ -65,10 +69,11 @@ public LoadBalancedTests() .Returns(_mockConnectionPool.Object); _capturedEvents = new EventCapturer(); + _eventLogger = _capturedEvents.ToEventLogger(); _serverApi = new ServerApi(ServerApiVersion.V1, true, true); _settings = new ServerSettings(heartbeatInterval: Timeout.InfiniteTimeSpan); - _subject = new LoadBalancedServer(_clusterId, _clusterClock, _settings, _endPoint, _mockConnectionPoolFactory.Object, _capturedEvents, _serverApi); + _subject = new LoadBalancedServer(_clusterId, _clusterClock, _settings, _endPoint, _mockConnectionPoolFactory.Object, _serverApi, _eventLogger); _connectionId = new ConnectionId(_subject.ServerId); } @@ -97,13 +102,13 @@ public void ChannelFork_should_not_affect_operations_count([Values(false, true)] [Fact] public void Constructor_should_not_throw_when_serverApi_is_null() { - _ = new LoadBalancedServer(_clusterId, _clusterClock, _settings, _endPoint, _mockConnectionPoolFactory.Object, _capturedEvents, serverApi: null); + _ = new LoadBalancedServer(_clusterId, _clusterClock, _settings, _endPoint, _mockConnectionPoolFactory.Object, serverApi: null, _capturedEvents.ToEventLogger()); } [Fact] public void Constructor_should_throw_when_settings_is_null() { - var exception = Record.Exception(() => new LoadBalancedServer(_clusterId, _clusterClock, serverSettings: null, _endPoint, _mockConnectionPoolFactory.Object, _capturedEvents, _serverApi)); + var exception = Record.Exception(() => new LoadBalancedServer(_clusterId, _clusterClock, serverSettings: null, _endPoint, _mockConnectionPoolFactory.Object, _serverApi, _eventLogger)); exception.Should().BeOfType(); } @@ -111,7 +116,7 @@ public void Constructor_should_throw_when_settings_is_null() [Fact] public void Constructor_should_throw_when_clusterId_is_null() { - var exception = Record.Exception(() => new LoadBalancedServer(clusterId: null, _clusterClock, serverSettings: _settings, _endPoint, _mockConnectionPoolFactory.Object, _capturedEvents, _serverApi)); + var exception = Record.Exception(() => new LoadBalancedServer(clusterId: null, _clusterClock, serverSettings: _settings, _endPoint, _mockConnectionPoolFactory.Object, _serverApi, _eventLogger)); exception.Should().BeOfType(); } @@ -119,7 +124,7 @@ public void Constructor_should_throw_when_clusterId_is_null() [Fact] public void Constructor_should_throw_when_clusterClock_is_null() { - var exception = Record.Exception(() => new LoadBalancedServer(_clusterId, clusterClock: null, serverSettings: _settings, _endPoint, _mockConnectionPoolFactory.Object, _capturedEvents, _serverApi)); + var exception = Record.Exception(() => new LoadBalancedServer(_clusterId, clusterClock: null, serverSettings: _settings, _endPoint, _mockConnectionPoolFactory.Object, _serverApi, _eventLogger)); exception.Should().BeOfType(); } @@ -127,7 +132,7 @@ public void Constructor_should_throw_when_clusterClock_is_null() [Fact] public void Constructor_should_throw_when_endPoint_is_null() { - var exception = Record.Exception(() => new LoadBalancedServer(_clusterId, _clusterClock, serverSettings: _settings, endPoint: null, _mockConnectionPoolFactory.Object, _capturedEvents, _serverApi)); + var exception = Record.Exception(() => new LoadBalancedServer(_clusterId, _clusterClock, serverSettings: _settings, endPoint: null, _mockConnectionPoolFactory.Object, _serverApi, _eventLogger)); exception.Should().BeOfType(); } @@ -135,15 +140,15 @@ public void Constructor_should_throw_when_endPoint_is_null() [Fact] public void Constructor_should_throw_when_connectionPoolFactory_is_null() { - var exception = Record.Exception(() => new LoadBalancedServer(_clusterId, _clusterClock, serverSettings: _settings, _endPoint, connectionPoolFactory: null, _capturedEvents, _serverApi)); + var exception = Record.Exception(() => new LoadBalancedServer(_clusterId, _clusterClock, serverSettings: _settings, _endPoint, connectionPoolFactory: null, _serverApi, _eventLogger)); exception.Should().BeOfType(); } [Fact] - public void Constructor_should_throw_when_eventSubscriber_is_null() + public void Constructor_should_throw_when_eventLogger_is_null() { - var exception = Record.Exception(() => new LoadBalancedServer(_clusterId, _clusterClock, serverSettings: _settings, _endPoint, _mockConnectionPoolFactory.Object, eventSubscriber: null, _serverApi)); + var exception = Record.Exception(() => new LoadBalancedServer(_clusterId, _clusterClock, serverSettings: _settings, _endPoint, _mockConnectionPoolFactory.Object, _serverApi, eventLogger: null)); exception.Should().BeOfType(); } @@ -196,8 +201,8 @@ public void GetChannel_should_clear_connection_pool_when_opening_connection_thro _settings, _endPoint, mockConnectionPoolFactory.Object, - _capturedEvents, - _serverApi); + _serverApi, + _eventLogger); server.Initialize(); var exception = Record.Exception(() => @@ -339,21 +344,23 @@ public void GetChannel_should_not_update_topology_and_clear_connection_pool_on_M var openConnectionException = new MongoConnectionException(connectionId, "Oops", new IOException("Cry", innerMostException)); var mockConnection = new Mock(); + mockConnection.Setup(c => c.ConnectionId).Returns(connectionId); mockConnection.Setup(c => c.Open(It.IsAny())).Throws(openConnectionException); mockConnection.Setup(c => c.OpenAsync(It.IsAny())).ThrowsAsync(openConnectionException); var connectionFactory = new Mock(); connectionFactory.Setup(cf => cf.CreateConnection(serverId, _endPoint)).Returns(mockConnection.Object); + connectionFactory.Setup(cf => cf.ConnectionSettings).Returns(new ConnectionSettings()); var connectionPoolSettings = new ConnectionPoolSettings(); - var connectionPool = new ExclusiveConnectionPool(serverId, _endPoint, connectionPoolSettings, connectionFactory.Object, new EventAggregator(), mockConnectionExceptionHandler.Object); + var connectionPool = new ExclusiveConnectionPool(serverId, _endPoint, connectionPoolSettings, connectionFactory.Object, mockConnectionExceptionHandler.Object, CreateLogger().ToEventLogger(null)); var mockConnectionPoolFactory = new Mock(); mockConnectionPoolFactory .Setup(f => f.CreateConnectionPool(It.IsAny(), _endPoint, It.IsAny())) .Returns(connectionPool); - var subject = new LoadBalancedServer(_clusterId, _clusterClock, _settings, _endPoint, mockConnectionPoolFactory.Object, _capturedEvents, _serverApi); + var subject = new LoadBalancedServer(_clusterId, _clusterClock, _settings, _endPoint, mockConnectionPoolFactory.Object, _serverApi, _eventLogger); subject.Initialize(); IChannelHandle channel = null; @@ -466,8 +473,8 @@ private Server SetupServer(bool exceptionOnConnectionOpen, bool exceptionOnConne _settings, _endPoint, mockConnectionPoolFactory.Object, - _capturedEvents, - _serverApi); + _serverApi, + _eventLogger); server.Initialize(); return server; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Servers/RoundTripTimeMonitorTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Servers/RoundTripTimeMonitorTests.cs index 21a9df07396..a7f051d67a6 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Servers/RoundTripTimeMonitorTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Servers/RoundTripTimeMonitorTests.cs @@ -17,17 +17,17 @@ using System.Collections.Concurrent; using System.Net; using System.Threading; -using System.Threading.Tasks; using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters; +using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.Connections; using MongoDB.Driver.Core.Helpers; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; +using MongoDB.Driver.Core.TestHelpers; using MongoDB.Driver.Core.WireProtocol.Messages; using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; using Moq; @@ -43,7 +43,7 @@ public class RoundTripTimeMonitorTests [Fact] public void Constructor_should_throw_connection_endpoint_is_null() { - var exception = Record.Exception(() => new RoundTripTimeMonitor(Mock.Of(), __serverId, endpoint: null, TimeSpan.Zero, serverApi: null)); + var exception = Record.Exception(() => new RoundTripTimeMonitor(Mock.Of(), __serverId, endpoint: null, TimeSpan.Zero, serverApi: null, logger: null)); var e = exception.Should().BeOfType().Subject; e.ParamName.Should().Be("endpoint"); } @@ -51,7 +51,7 @@ public void Constructor_should_throw_connection_endpoint_is_null() [Fact] public void Constructor_should_throw_connection_factory_is_null() { - var exception = Record.Exception(() => new RoundTripTimeMonitor(connectionFactory: null, __serverId, __endPoint, TimeSpan.Zero, serverApi: null)); + var exception = Record.Exception(() => new RoundTripTimeMonitor(connectionFactory: null, __serverId, __endPoint, TimeSpan.Zero, serverApi: null, logger: null)); var e = exception.Should().BeOfType().Subject; e.ParamName.Should().Be("connectionFactory"); } @@ -59,7 +59,7 @@ public void Constructor_should_throw_connection_factory_is_null() [Fact] public void Constructor_should_throw_connection_serverId_is_null() { - var exception = Record.Exception(() => new RoundTripTimeMonitor(Mock.Of(), serverId: null, __endPoint, TimeSpan.Zero, serverApi: null)); + var exception = Record.Exception(() => new RoundTripTimeMonitor(Mock.Of(), serverId: null, __endPoint, TimeSpan.Zero, serverApi: null, logger: null)); var e = exception.Should().BeOfType().Subject; e.ParamName.Should().Be("serverId"); } @@ -96,6 +96,7 @@ public void Round_trip_time_monitor_should_work_as_expected() mockConnection, mockConnectionFactory); + mockConnectionFactory.Setup(f => f.ConnectionSettings).Returns(() => new ConnectionSettings()); mockConnectionFactory .Setup(f => f.CreateConnection(__serverId, __endPoint)) .Returns( @@ -164,6 +165,7 @@ public void Start_should_use_serverApi() var connection = new MockConnection(__serverId); var mockConnectionFactory = new Mock(); + mockConnectionFactory.Setup(f => f.ConnectionSettings).Returns(() => new ConnectionSettings()); mockConnectionFactory .Setup(x => x.CreateConnection(__serverId, __endPoint)) .Returns(connection); @@ -174,7 +176,8 @@ public void Start_should_use_serverApi() __serverId, __endPoint, TimeSpan.FromMilliseconds(10), - serverApi)) + serverApi, + logger: null)) { subject.Start(); @@ -217,7 +220,8 @@ private RoundTripTimeMonitor CreateSubject( __serverId, __endPoint, frequency, - serverApi: null); + serverApi: null, + logger: null); } private ConnectionDescription CreateConnectionDescription() @@ -239,6 +243,7 @@ private RoundTripTimeMonitor CreateSubject(TimeSpan frequency, Mock .Returns(() => CreateResponseMessage()); var mockConnectionFactory = new Mock(); + mockConnectionFactory.Setup(f => f.ConnectionSettings).Returns(() => new ConnectionSettings()); mockConnectionFactory .Setup(f => f.CreateConnection(__serverId, __endPoint)) .Returns(mockConnection.Object); diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Servers/ServerDescriptionTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Servers/ServerDescriptionTests.cs index c9146f177fd..8e925df61be 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Servers/ServerDescriptionTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Servers/ServerDescriptionTests.cs @@ -278,7 +278,9 @@ public void Equals_should_return_true_when_all_fields_are_equal() [InlineData(new[] { 14, 15 }, true)] [InlineData(new[] { 15, 16 }, true)] [InlineData(new[] { 16, 17 }, true)] - [InlineData(new[] { 18, 19 }, false)] + [InlineData(new[] { 18, 19 }, true)] + [InlineData(new[] { 19, 20 }, true)] + [InlineData(new[] { 20, 21 }, false)] public void IsCompatibleWithDriver_should_return_expected_result(int[] minMaxWireVersions, bool expectedResult) { var clusterId = new ClusterId(1); diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Servers/ServerFactoryTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Servers/ServerFactoryTests.cs index 94ed3fb69a0..65eaa13f10f 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Servers/ServerFactoryTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Servers/ServerFactoryTests.cs @@ -63,7 +63,7 @@ public ServerFactoryTests() [Fact] public void Constructor_should_not_throw_when_serverApi_is_null() { - Action act = () => new ServerFactory(_clusterConnectionMode, _connectionModeSwitch, _directConnection, _settings, _connectionPoolFactory, _serverMonitorFactory, _eventSubscriber, null); + Action act = () => new ServerFactory(_clusterConnectionMode, _connectionModeSwitch, _directConnection, _settings, _connectionPoolFactory, _serverMonitorFactory, _eventSubscriber, null, null); act.ShouldNotThrow(); } @@ -71,7 +71,7 @@ public void Constructor_should_not_throw_when_serverApi_is_null() [Fact] public void Constructor_should_throw_when_settings_is_null() { - Action act = () => new ServerFactory(_clusterConnectionMode, _connectionModeSwitch, _directConnection, null, _connectionPoolFactory, _serverMonitorFactory, _eventSubscriber, _serverApi); + Action act = () => new ServerFactory(_clusterConnectionMode, _connectionModeSwitch, _directConnection, null, _connectionPoolFactory, _serverMonitorFactory, _eventSubscriber, _serverApi, null); act.ShouldThrow(); } @@ -79,7 +79,7 @@ public void Constructor_should_throw_when_settings_is_null() [Fact] public void Constructor_should_throw_when_connectionPoolFactory_is_null() { - Action act = () => new ServerFactory(_clusterConnectionMode, _connectionModeSwitch, _directConnection, _settings, null, _serverMonitorFactory, _eventSubscriber, _serverApi); + Action act = () => new ServerFactory(_clusterConnectionMode, _connectionModeSwitch, _directConnection, _settings, null, _serverMonitorFactory, _eventSubscriber, _serverApi, null); act.ShouldThrow(); } @@ -87,7 +87,7 @@ public void Constructor_should_throw_when_connectionPoolFactory_is_null() [Fact] public void Constructor_should_throw_when_heartbeatConnectionFactory_is_null() { - Action act = () => new ServerFactory(_clusterConnectionMode, _connectionModeSwitch, _directConnection, _settings, _connectionPoolFactory, null, _eventSubscriber, _serverApi); + Action act = () => new ServerFactory(_clusterConnectionMode, _connectionModeSwitch, _directConnection, _settings, _connectionPoolFactory, null, _eventSubscriber, _serverApi, null); act.ShouldThrow(); } @@ -95,7 +95,7 @@ public void Constructor_should_throw_when_heartbeatConnectionFactory_is_null() [Fact] public void Constructor_should_throw_when_eventSubscriber_is_null() { - Action act = () => new ServerFactory(_clusterConnectionMode, _connectionModeSwitch, _directConnection, _settings, _connectionPoolFactory, _serverMonitorFactory, null, _serverApi); + Action act = () => new ServerFactory(_clusterConnectionMode, _connectionModeSwitch, _directConnection, _settings, _connectionPoolFactory, _serverMonitorFactory, null, _serverApi, null); act.ShouldThrow(); } @@ -103,7 +103,7 @@ public void Constructor_should_throw_when_eventSubscriber_is_null() [Fact] public void CreateServer_should_throw_if_clusterId_is_null() { - var subject = new ServerFactory(_clusterConnectionMode, _connectionModeSwitch, _directConnection, _settings, _connectionPoolFactory, _serverMonitorFactory, _eventSubscriber, _serverApi); + var subject = new ServerFactory(_clusterConnectionMode, _connectionModeSwitch, _directConnection, _settings, _connectionPoolFactory, _serverMonitorFactory, _eventSubscriber, _serverApi, null); var clusterClock = new Mock().Object; Action act = () => subject.CreateServer(ClusterType.Unknown, null, clusterClock, _endPoint); @@ -114,7 +114,7 @@ public void CreateServer_should_throw_if_clusterId_is_null() [Fact] public void CreateServer_should_throw_if_clusterClock_is_null() { - var subject = new ServerFactory(_clusterConnectionMode, _connectionModeSwitch, _directConnection, _settings, _connectionPoolFactory, _serverMonitorFactory, _eventSubscriber, _serverApi); + var subject = new ServerFactory(_clusterConnectionMode, _connectionModeSwitch, _directConnection, _settings, _connectionPoolFactory, _serverMonitorFactory, _eventSubscriber, _serverApi, null); var clusterId = new ClusterId(); Action act = () => subject.CreateServer(ClusterType.Unknown, clusterId, null, _endPoint); @@ -125,7 +125,7 @@ public void CreateServer_should_throw_if_clusterClock_is_null() [Fact] public void CreateServer_should_throw_if_endPoint_is_null() { - var subject = new ServerFactory(_clusterConnectionMode, _connectionModeSwitch, _directConnection, _settings, _connectionPoolFactory, _serverMonitorFactory, _eventSubscriber, _serverApi); + var subject = new ServerFactory(_clusterConnectionMode, _connectionModeSwitch, _directConnection, _settings, _connectionPoolFactory, _serverMonitorFactory, _eventSubscriber, _serverApi, null); var clusterClock = new Mock().Object; Action act = () => subject.CreateServer(ClusterType.Unknown, _clusterId, clusterClock, null); @@ -138,8 +138,9 @@ public void CreateServer_should_throw_if_endPoint_is_null() [InlineData(ClusterType.Unknown, typeof(DefaultServer))] public void CreateServer_should_return_correct_Server(ClusterType clusterType, Type expectedServerType) { - var subject = new ServerFactory(_clusterConnectionMode, _connectionModeSwitch, _directConnection, _settings, _connectionPoolFactory, _serverMonitorFactory, _eventSubscriber, _serverApi); + var subject = new ServerFactory(_clusterConnectionMode, _connectionModeSwitch, _directConnection, _settings, _connectionPoolFactory, _serverMonitorFactory, _eventSubscriber, _serverApi, null); var clusterClock = new Mock().Object; + var result = subject.CreateServer(clusterType, _clusterId, clusterClock, _endPoint); diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Servers/ServerIdTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Servers/ServerIdTests.cs index f5f4bdd6acf..7b719a6157c 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Servers/ServerIdTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Servers/ServerIdTests.cs @@ -86,9 +86,11 @@ public void Serialization_should_work() var formatter = new BinaryFormatter(); using (var stream = new MemoryStream()) { +#pragma warning disable SYSLIB0011 // BinaryFormatter serialization is obsolete formatter.Serialize(stream, subject); stream.Position = 0; var rehydrated = (ServerId)formatter.Deserialize(stream); +#pragma warning restore SYSLIB0011 // BinaryFormatter serialization is obsolete rehydrated.Should().Be(subject); rehydrated.ClusterId.Should().Be(subject.ClusterId); diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Servers/ServerMonitorTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Servers/ServerMonitorTests.cs index f0a100ae974..5d09da7c093 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Servers/ServerMonitorTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Servers/ServerMonitorTests.cs @@ -28,14 +28,16 @@ using MongoDB.Driver.Core.Helpers; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers; +using MongoDB.Driver.Core.TestHelpers.Logging; using MongoDB.Driver.Core.WireProtocol; using MongoDB.Driver.Core.WireProtocol.Messages; using Moq; using Xunit; +using Xunit.Abstractions; namespace MongoDB.Driver.Core.Servers { - public class ServerMonitorTests + public class ServerMonitorTests : LoggableTestClass { #region static private static readonly EndPoint __endPoint = new DnsEndPoint("localhost", 27017); @@ -43,10 +45,14 @@ public class ServerMonitorTests private static readonly ServerMonitorSettings __serverMonitorSettings = new ServerMonitorSettings(TimeSpan.FromSeconds(30), Timeout.InfiniteTimeSpan); #endregion + public ServerMonitorTests(ITestOutputHelper output) : base(output) + { + } + [Fact] public void Constructor_should_throw_when_serverId_is_null() { - Action act = () => new ServerMonitor(null, __endPoint, Mock.Of(), __serverMonitorSettings, new EventCapturer(), serverApi: null); + Action act = () => new ServerMonitor(null, __endPoint, Mock.Of(), __serverMonitorSettings, new EventCapturer(), serverApi: null, loggerFactory: null); act.ShouldThrow(); } @@ -54,7 +60,7 @@ public void Constructor_should_throw_when_serverId_is_null() [Fact] public void Constructor_should_throw_when_endPoint_is_null() { - Action act = () => new ServerMonitor(__serverId, null, Mock.Of(), __serverMonitorSettings, new EventCapturer(), serverApi: null); + Action act = () => new ServerMonitor(__serverId, null, Mock.Of(), __serverMonitorSettings, new EventCapturer(), serverApi: null, loggerFactory: null); act.ShouldThrow(); } @@ -62,7 +68,7 @@ public void Constructor_should_throw_when_endPoint_is_null() [Fact] public void Constructor_should_throw_when_connectionFactory_is_null() { - Action act = () => new ServerMonitor(__serverId, __endPoint, null, __serverMonitorSettings, new EventCapturer(), serverApi: null); + Action act = () => new ServerMonitor(__serverId, __endPoint, null, __serverMonitorSettings, new EventCapturer(), serverApi: null, loggerFactory: null); act.ShouldThrow(); } @@ -70,7 +76,7 @@ public void Constructor_should_throw_when_connectionFactory_is_null() [Fact] public void Constructor_should_throw_when_eventSubscriber_is_null() { - Action act = () => new ServerMonitor(__serverId, __endPoint, Mock.Of(), __serverMonitorSettings, eventSubscriber: null, serverApi: null); + Action act = () => new ServerMonitor(__serverId, __endPoint, Mock.Of(), __serverMonitorSettings, eventSubscriber: null, serverApi: null, loggerFactory: null); act.ShouldThrow(); } @@ -78,7 +84,7 @@ public void Constructor_should_throw_when_eventSubscriber_is_null() [Fact] public void Constructor_should_throw_when_roundTripTimeMonitor_is_null() { - var exception = Record.Exception(() => new ServerMonitor(__serverId, __endPoint, Mock.Of(), __serverMonitorSettings, new EventCapturer(), roundTripTimeMonitor: null, serverApi: null)); + var exception = Record.Exception(() => new ServerMonitor(__serverId, __endPoint, Mock.Of(), __serverMonitorSettings, new EventCapturer(), roundTripTimeMonitor: null, serverApi: null, loggerFactory: null)); exception.Should().BeOfType(); } @@ -86,7 +92,7 @@ public void Constructor_should_throw_when_roundTripTimeMonitor_is_null() [Fact] public void Constructor_should_throw_when_serverMonitorSettings_is_null() { - var exception = Record.Exception(() => new ServerMonitor(__serverId, __endPoint, Mock.Of(), serverMonitorSettings: null, new EventCapturer(), serverApi: null)); + var exception = Record.Exception(() => new ServerMonitor(__serverId, __endPoint, Mock.Of(), serverMonitorSettings: null, new EventCapturer(), serverApi: null, loggerFactory: null)); exception.Should().BeOfType(); } @@ -384,6 +390,7 @@ private ServerMonitor CreateSubject( connection = new MockConnection(); } mockConnectionFactory = new Mock(); + mockConnectionFactory.Setup(f => f.ConnectionSettings).Returns(() => new ConnectionSettings()); mockConnectionFactory .Setup(f => f.CreateConnection(__serverId, __endPoint)) .Returns(connection); @@ -395,7 +402,8 @@ private ServerMonitor CreateSubject( __serverMonitorSettings, eventCapturer ?? new EventCapturer(), mockRoundTripTimeMonitor.Object, - serverApi); + serverApi, + LoggerFactory); } private void SetupHeartbeatConnection(MockConnection connection, bool isStreamable = false, bool autoFillStreamingResponses = true) diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Servers/ServerTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Servers/ServerTests.cs index b5cee3090ce..73a1f2d8957 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Servers/ServerTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Servers/ServerTests.cs @@ -25,7 +25,7 @@ using MongoDB.Bson.IO; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Clusters.ServerSelectors; @@ -33,17 +33,20 @@ using MongoDB.Driver.Core.ConnectionPools; using MongoDB.Driver.Core.Connections; using MongoDB.Driver.Core.Events; +using MongoDB.Driver.Core.Logging; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers; +using MongoDB.Driver.Core.TestHelpers.Logging; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.WireProtocol; using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; using Moq; using Xunit; +using Xunit.Abstractions; namespace MongoDB.Driver.Core.Servers { - public class ServerTests + public class ServerTests : LoggableTestClass { private IClusterClock _clusterClock; private ClusterId _clusterId; @@ -57,13 +60,14 @@ public class ServerTests private Mock _mockConnectionPoolFactory; private EndPoint _endPoint; private EventCapturer _capturedEvents; + private EventLogger _eventLogger; private Mock _mockServerMonitor; private Mock _mockServerMonitorFactory; private ServerApi _serverApi; private ServerSettings _settings; private DefaultServer _subject; - public ServerTests() + public ServerTests(ITestOutputHelper output) : base(output) { _clusterId = new ClusterId(); _endPoint = new DnsEndPoint("localhost", 27017); @@ -88,13 +92,19 @@ public ServerTests() _mockServerMonitorFactory.Setup(f => f.Create(It.IsAny(), _endPoint)).Returns(_mockServerMonitor.Object); _capturedEvents = new EventCapturer(); + _eventLogger = CreateLogger().ToEventLogger(_capturedEvents); _serverApi = new ServerApi(ServerApiVersion.V1, true, true); _settings = new ServerSettings(heartbeatInterval: Timeout.InfiniteTimeSpan); - _subject = new DefaultServer(_clusterId, _clusterClock, _clusterConnectionMode, _connectionModeSwitch, _directConnection, _settings, _endPoint, _mockConnectionPoolFactory.Object, _mockServerMonitorFactory.Object, _capturedEvents, _serverApi); + _subject = new DefaultServer(_clusterId, _clusterClock, _clusterConnectionMode, _connectionModeSwitch, _directConnection, _settings, _endPoint, _mockConnectionPoolFactory.Object, _mockServerMonitorFactory.Object, _serverApi, _eventLogger); _connectionId = new ConnectionId(_subject.ServerId); } + protected override void DisposeInternal() + { + _subject.Dispose(); + } + [Theory] [ParameterAttributeData] public void ChannelFork_should_not_affect_operations_count([Values(false, true)] bool async) @@ -120,7 +130,7 @@ public void ChannelFork_should_not_affect_operations_count([Values(false, true)] [Fact] public void Constructor_should_not_throw_when_serverApi_is_null() { - Action act = () => new DefaultServer(_clusterId, _clusterClock, _clusterConnectionMode, _connectionModeSwitch, _directConnection, _settings, _endPoint, _mockConnectionPoolFactory.Object, _mockServerMonitorFactory.Object, _capturedEvents, null); + Action act = () => new DefaultServer(_clusterId, _clusterClock, _clusterConnectionMode, _connectionModeSwitch, _directConnection, _settings, _endPoint, _mockConnectionPoolFactory.Object, _mockServerMonitorFactory.Object, null, _eventLogger); act.ShouldNotThrow(); } @@ -128,7 +138,7 @@ public void Constructor_should_not_throw_when_serverApi_is_null() [Fact] public void Constructor_should_throw_when_settings_is_null() { - Action act = () => new DefaultServer(_clusterId, _clusterClock, _clusterConnectionMode, _connectionModeSwitch, _directConnection, null, _endPoint, _mockConnectionPoolFactory.Object, _mockServerMonitorFactory.Object, _capturedEvents, _serverApi); + Action act = () => new DefaultServer(_clusterId, _clusterClock, _clusterConnectionMode, _connectionModeSwitch, _directConnection, null, _endPoint, _mockConnectionPoolFactory.Object, _mockServerMonitorFactory.Object, _serverApi, _eventLogger); act.ShouldThrow(); } @@ -136,7 +146,7 @@ public void Constructor_should_throw_when_settings_is_null() [Fact] public void Constructor_should_throw_when_clusterId_is_null() { - Action act = () => new DefaultServer(null, _clusterClock, _clusterConnectionMode, _connectionModeSwitch, _directConnection, _settings, _endPoint, _mockConnectionPoolFactory.Object, _mockServerMonitorFactory.Object, _capturedEvents, _serverApi); + Action act = () => new DefaultServer(null, _clusterClock, _clusterConnectionMode, _connectionModeSwitch, _directConnection, _settings, _endPoint, _mockConnectionPoolFactory.Object, _mockServerMonitorFactory.Object, _serverApi, _eventLogger); act.ShouldThrow(); } @@ -144,7 +154,7 @@ public void Constructor_should_throw_when_clusterId_is_null() [Fact] public void Constructor_should_throw_when_clusterClock_is_null() { - Action act = () => new DefaultServer(_clusterId, null, _clusterConnectionMode, _connectionModeSwitch, _directConnection, _settings, _endPoint, _mockConnectionPoolFactory.Object, _mockServerMonitorFactory.Object, _capturedEvents, _serverApi); + Action act = () => new DefaultServer(_clusterId, null, _clusterConnectionMode, _connectionModeSwitch, _directConnection, _settings, _endPoint, _mockConnectionPoolFactory.Object, _mockServerMonitorFactory.Object, _serverApi, _eventLogger); act.ShouldThrow(); } @@ -152,7 +162,7 @@ public void Constructor_should_throw_when_clusterClock_is_null() [Fact] public void Constructor_should_throw_when_endPoint_is_null() { - Action act = () => new DefaultServer(_clusterId, _clusterClock, _clusterConnectionMode, _connectionModeSwitch, _directConnection, _settings, null, _mockConnectionPoolFactory.Object, _mockServerMonitorFactory.Object, _capturedEvents, _serverApi); + Action act = () => new DefaultServer(_clusterId, _clusterClock, _clusterConnectionMode, _connectionModeSwitch, _directConnection, _settings, null, _mockConnectionPoolFactory.Object, _mockServerMonitorFactory.Object, _serverApi, _eventLogger); act.ShouldThrow(); } @@ -160,7 +170,7 @@ public void Constructor_should_throw_when_endPoint_is_null() [Fact] public void Constructor_should_throw_when_connectionPoolFactory_is_null() { - Action act = () => new DefaultServer(_clusterId, _clusterClock, _clusterConnectionMode, _connectionModeSwitch, _directConnection, _settings, _endPoint, null, _mockServerMonitorFactory.Object, _capturedEvents, _serverApi); + Action act = () => new DefaultServer(_clusterId, _clusterClock, _clusterConnectionMode, _connectionModeSwitch, _directConnection, _settings, _endPoint, null, _mockServerMonitorFactory.Object, _serverApi, _eventLogger); act.ShouldThrow(); } @@ -168,15 +178,15 @@ public void Constructor_should_throw_when_connectionPoolFactory_is_null() [Fact] public void Constructor_should_throw_when_serverMonitorFactory_is_null() { - Action act = () => new DefaultServer(_clusterId, _clusterClock, _clusterConnectionMode, _connectionModeSwitch, _directConnection, _settings, _endPoint, _mockConnectionPoolFactory.Object, null, _capturedEvents, _serverApi); + Action act = () => new DefaultServer(_clusterId, _clusterClock, _clusterConnectionMode, _connectionModeSwitch, _directConnection, _settings, _endPoint, _mockConnectionPoolFactory.Object, null, _serverApi, _eventLogger); act.ShouldThrow(); } [Fact] - public void Constructor_should_throw_when_eventSubscriber_is_null() + public void Constructor_should_throw_when_eventLogger_is_null() { - Action act = () => new DefaultServer(_clusterId, _clusterClock, _clusterConnectionMode, _connectionModeSwitch, _directConnection, _settings, _endPoint, _mockConnectionPoolFactory.Object, _mockServerMonitorFactory.Object, null, _serverApi); + Action act = () => new DefaultServer(_clusterId, _clusterClock, _clusterConnectionMode, _connectionModeSwitch, _directConnection, _settings, _endPoint, _mockConnectionPoolFactory.Object, _mockServerMonitorFactory.Object, _serverApi, null); act.ShouldThrow(); } @@ -221,8 +231,8 @@ public void GetChannel_should_clear_connection_pool_when_opening_connection_thro _endPoint, mockConnectionPoolFactory.Object, _mockServerMonitorFactory.Object, - _capturedEvents, - _serverApi); + _serverApi, + _eventLogger); var exceptionToThrow = new MongoAuthenticationException(connectionId, "Invalid login."); mockConnectionPool @@ -378,15 +388,17 @@ public void GetChannel_should_update_topology_and_clear_connection_pool_on_netwo var openConnectionException = new MongoConnectionException(connectionId, "Oops", new IOException("Cry", innerMostException)); var mockConnection = new Mock(); + mockConnection.Setup(c => c.ConnectionId).Returns(connectionId); mockConnection.Setup(c => c.Open(It.IsAny())).Throws(openConnectionException); mockConnection.Setup(c => c.OpenAsync(It.IsAny())).ThrowsAsync(openConnectionException); var connectionFactory = new Mock(); + connectionFactory.Setup(f => f.ConnectionSettings).Returns(() => new ConnectionSettings()); connectionFactory.Setup(cf => cf.CreateConnection(serverId, _endPoint)).Returns(mockConnection.Object); var mockExceptionHandler = new Mock(); var connectionPoolSettings = new ConnectionPoolSettings(); - var connectionPool = new ExclusiveConnectionPool(serverId, _endPoint, connectionPoolSettings, connectionFactory.Object, new EventAggregator(), mockExceptionHandler.Object); + var connectionPool = new ExclusiveConnectionPool(serverId, _endPoint, connectionPoolSettings, connectionFactory.Object, mockExceptionHandler.Object, CreateLogger().ToEventLogger(null)); var mockConnectionPoolFactory = new Mock(); mockConnectionPoolFactory @@ -399,7 +411,7 @@ public void GetChannel_should_update_topology_and_clear_connection_pool_on_netwo var mockServerMonitorFactory = new Mock(); mockServerMonitorFactory.Setup(f => f.Create(It.IsAny(), _endPoint)).Returns(mockServerMonitor.Object); - var subject = new DefaultServer(_clusterId, _clusterClock, _clusterConnectionMode, _connectionModeSwitch, _directConnection, _settings, _endPoint, mockConnectionPoolFactory.Object, mockServerMonitorFactory.Object, _capturedEvents, _serverApi); + var subject = new DefaultServer(_clusterId, _clusterClock, _clusterConnectionMode, _connectionModeSwitch, _directConnection, _settings, _endPoint, mockConnectionPoolFactory.Object, mockServerMonitorFactory.Object, _serverApi, _eventLogger); connectionPool._connectionExceptionHandler(subject); subject.Initialize(); connectionPool.SetReady(); @@ -459,7 +471,7 @@ public void HandleChannelException_should_update_topology_as_expected_on_network mockServerMonitor.SetupGet(m => m.Lock).Returns(new object()); var mockServerMonitorFactory = new Mock(); mockServerMonitorFactory.Setup(f => f.Create(It.IsAny(), _endPoint)).Returns(mockServerMonitor.Object); - var subject = new DefaultServer(_clusterId, _clusterClock, _clusterConnectionMode, _connectionModeSwitch, _directConnection, _settings, _endPoint, mockConnectionPoolFactory.Object, mockServerMonitorFactory.Object, _capturedEvents, _serverApi); + var subject = new DefaultServer(_clusterId, _clusterClock, _clusterConnectionMode, _connectionModeSwitch, _directConnection, _settings, _endPoint, mockConnectionPoolFactory.Object, mockServerMonitorFactory.Object, _serverApi, _eventLogger); subject.Initialize(); var heartbeatDescription = mockMonitorServerInitialDescription.With(reasonChanged: "Heartbeat", type: ServerType.Standalone); mockServerMonitor.Setup(m => m.Description).Returns(heartbeatDescription); @@ -875,8 +887,8 @@ private Server SetupServer(bool exceptionOnConnectionOpen, bool exceptionOnConne _endPoint, mockConnectionPoolFactory.Object, _mockServerMonitorFactory.Object, - _capturedEvents, - _serverApi); + _serverApi, + _eventLogger); server.Initialize(); return server; @@ -885,7 +897,7 @@ private Server SetupServer(bool exceptionOnConnectionOpen, bool exceptionOnConne public class ServerChannelTests { - [SkippableTheory] + [Theory] [InlineData(1, 2, 2)] [InlineData(2, 1, 2)] public void Command_should_send_the_greater_of_the_session_and_cluster_cluster_times(long sessionTimestamp, long clusterTimestamp, long expectedTimestamp) @@ -938,7 +950,7 @@ public void Command_should_send_the_greater_of_the_session_and_cluster_cluster_t actualClusterTime.Should().Be(expectedClusterTime); } - [SkippableFact] + [Fact] public void Command_should_update_the_session_and_cluster_cluster_times() { RequireServer.Check().ClusterTypes(ClusterType.ReplicaSet, ClusterType.Sharded); @@ -975,7 +987,7 @@ public void Command_should_update_the_session_and_cluster_cluster_times() } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Command_should_use_serverApi([Values(false, true)] bool async) { diff --git a/tests/MongoDB.Driver.Core.Tests/Core/WireProtocol/CommandWriteProtocolTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/WireProtocol/CommandWriteProtocolTests.cs index 7c337b286fd..7e5c993b307 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/WireProtocol/CommandWriteProtocolTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/WireProtocol/CommandWriteProtocolTests.cs @@ -24,13 +24,14 @@ using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Connections; using MongoDB.Driver.Core.Helpers; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; +using MongoDB.Driver.Core.TestHelpers; using MongoDB.Driver.Core.WireProtocol.Messages; using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; using Moq; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/WireProtocol/Messages/CommandMessageSectionTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/WireProtocol/Messages/CommandMessageSectionTests.cs index 5bf94888178..c73ee5cea79 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/WireProtocol/Messages/CommandMessageSectionTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/WireProtocol/Messages/CommandMessageSectionTests.cs @@ -19,7 +19,7 @@ using MongoDB.Bson.IO; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Misc; using Moq; using Xunit; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/WireProtocol/Messages/CommandMessageTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/WireProtocol/Messages/CommandMessageTests.cs index b912af970f4..beeda5ee40a 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/WireProtocol/Messages/CommandMessageTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/WireProtocol/Messages/CommandMessageTests.cs @@ -21,7 +21,7 @@ using MongoDB.Bson; using MongoDB.Bson.IO; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; using MongoDB.Driver.Core.WireProtocol.Messages.Encoders.BinaryEncoders; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/WireProtocol/Messages/Encoders/BinaryEncoders/CommandMessageBinaryEncoderTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/WireProtocol/Messages/Encoders/BinaryEncoders/CommandMessageBinaryEncoderTests.cs index 91b2b520bcb..2c99282dbd1 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/WireProtocol/Messages/Encoders/BinaryEncoders/CommandMessageBinaryEncoderTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/WireProtocol/Messages/Encoders/BinaryEncoders/CommandMessageBinaryEncoderTests.cs @@ -22,7 +22,7 @@ using MongoDB.Bson.IO; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Misc; using Xunit; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/WireProtocol/Messages/Encoders/BinaryEncoders/MessageBinaryEncoderBaseTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/WireProtocol/Messages/Encoders/BinaryEncoders/MessageBinaryEncoderBaseTests.cs index 49d464dd409..88cbf532c46 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/WireProtocol/Messages/Encoders/BinaryEncoders/MessageBinaryEncoderBaseTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/WireProtocol/Messages/Encoders/BinaryEncoders/MessageBinaryEncoderBaseTests.cs @@ -16,7 +16,7 @@ using System.IO; using System.Reflection; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.Core.WireProtocol.Messages.Encoders.BinaryEncoders diff --git a/tests/MongoDB.Driver.Core.Tests/Core/WireProtocol/Messages/Encoders/BinaryEncoders/QueryMessageBinaryEncoderTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/WireProtocol/Messages/Encoders/BinaryEncoders/QueryMessageBinaryEncoderTests.cs index abd83d75469..5f9196472d1 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/WireProtocol/Messages/Encoders/BinaryEncoders/QueryMessageBinaryEncoderTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/WireProtocol/Messages/Encoders/BinaryEncoders/QueryMessageBinaryEncoderTests.cs @@ -18,7 +18,7 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.IO; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.Core.WireProtocol.Messages.Encoders.BinaryEncoders diff --git a/tests/MongoDB.Driver.Core.Tests/Core/WireProtocol/Messages/Encoders/JsonEncoders/CommandMessageJsonEncoderTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/WireProtocol/Messages/Encoders/JsonEncoders/CommandMessageJsonEncoderTests.cs index 34fa0b32c53..c4dae987fa4 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/WireProtocol/Messages/Encoders/JsonEncoders/CommandMessageJsonEncoderTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/WireProtocol/Messages/Encoders/JsonEncoders/CommandMessageJsonEncoderTests.cs @@ -22,7 +22,7 @@ using MongoDB.Bson; using MongoDB.Bson.IO; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Misc; using Xunit; diff --git a/tests/MongoDB.Driver.Core.Tests/Core/WireProtocol/Messages/QueryMessageTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/WireProtocol/Messages/QueryMessageTests.cs index 67591281745..8beb8be06b3 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/WireProtocol/Messages/QueryMessageTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/WireProtocol/Messages/QueryMessageTests.cs @@ -17,7 +17,7 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.IO; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; using Moq; using Xunit; diff --git a/tests/MongoDB.Driver.Core.Tests/CreateIndexCommitQuorumTests.cs b/tests/MongoDB.Driver.Core.Tests/CreateIndexCommitQuorumTests.cs index 91a0ca0d051..1742fe8282a 100644 --- a/tests/MongoDB.Driver.Core.Tests/CreateIndexCommitQuorumTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/CreateIndexCommitQuorumTests.cs @@ -16,7 +16,7 @@ using System; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.Core.Tests diff --git a/tests/MongoDB.Driver.Core.Tests/IAsyncCursorExtensionsTests.cs b/tests/MongoDB.Driver.Core.Tests/IAsyncCursorExtensionsTests.cs index f6d90885bf5..aff630021b5 100644 --- a/tests/MongoDB.Driver.Core.Tests/IAsyncCursorExtensionsTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/IAsyncCursorExtensionsTests.cs @@ -19,7 +19,7 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Operations; using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; diff --git a/tests/MongoDB.Driver.Core.Tests/IAsyncCursorSourceExtensionsTests.cs b/tests/MongoDB.Driver.Core.Tests/IAsyncCursorSourceExtensionsTests.cs index 1e0b081cc9b..e2b8188ce43 100644 --- a/tests/MongoDB.Driver.Core.Tests/IAsyncCursorSourceExtensionsTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/IAsyncCursorSourceExtensionsTests.cs @@ -21,7 +21,7 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Operations; using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; diff --git a/tests/MongoDB.Driver.Core.Tests/Jira/CSharp3173Tests.cs b/tests/MongoDB.Driver.Core.Tests/Jira/CSharp3173Tests.cs index 6ed6b34a8b8..8d5c405ea23 100644 --- a/tests/MongoDB.Driver.Core.Tests/Jira/CSharp3173Tests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Jira/CSharp3173Tests.cs @@ -22,7 +22,7 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Clusters.ServerSelectors; using MongoDB.Driver.Core.Configuration; @@ -32,14 +32,16 @@ using MongoDB.Driver.Core.Helpers; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; +using MongoDB.Driver.Core.TestHelpers.Logging; using MongoDB.Driver.Core.WireProtocol.Messages; using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; using Moq; using Xunit; +using Xunit.Abstractions; namespace MongoDB.Driver.Core.Tests.Jira { - public class CSharp3173Tests + public class CSharp3173Tests : LoggableTestClass { #pragma warning disable CS0618 // Type or member is obsolete private readonly static ClusterConnectionMode __clusterConnectionMode = ClusterConnectionMode.Sharded; @@ -53,6 +55,10 @@ public class CSharp3173Tests private readonly static ServerId __serverId1 = new ServerId(__clusterId, __endPoint1); private readonly static ServerId __serverId2 = new ServerId(__clusterId, __endPoint2); + public CSharp3173Tests(ITestOutputHelper output) : base(output) + { + } + [Theory] [ParameterAttributeData] public void Ensure_command_network_error_before_hadnshake_is_correctly_handled([Values(false, true)] bool async, [Values(false, true)] bool streamable) @@ -207,6 +213,7 @@ private IConnectionFactory CreateAndSetupServerMonitorConnectionFactory( params (ServerId ServerId, EndPoint Endpoint, bool IsHealthy)[] serverInfoCollection) { var mockConnectionFactory = new Mock(); + mockConnectionFactory.Setup(f => f.ConnectionSettings).Returns(() => new ConnectionSettings()); foreach (var serverInfo in serverInfoCollection) { @@ -262,11 +269,11 @@ private MultiServerCluster CreateAndSetupCluster(TaskCompletionSource hasN serverId => (IConnectionExceptionHandler)cluster._servers().Single(s => s.ServerId.Equals(serverId)), serverInfoCollection); var serverMonitorConnectionFactory = CreateAndSetupServerMonitorConnectionFactory(hasNetworkErrorBeenTriggered, hasClusterBeenDisposed, streamable, serverInfoCollection); - var serverMonitorFactory = new ServerMonitorFactory(serverMonitorSettings, serverMonitorConnectionFactory, eventCapturer, serverApi: null); + var serverMonitorFactory = new ServerMonitorFactory(serverMonitorSettings, serverMonitorConnectionFactory, eventCapturer, serverApi: null, LoggerFactory); - var serverFactory = new ServerFactory(__clusterConnectionMode, __connectionModeSwitch, __directConnection, serverSettings, connectionPoolFactory, serverMonitorFactory, eventCapturer, serverApi: null); + var serverFactory = new ServerFactory(__clusterConnectionMode, __connectionModeSwitch, __directConnection, serverSettings, connectionPoolFactory, serverMonitorFactory, eventCapturer, serverApi: null, loggerFactory: null); - return cluster = new MultiServerCluster(clusterSettings, serverFactory, eventCapturer); + return cluster = new MultiServerCluster(clusterSettings, serverFactory, eventCapturer, LoggerFactory); } private Exception CreateDnsException(ConnectionId connectionId, string from) diff --git a/tests/MongoDB.Driver.Core.Tests/Jira/CSharp3302Tests.cs b/tests/MongoDB.Driver.Core.Tests/Jira/CSharp3302Tests.cs index 5c297bbfc9d..44473a7598d 100644 --- a/tests/MongoDB.Driver.Core.Tests/Jira/CSharp3302Tests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Jira/CSharp3302Tests.cs @@ -30,14 +30,16 @@ using MongoDB.Driver.Core.Helpers; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; +using MongoDB.Driver.Core.TestHelpers.Logging; using MongoDB.Driver.Core.WireProtocol.Messages; using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; using Moq; using Xunit; +using Xunit.Abstractions; namespace MongoDB.Driver.Core.Tests.Jira { - public class CSharp3302Tests + public class CSharp3302Tests : LoggableTestClass { #pragma warning disable CS0618 // Type or member is obsolete private readonly static ClusterConnectionMode __clusterConnectionMode = ClusterConnectionMode.ReplicaSet; @@ -51,6 +53,10 @@ public class CSharp3302Tests private readonly static ServerId __serverId1 = new ServerId(__clusterId, __endPoint1); private readonly static ServerId __serverId2 = new ServerId(__clusterId, __endPoint2); + public CSharp3302Tests(ITestOutputHelper output) : base(output) + { + } + [Fact] public async Task RapidHeartbeatTimerCallback_should_ignore_reentrant_calls() { @@ -73,6 +79,7 @@ public async Task RapidHeartbeatTimerCallback_should_ignore_reentrant_calls() replicaSetConfig: new ReplicaSetConfig(new[] { __endPoint1 }, "rs", __endPoint1, null)); var serverMock = new Mock(); + serverMock.Setup(s => s.ServerId).Returns(__serverId1); serverMock.Setup(s => s.EndPoint).Returns(__endPoint1); serverMock.Setup(s => s.IsInitialized).Returns(true); serverMock.Setup(s => s.Description).Returns(serverDescription); @@ -83,7 +90,7 @@ public async Task RapidHeartbeatTimerCallback_should_ignore_reentrant_calls() .Setup(f => f.CreateServer(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns(serverMock.Object); - using (var cluster = new MultiServerCluster(clusterSettings, serverFactoryMock.Object, new EventCapturer())) + using (var cluster = new MultiServerCluster(clusterSettings, serverFactoryMock.Object, new EventCapturer(), LoggerFactory)) { cluster._minHeartbeatInterval(TimeSpan.FromMilliseconds(10)); @@ -214,6 +221,7 @@ private IConnectionFactory CreateAndSetupServerMonitorConnectionFactory( params (ServerId ServerId, EndPoint Endpoint)[] serverInfoCollection) { var mockConnectionFactory = new Mock(); + mockConnectionFactory.Setup(f => f.ConnectionSettings).Returns(() => new ConnectionSettings()); foreach (var serverInfo in serverInfoCollection) { @@ -249,10 +257,10 @@ private MultiServerCluster CreateAndSetupCluster(HashSet primaries) var eventCapturer = new EventCapturer(); var connectionPoolFactory = CreateAndSetupConnectionPoolFactory(serverInfoCollection); var serverMonitorConnectionFactory = CreateAndSetupServerMonitorConnectionFactory(primaries, serverInfoCollection); - var serverMonitorFactory = new ServerMonitorFactory(serverMonitorSettings, serverMonitorConnectionFactory, eventCapturer, serverApi: null); + var serverMonitorFactory = new ServerMonitorFactory(serverMonitorSettings, serverMonitorConnectionFactory, eventCapturer, serverApi: null, LoggerFactory); - var serverFactory = new ServerFactory(__clusterConnectionMode, __connectionModeSwitch, __directConnection, serverSettings, connectionPoolFactory, serverMonitorFactory, eventCapturer, serverApi: null); - return new MultiServerCluster(clusterSettings, serverFactory, eventCapturer); + var serverFactory = new ServerFactory(__clusterConnectionMode, __connectionModeSwitch, __directConnection, serverSettings, connectionPoolFactory, serverMonitorFactory, eventCapturer, serverApi: null, null); + return new MultiServerCluster(clusterSettings, serverFactory, eventCapturer, LoggerFactory); } private IServerSelector CreateWritableServerAndEndPointSelector(EndPoint endPoint) diff --git a/tests/MongoDB.Driver.Core.Tests/LoadBalancingIntergationTests.cs b/tests/MongoDB.Driver.Core.Tests/LoadBalancingIntergationTests.cs index 79fd6560372..64edef3bb35 100644 --- a/tests/MongoDB.Driver.Core.Tests/LoadBalancingIntergationTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/LoadBalancingIntergationTests.cs @@ -20,7 +20,7 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Connections; @@ -43,7 +43,7 @@ public LoadBalancingIntergationTests() _collectionNamespace = CollectionNamespace.FromFullName("db.coll"); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void BulkWrite_should_pin_connection_as_expected( [Values(1, 3)] int attempts, @@ -91,7 +91,7 @@ public void BulkWrite_should_pin_connection_as_expected( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void BulkWrite_and_cursor_should_share_pinned_connection_under_the_same_transaction_2( [Values(false, true)] bool async) @@ -163,7 +163,7 @@ public void BulkWrite_and_cursor_should_share_pinned_connection_under_the_same_t } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void BulkWrite_and_cursor_should_share_pinned_connection_under_the_same_transaction( [Values(1, 3)] int attempts, @@ -229,7 +229,7 @@ public void BulkWrite_and_cursor_should_share_pinned_connection_under_the_same_t } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Cursor_should_pin_connection_as_expected( [Values(1, 3)] int attempts, @@ -300,7 +300,7 @@ public void Cursor_should_pin_connection_as_expected( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Cursor_should_pin_connection_in_transaction_with_new_sessions_as_expected( [Values(1, 3)] int attempts, @@ -374,7 +374,7 @@ public void Cursor_should_pin_connection_in_transaction_with_new_sessions_as_exp } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Cursor_should_pin_connection_in_transaction_with_the_same_session_as_expected( [Values(1, 4)] int attempts, @@ -451,7 +451,7 @@ public void Cursor_should_pin_connection_in_transaction_with_the_same_session_as } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Cursor_should_unpin_connection_for_operations_under_the_same_transaction_after_abortTransaction_and_cursor_dispose( [Values(1, 3)] int attempts, diff --git a/tests/MongoDB.Driver.Core.Tests/MongoAuthenticationExceptionTests.cs b/tests/MongoDB.Driver.Core.Tests/MongoAuthenticationExceptionTests.cs index a027e3bf430..b5e5caa0c42 100644 --- a/tests/MongoDB.Driver.Core.Tests/MongoAuthenticationExceptionTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/MongoAuthenticationExceptionTests.cs @@ -59,9 +59,11 @@ public void Serialization_should_work() var formatter = new BinaryFormatter(); using (var stream = new MemoryStream()) { +#pragma warning disable SYSLIB0011 // BinaryFormatter serialization is obsolete formatter.Serialize(stream, subject); stream.Position = 0; var rehydrated = (MongoAuthenticationException)formatter.Deserialize(stream); +#pragma warning restore SYSLIB0011 // BinaryFormatter serialization is obsolete rehydrated.ConnectionId.Should().Be(subject.ConnectionId); rehydrated.Message.Should().Be(subject.Message); diff --git a/tests/MongoDB.Driver.Core.Tests/MongoClientExceptionTests.cs b/tests/MongoDB.Driver.Core.Tests/MongoClientExceptionTests.cs index 732bd8467f2..9a2fc715bf0 100644 --- a/tests/MongoDB.Driver.Core.Tests/MongoClientExceptionTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/MongoClientExceptionTests.cs @@ -52,9 +52,11 @@ public void Serialization_should_work() var formatter = new BinaryFormatter(); using (var stream = new MemoryStream()) { +#pragma warning disable SYSLIB0011 // BinaryFormatter serialization is obsolete formatter.Serialize(stream, subject); stream.Position = 0; var rehydrated = (MongoClientException)formatter.Deserialize(stream); +#pragma warning restore SYSLIB0011 // BinaryFormatter serialization is obsolete rehydrated.Message.Should().Be(subject.Message); rehydrated.InnerException.Message.Should().Be(subject.InnerException.Message); // Exception does not override Equals diff --git a/tests/MongoDB.Driver.Core.Tests/MongoCommandExceptionTests.cs b/tests/MongoDB.Driver.Core.Tests/MongoCommandExceptionTests.cs index b31e3f6bcb5..74ed29f0315 100644 --- a/tests/MongoDB.Driver.Core.Tests/MongoCommandExceptionTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/MongoCommandExceptionTests.cs @@ -116,9 +116,11 @@ public void Serialization_should_work() var formatter = new BinaryFormatter(); using (var stream = new MemoryStream()) { +#pragma warning disable SYSLIB0011 // BinaryFormatter serialization is obsolete formatter.Serialize(stream, subject); stream.Position = 0; var rehydrated = (MongoCommandException)formatter.Deserialize(stream); +#pragma warning restore SYSLIB0011 // BinaryFormatter serialization is obsolete rehydrated.ErrorLabels.Should().Equal(subject.ErrorLabels); rehydrated.ConnectionId.Should().Be(subject.ConnectionId); diff --git a/tests/MongoDB.Driver.Core.Tests/MongoConfigurationExceptionTests.cs b/tests/MongoDB.Driver.Core.Tests/MongoConfigurationExceptionTests.cs index ce7ec950c71..246ca698cbd 100644 --- a/tests/MongoDB.Driver.Core.Tests/MongoConfigurationExceptionTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/MongoConfigurationExceptionTests.cs @@ -52,9 +52,11 @@ public void Serialization_should_work() var formatter = new BinaryFormatter(); using (var stream = new MemoryStream()) { +#pragma warning disable SYSLIB0011 // BinaryFormatter serialization is obsolete formatter.Serialize(stream, subject); stream.Position = 0; var rehydrated = (MongoConfigurationException)formatter.Deserialize(stream); +#pragma warning restore SYSLIB0011 // BinaryFormatter serialization is obsolete rehydrated.Message.Should().Be(subject.Message); rehydrated.InnerException.Message.Should().Be(subject.InnerException.Message); // Exception does not override Equals diff --git a/tests/MongoDB.Driver.Core.Tests/MongoConnectionClosedExceptionTests.cs b/tests/MongoDB.Driver.Core.Tests/MongoConnectionClosedExceptionTests.cs index d5723787bf2..1a1b5099d62 100644 --- a/tests/MongoDB.Driver.Core.Tests/MongoConnectionClosedExceptionTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/MongoConnectionClosedExceptionTests.cs @@ -46,9 +46,11 @@ public void Serialization_should_work() var formatter = new BinaryFormatter(); using (var stream = new MemoryStream()) { +#pragma warning disable SYSLIB0011 // BinaryFormatter serialization is obsolete formatter.Serialize(stream, subject); stream.Position = 0; var rehydrated = (MongoConnectionClosedException)formatter.Deserialize(stream); +#pragma warning restore SYSLIB0011 // BinaryFormatter serialization is obsolete rehydrated.ConnectionId.Should().Be(subject.ConnectionId); rehydrated.InnerException.Should().BeNull(); diff --git a/tests/MongoDB.Driver.Core.Tests/MongoConnectionExceptionTests.cs b/tests/MongoDB.Driver.Core.Tests/MongoConnectionExceptionTests.cs index af105991f88..24533c22a3f 100644 --- a/tests/MongoDB.Driver.Core.Tests/MongoConnectionExceptionTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/MongoConnectionExceptionTests.cs @@ -59,9 +59,11 @@ public void Serialization_should_work() var formatter = new BinaryFormatter(); using (var stream = new MemoryStream()) { +#pragma warning disable SYSLIB0011 // BinaryFormatter serialization is obsolete formatter.Serialize(stream, subject); stream.Position = 0; var rehydrated = (MongoConnectionException)formatter.Deserialize(stream); +#pragma warning restore SYSLIB0011 // BinaryFormatter serialization is obsolete rehydrated.ConnectionId.Should().Be(subject.ConnectionId); rehydrated.Message.Should().Be(subject.Message); diff --git a/tests/MongoDB.Driver.Core.Tests/MongoCursorNotFoundExceptionTests.cs b/tests/MongoDB.Driver.Core.Tests/MongoCursorNotFoundExceptionTests.cs index 9c53da4d059..641d4bab98d 100644 --- a/tests/MongoDB.Driver.Core.Tests/MongoCursorNotFoundExceptionTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/MongoCursorNotFoundExceptionTests.cs @@ -54,9 +54,11 @@ public void Serialization_should_work() var formatter = new BinaryFormatter(); using (var stream = new MemoryStream()) { +#pragma warning disable SYSLIB0011 // BinaryFormatter serialization is obsolete formatter.Serialize(stream, subject); stream.Position = 0; var rehydrated = (MongoCursorNotFoundException)formatter.Deserialize(stream); +#pragma warning restore SYSLIB0011 // BinaryFormatter serialization is obsolete rehydrated.ConnectionId.Should().Be(subject.ConnectionId); rehydrated.CursorId.Should().Be(subject.CursorId); diff --git a/tests/MongoDB.Driver.Core.Tests/MongoDB.Driver.Core.Tests.csproj b/tests/MongoDB.Driver.Core.Tests/MongoDB.Driver.Core.Tests.csproj index 3974b81b1f2..3520a37b3d7 100644 --- a/tests/MongoDB.Driver.Core.Tests/MongoDB.Driver.Core.Tests.csproj +++ b/tests/MongoDB.Driver.Core.Tests/MongoDB.Driver.Core.Tests.csproj @@ -1,42 +1,20 @@ - - - true - + + - netcoreapp2.1;netcoreapp3.1;net472 - netcoreapp2.1;netcoreapp3.1 - 9 - true - - false ..\..\MongoDBTest.ruleset MongoDB.Driver.Core.Tests MongoDB.Driver.Core.Tests - MongoDB Inc. - Copyright © 2010-present MongoDB Inc. MongoDB.Driver.Core tests. - - 0.0.0-local - - - - TRACE - - - - $(DefineConstants);WINDOWS - - 1701;1702; @@ -51,13 +29,8 @@ - - - - - - + @@ -67,14 +40,6 @@ - - - - - - - - Always diff --git a/tests/MongoDB.Driver.Core.Tests/MongoDuplicateKeyExceptionTests.cs b/tests/MongoDB.Driver.Core.Tests/MongoDuplicateKeyExceptionTests.cs index 26c99704efb..0de9db2ae28 100644 --- a/tests/MongoDB.Driver.Core.Tests/MongoDuplicateKeyExceptionTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/MongoDuplicateKeyExceptionTests.cs @@ -53,9 +53,11 @@ public void Serialization_should_work() var formatter = new BinaryFormatter(); using (var stream = new MemoryStream()) { +#pragma warning disable SYSLIB0011 // BinaryFormatter serialization is obsolete formatter.Serialize(stream, subject); stream.Position = 0; var rehydrated = (MongoDuplicateKeyException)formatter.Deserialize(stream); +#pragma warning restore SYSLIB0011 // BinaryFormatter serialization is obsolete rehydrated.Command.Should().BeNull(); rehydrated.ConnectionId.Should().Be(subject.ConnectionId); diff --git a/tests/MongoDB.Driver.Core.Tests/MongoExceptionTests.cs b/tests/MongoDB.Driver.Core.Tests/MongoExceptionTests.cs index ffce1ca4273..4425ec13bb9 100644 --- a/tests/MongoDB.Driver.Core.Tests/MongoExceptionTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/MongoExceptionTests.cs @@ -19,7 +19,7 @@ using System.Linq; using System.Runtime.Serialization.Formatters.Binary; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver @@ -149,9 +149,11 @@ public void Serialization_should_work() var formatter = new BinaryFormatter(); using (var stream = new MemoryStream()) { +#pragma warning disable SYSLIB0011 // BinaryFormatter serialization is obsolete formatter.Serialize(stream, subject); stream.Position = 0; var rehydrated = (MongoException)formatter.Deserialize(stream); +#pragma warning restore SYSLIB0011 // BinaryFormatter serialization is obsolete rehydrated.Message.Should().Be(subject.Message); rehydrated.InnerException.Message.Should().Be(subject.InnerException.Message); // Exception does not override Equals diff --git a/tests/MongoDB.Driver.Core.Tests/MongoExecutionTimeoutExceptionTests.cs b/tests/MongoDB.Driver.Core.Tests/MongoExecutionTimeoutExceptionTests.cs index d9eb55af8f4..ea6aaebc3f8 100644 --- a/tests/MongoDB.Driver.Core.Tests/MongoExecutionTimeoutExceptionTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/MongoExecutionTimeoutExceptionTests.cs @@ -60,9 +60,11 @@ public void Serialization_should_work() var formatter = new BinaryFormatter(); using (var stream = new MemoryStream()) { +#pragma warning disable SYSLIB0011 // BinaryFormatter serialization is obsolete formatter.Serialize(stream, subject); stream.Position = 0; var rehydrated = (MongoExecutionTimeoutException)formatter.Deserialize(stream); +#pragma warning restore SYSLIB0011 // BinaryFormatter serialization is obsolete rehydrated.Code.Should().Be(subject.Code); rehydrated.ConnectionId.Should().Be(subject.ConnectionId); diff --git a/tests/MongoDB.Driver.Core.Tests/MongoIncompatibleDriverExceptionTests.cs b/tests/MongoDB.Driver.Core.Tests/MongoIncompatibleDriverExceptionTests.cs index 1fd16fec0b2..ae8d35c034e 100644 --- a/tests/MongoDB.Driver.Core.Tests/MongoIncompatibleDriverExceptionTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/MongoIncompatibleDriverExceptionTests.cs @@ -61,9 +61,11 @@ public void Serialization_should_work() var formatter = new BinaryFormatter(); using (var stream = new MemoryStream()) { +#pragma warning disable SYSLIB0011 // BinaryFormatter serialization is obsolete formatter.Serialize(stream, subject); stream.Position = 0; var rehydrated = (MongoIncompatibleDriverException)formatter.Deserialize(stream); +#pragma warning restore SYSLIB0011 // BinaryFormatter serialization is obsolete rehydrated.Message.Should().Be(subject.Message); rehydrated.InnerException.Should().BeNull(); diff --git a/tests/MongoDB.Driver.Core.Tests/MongoInternalExceptionTests.cs b/tests/MongoDB.Driver.Core.Tests/MongoInternalExceptionTests.cs index 9c070912d93..a088f011629 100644 --- a/tests/MongoDB.Driver.Core.Tests/MongoInternalExceptionTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/MongoInternalExceptionTests.cs @@ -52,9 +52,11 @@ public void Serialization_should_work() var formatter = new BinaryFormatter(); using (var stream = new MemoryStream()) { +#pragma warning disable SYSLIB0011 // BinaryFormatter serialization is obsolete formatter.Serialize(stream, subject); stream.Position = 0; var rehydrated = (MongoInternalException)formatter.Deserialize(stream); +#pragma warning restore SYSLIB0011 // BinaryFormatter serialization is obsolete rehydrated.InnerException.Message.Should().Be(subject.InnerException.Message); // Exception does not override Equals rehydrated.Message.Should().Be(subject.Message); diff --git a/tests/MongoDB.Driver.Core.Tests/MongoNodeIsRecoveringExceptionTests.cs b/tests/MongoDB.Driver.Core.Tests/MongoNodeIsRecoveringExceptionTests.cs index fb638898e51..49f88bfedb3 100644 --- a/tests/MongoDB.Driver.Core.Tests/MongoNodeIsRecoveringExceptionTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/MongoNodeIsRecoveringExceptionTests.cs @@ -84,9 +84,11 @@ public void Serialization_should_work() var formatter = new BinaryFormatter(); using (var stream = new MemoryStream()) { +#pragma warning disable SYSLIB0011 // BinaryFormatter serialization is obsolete formatter.Serialize(stream, subject); stream.Position = 0; var rehydrated = (MongoNodeIsRecoveringException)formatter.Deserialize(stream); +#pragma warning restore SYSLIB0011 // BinaryFormatter serialization is obsolete rehydrated.ConnectionId.Should().Be(subject.ConnectionId); rehydrated.InnerException.Should().BeNull(); diff --git a/tests/MongoDB.Driver.Core.Tests/MongoNotPrimaryExceptionTests.cs b/tests/MongoDB.Driver.Core.Tests/MongoNotPrimaryExceptionTests.cs index cef66d93b24..3d0c91d86ba 100644 --- a/tests/MongoDB.Driver.Core.Tests/MongoNotPrimaryExceptionTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/MongoNotPrimaryExceptionTests.cs @@ -51,9 +51,11 @@ public void Serialization_should_work() var formatter = new BinaryFormatter(); using (var stream = new MemoryStream()) { +#pragma warning disable SYSLIB0011 // BinaryFormatter serialization is obsolete formatter.Serialize(stream, subject); stream.Position = 0; var rehydrated = (MongoNotPrimaryException)formatter.Deserialize(stream); +#pragma warning restore SYSLIB0011 // BinaryFormatter serialization is obsolete rehydrated.ConnectionId.Should().Be(subject.ConnectionId); rehydrated.InnerException.Should().BeNull(); diff --git a/tests/MongoDB.Driver.Core.Tests/MongoQueryExceptionTests.cs b/tests/MongoDB.Driver.Core.Tests/MongoQueryExceptionTests.cs index 0e6ffb9f058..5618b6b48e0 100644 --- a/tests/MongoDB.Driver.Core.Tests/MongoQueryExceptionTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/MongoQueryExceptionTests.cs @@ -52,9 +52,11 @@ public void Serialization_should_work() var formatter = new BinaryFormatter(); using (var stream = new MemoryStream()) { +#pragma warning disable SYSLIB0011 // BinaryFormatter serialization is obsolete formatter.Serialize(stream, subject); stream.Position = 0; var rehydrated = (MongoQueryException)formatter.Deserialize(stream); +#pragma warning restore SYSLIB0011 // BinaryFormatter serialization is obsolete rehydrated.ConnectionId.Should().Be(subject.ConnectionId); rehydrated.InnerException.Should().BeNull(); diff --git a/tests/MongoDB.Driver.Core.Tests/MongoServerExceptionTests.cs b/tests/MongoDB.Driver.Core.Tests/MongoServerExceptionTests.cs index c0e62ddd303..ea71024f829 100644 --- a/tests/MongoDB.Driver.Core.Tests/MongoServerExceptionTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/MongoServerExceptionTests.cs @@ -59,9 +59,11 @@ public void Serialization_should_work() var formatter = new BinaryFormatter(); using (var stream = new MemoryStream()) { +#pragma warning disable SYSLIB0011 // BinaryFormatter serialization is obsolete formatter.Serialize(stream, subject); stream.Position = 0; var rehydrated = (MongoServerException)formatter.Deserialize(stream); +#pragma warning restore SYSLIB0011 // BinaryFormatter serialization is obsolete rehydrated.ConnectionId.Should().Be(subject.ConnectionId); rehydrated.Message.Should().Be(subject.Message); diff --git a/tests/MongoDB.Driver.Core.Tests/MongoWaitQueueFullExceptionTests.cs b/tests/MongoDB.Driver.Core.Tests/MongoWaitQueueFullExceptionTests.cs index 511b3f84cdc..4fc1df63784 100644 --- a/tests/MongoDB.Driver.Core.Tests/MongoWaitQueueFullExceptionTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/MongoWaitQueueFullExceptionTests.cs @@ -57,9 +57,11 @@ public void Serialization_should_work() var formatter = new BinaryFormatter(); using (var stream = new MemoryStream()) { +#pragma warning disable SYSLIB0011 // BinaryFormatter serialization is obsolete formatter.Serialize(stream, subject); stream.Position = 0; var rehydrated = (MongoWaitQueueFullException)formatter.Deserialize(stream); +#pragma warning restore SYSLIB0011 // BinaryFormatter serialization is obsolete rehydrated.Message.Should().Be(subject.Message); rehydrated.InnerException.Should().BeNull(); diff --git a/tests/MongoDB.Driver.Core.Tests/MongoWriteConcernExceptionTests.cs b/tests/MongoDB.Driver.Core.Tests/MongoWriteConcernExceptionTests.cs index cc8406a0bf3..20447f4f2d9 100644 --- a/tests/MongoDB.Driver.Core.Tests/MongoWriteConcernExceptionTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/MongoWriteConcernExceptionTests.cs @@ -101,9 +101,11 @@ public void Serialization_should_work() var formatter = new BinaryFormatter(); using (var stream = new MemoryStream()) { +#pragma warning disable SYSLIB0011 // BinaryFormatter serialization is obsolete formatter.Serialize(stream, subject); stream.Position = 0; var rehydrated = (MongoWriteConcernException)formatter.Deserialize(stream); +#pragma warning restore SYSLIB0011 // BinaryFormatter serialization is obsolete rehydrated.Command.Should().BeNull(); rehydrated.ConnectionId.Should().Be(subject.ConnectionId); diff --git a/tests/MongoDB.Driver.Core.Tests/Properties/AssemblyInfo.cs b/tests/MongoDB.Driver.Core.Tests/Properties/AssemblyInfo.cs index c98f691b42d..5ccb2904469 100644 --- a/tests/MongoDB.Driver.Core.Tests/Properties/AssemblyInfo.cs +++ b/tests/MongoDB.Driver.Core.Tests/Properties/AssemblyInfo.cs @@ -14,11 +14,11 @@ */ using System.Runtime.InteropServices; -using MongoDB.Driver.Core.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; [assembly: ComVisible(false)] [assembly: CollectionBehavior(DisableTestParallelization = true)] -[assembly: TestFramework(XunitExtensionsConsts.TimeoutEnforcingXunitFramework, XunitExtensionsConsts.TimeoutEnforcingFrameworkAssembly)] +[assembly: TestFramework(XunitExtensionsConstants.TimeoutEnforcingXunitFramework, XunitExtensionsConstants.TimeoutEnforcingFrameworkAssembly)] diff --git a/tests/MongoDB.Driver.Core.Tests/ReadConcernTests.cs b/tests/MongoDB.Driver.Core.Tests/ReadConcernTests.cs index c594865f310..db0c5284b11 100644 --- a/tests/MongoDB.Driver.Core.Tests/ReadConcernTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/ReadConcernTests.cs @@ -20,7 +20,7 @@ using System.Threading.Tasks; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Misc; using Xunit; diff --git a/tests/MongoDB.Driver.Core.Tests/ReadPreferenceHedgeTests.cs b/tests/MongoDB.Driver.Core.Tests/ReadPreferenceHedgeTests.cs index 6cf46ed5a65..9b6f36e6b7e 100644 --- a/tests/MongoDB.Driver.Core.Tests/ReadPreferenceHedgeTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/ReadPreferenceHedgeTests.cs @@ -14,7 +14,7 @@ */ using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.Core.Tests diff --git a/tests/MongoDB.Driver.Core.Tests/ReadPreferenceTests.cs b/tests/MongoDB.Driver.Core.Tests/ReadPreferenceTests.cs index 22535318a7e..602d018ed55 100644 --- a/tests/MongoDB.Driver.Core.Tests/ReadPreferenceTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/ReadPreferenceTests.cs @@ -21,7 +21,7 @@ using System.Threading.Tasks; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Tests; using Xunit; diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/read-write-concern/tests/Makefile b/tests/MongoDB.Driver.Core.Tests/Specifications/read-write-concern/tests/Makefile deleted file mode 100644 index 71d6cf840c6..00000000000 --- a/tests/MongoDB.Driver.Core.Tests/Specifications/read-write-concern/tests/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -YAML_FILES=$(shell find . -iname '*.yml') -JSON_FILES=$(patsubst %.yml,%.json,$(YAML_FILES)) - -all: $(JSON_FILES) - -%.json : %.yml - jwc yaml2json $< > $@ diff --git a/tests/MongoDB.Driver.Core.Tests/TargetFrameworkTests.cs b/tests/MongoDB.Driver.Core.Tests/TargetFrameworkTests.cs index 5f142104173..8f434d4926c 100644 --- a/tests/MongoDB.Driver.Core.Tests/TargetFrameworkTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/TargetFrameworkTests.cs @@ -23,7 +23,7 @@ public class TargetFrameworkTests [Fact] public void TargetFramework_should_be_valid() { - var actualFramework = MongoDB.Driver.Core.TargetFramework.Moniker; + var actualFramework = TargetFramework.Moniker; var expectedFramework = GetExpectedTargetFramework(); actualFramework.Should().Be(expectedFramework); } @@ -33,7 +33,7 @@ private string GetExpectedTargetFramework() { #if NETCOREAPP2_1 return "netstandard20"; -#elif NETCOREAPP3_1 +#elif NETCOREAPP3_1_OR_GREATER return "netstandard21"; #elif NET472 return "net472"; diff --git a/tests/MongoDB.Driver.Core.Tests/TransactionOptionsTests.cs b/tests/MongoDB.Driver.Core.Tests/TransactionOptionsTests.cs index dc2d18100e7..d9257362bed 100644 --- a/tests/MongoDB.Driver.Core.Tests/TransactionOptionsTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/TransactionOptionsTests.cs @@ -14,7 +14,7 @@ */ using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver diff --git a/tests/MongoDB.Driver.Core.Tests/WriteConcernTests.cs b/tests/MongoDB.Driver.Core.Tests/WriteConcernTests.cs index a44f75e0e05..690b8d61657 100644 --- a/tests/MongoDB.Driver.Core.Tests/WriteConcernTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/WriteConcernTests.cs @@ -20,7 +20,7 @@ using System.Threading.Tasks; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver diff --git a/tests/MongoDB.Driver.Examples/Aws/AwsAuthenticationExamples.cs b/tests/MongoDB.Driver.Examples/Aws/AwsAuthenticationExamples.cs new file mode 100644 index 00000000000..8537d73e500 --- /dev/null +++ b/tests/MongoDB.Driver.Examples/Aws/AwsAuthenticationExamples.cs @@ -0,0 +1,162 @@ +/* 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 MongoDB.Bson; +using MongoDB.TestHelpers.XunitExtensions; +using Xunit; + +namespace MongoDB.Driver.Examples.Aws +{ + /// + /// Atlas preconditions for local run: + /// 1. Configure AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and optionally AWS_SESSION_TOKEN. If used, AWS_SESSION_TOKEN should be regenerated periodically. + /// You may use `Command line or programmatic access` page on awsapps.com + /// 2. Configure credentials folder here: c:\Users\{user}\.aws\ + /// 3. Get your arn via `get-caller-identity`: + /// ./aws sts get-caller-identity + ///{ + /// "UserId": "%ID%:[user@example.com]", + /// "Account": "%ID_VALUE%", + /// "Arn": "arn:aws:sts::%ID_VALUE%:assumed-role/%ROLE_NAME%/[user@example.com]" + /// } + /// pay attention on %ROLE_NAME%. + /// 4. list all roles via:. + /// $ ./aws iam list-roles + /// { + /// "Roles": [ + /// { + /// "Path": "..", + /// "RoleName": "%ROLE_NAME%", + /// "Arn": "arn:aws...: + /// ... + /// in the provided roles, search for a record with a RoleName equal to %ROLE_NAME% and record his arn. + /// 5. In your atlas cluster, create a new user with AWS authentication and set AWS IAM Role ARN from #4. + /// 6. Then configure a MongoClient in the same way as it's done in these examples with MONGODB-AWS auth credentials. + /// + /// Additional notes: + /// 1. To work with authentications that are not based on env vars credentials configuration, make sure that AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_SESSION_TOKEN are not set + /// 2. To work with aws profile make sure that env variable AWS_PROFILE has appropriate value if the used aws profile is not default + /// 3. To work with ECS container credentials make sure that AWS_CONTAINER_CREDENTIALS_RELATIVE_URI or AWS_CONTAINER_CREDENTIALS_FULL_URI has appropriate value + /// 4. To work with EC2 container credentials from EC2 instance metadata make sure a test is launched on EC2 env and AWS_CONTAINER_CREDENTIALS_* is not set + /// 5. To work with Aws WebIdentityToken make sure that AWS_WEB_IDENTITY_TOKEN_FILE, AWS_ROLE_ARN and AWS_ROLE_SESSION_NAME are configured + /// + public class AwsAuthenticationExamples + { + private static readonly string __connectionStringHosts = ""; + + [Fact] + public void ConnectionStringAuthConfiguration() + { + // the test uses env variables only to initialize userName, password and awsSessionToken + RequireEnvironment + .Check() + .EnvironmentVariable("AWS_ACCESS_KEY_ID") + .EnvironmentVariable("AWS_SECRET_ACCESS_KEY") + .EnvironmentVariable("AWS_SESSION_TOKEN"); + + // Start explicit aws credentials configuring via connection string + var username = Environment.GetEnvironmentVariable("AWS_ACCESS_KEY_ID"); + var password = Environment.GetEnvironmentVariable("AWS_SECRET_ACCESS_KEY"); + var awsSessionToken = Environment.GetEnvironmentVariable("AWS_SESSION_TOKEN"); + + // AWS_SESSION_TOKEN is optional. If you do not need to specify an AWS session token, omit the authMechanismProperties parameter and his value. + var connectionString = $"mongodb+srv://{username}:{password}@{__connectionStringHosts}?authSource=$external&authMechanism=MONGODB-AWS&authMechanismProperties=AWS_SESSION_TOKEN:{awsSessionToken}"; + var mongoClientSettings = MongoClientSettings.FromConnectionString(connectionString); + var client = new MongoClient(mongoClientSettings); + // End explicit aws credentials configuring via connection string + + RunHello(client); + } + + [Fact] + public void ConnectionStringAuthConfiguration_with_auto_fetching_env_variables() + { + // the test uses env variables fetched inside the driver behind the scene. + // Auto fetching can happen not only from env variables, but adding only a check for them as a simplest guard + RequireEnvironment + .Check() + .EnvironmentVariable("AWS_ACCESS_KEY_ID") + .EnvironmentVariable("AWS_SECRET_ACCESS_KEY") + .EnvironmentVariable("AWS_SESSION_TOKEN"); // optional + + // Start aws authentication configuring via connection string with implicit credentials fetching + var connectionString = $"mongodb+srv://{__connectionStringHosts}?authSource=$external&authMechanism=MONGODB-AWS"; + var mongoClientSettings = MongoClientSettings.FromConnectionString(connectionString); + var client = new MongoClient(mongoClientSettings); + // End aws authentication configuring via connection string with implicit credentials fetching + + RunHello(client); + } + + + [Fact] + public void MongoClientSettingsAuthConfiguration() + { + // the test uses env variable only to initialize userName, password and awsSessionToken + RequireEnvironment + .Check() + .EnvironmentVariable("AWS_ACCESS_KEY_ID") + .EnvironmentVariable("AWS_SECRET_ACCESS_KEY") + .EnvironmentVariable("AWS_SESSION_TOKEN"); + + // Start explicit aws credentials configuring via MongoCredential + var username = Environment.GetEnvironmentVariable("AWS_ACCESS_KEY_ID"); + var password = Environment.GetEnvironmentVariable("AWS_SECRET_ACCESS_KEY"); + var awsSessionToken = Environment.GetEnvironmentVariable("AWS_SESSION_TOKEN"); + + // AWS_SESSION_TOKEN is optional. If you do not need to specify an AWS session token, omit calling WithMechanismProperty method. + var awsCredentials = new MongoCredential("MONGODB-AWS", new MongoExternalIdentity(username), new PasswordEvidence(password)) + .WithMechanismProperty("AWS_SESSION_TOKEN", awsSessionToken); + + var mongoClientSettings = MongoClientSettings.FromConnectionString($"mongodb+srv://{username}:{password}@{__connectionStringHosts}"); + mongoClientSettings.Credential = awsCredentials; + mongoClientSettings.ServerApi = new ServerApi(ServerApiVersion.V1, strict: true); + var client = new MongoClient(mongoClientSettings); + // End explicit aws credentials configuring via MongoCredential + + RunHello(client); + } + + [Fact] + public void MongoClientSettingsAuthConfiguration_with_auto_fetching_env_variables() + { + // the test uses env variables fetched inside the driver behind the scene. + // Auto fetching can happen not only from env variables, but adding only a check for them as a simplest guard + RequireEnvironment + .Check() + .EnvironmentVariable("AWS_ACCESS_KEY_ID") + .EnvironmentVariable("AWS_SECRET_ACCESS_KEY") + .EnvironmentVariable("AWS_SESSION_TOKEN"); // optional + + // Start aws authentication configuring via MongoClientSettings with implicit credentials fetching + var awsCredentials = new MongoCredential("MONGODB-AWS", new MongoExternalAwsIdentity(), new ExternalEvidence()); + + var mongoClientSettings = MongoClientSettings.FromConnectionString($"mongodb+srv://{__connectionStringHosts}"); + mongoClientSettings.Credential = awsCredentials; + mongoClientSettings.ServerApi = new ServerApi(ServerApiVersion.V1, strict: true); + var client = new MongoClient(mongoClientSettings); + // End aws authentication configuring via MongoClientSettings with implicit credentials fetching + + RunHello(client); + } + + // private methods + private void RunHello(IMongoClient client) + { + client.GetDatabase(DatabaseNamespace.Admin.DatabaseName).RunCommand("{ hello : 1 }"); + } + } +} diff --git a/tests/MongoDB.Driver.Examples/AwsLambda/AwsLambdaExamples.cs b/tests/MongoDB.Driver.Examples/Aws/AwsLambdaExamples.cs similarity index 100% rename from tests/MongoDB.Driver.Examples/AwsLambda/AwsLambdaExamples.cs rename to tests/MongoDB.Driver.Examples/Aws/AwsLambdaExamples.cs diff --git a/tests/MongoDB.Driver.Examples/ClientSideEncryption2Examples.cs b/tests/MongoDB.Driver.Examples/ClientSideEncryption2Examples.cs index 536c478eae2..6c6b4dde152 100644 --- a/tests/MongoDB.Driver.Examples/ClientSideEncryption2Examples.cs +++ b/tests/MongoDB.Driver.Examples/ClientSideEncryption2Examples.cs @@ -33,7 +33,7 @@ public class ClientSideEncryption2Examples private readonly static CollectionNamespace CollectionNamespace = CollectionNamespace.FromFullName("docsExamples.encrypted"); private readonly static CollectionNamespace KeyVaultNamespace = CollectionNamespace.FromFullName("keyvault.datakeys"); - [SkippableFact] + [Fact] public void FLE2AutomaticEncryption() { RequireServer.Check().Supports(Feature.Csfle2).ClusterTypes(ClusterType.ReplicaSet, ClusterType.Sharded, ClusterType.LoadBalanced); diff --git a/tests/MongoDB.Driver.Examples/MongoDB.Driver.Examples.csproj b/tests/MongoDB.Driver.Examples/MongoDB.Driver.Examples.csproj index 45538596f57..b66f24afc54 100644 --- a/tests/MongoDB.Driver.Examples/MongoDB.Driver.Examples.csproj +++ b/tests/MongoDB.Driver.Examples/MongoDB.Driver.Examples.csproj @@ -1,23 +1,13 @@ - - true - + - netcoreapp2.1;netcoreapp3.1;net472 - netcoreapp2.1;netcoreapp3.1 - 9 - true - - false ..\..\MongoDBTest.ruleset MongoDB.Driver.Examples MongoDB.Driver.Examples - MongoDB Inc. - Copyright © 2010-present MongoDB Inc. MongoDB.Driver examples. @@ -25,24 +15,12 @@ - - 0.0.0-local - - - - TRACE - - - - - - - + - + diff --git a/tests/MongoDB.Driver.Examples/Properties/AssemblyInfo.cs b/tests/MongoDB.Driver.Examples/Properties/AssemblyInfo.cs index fbbd5774143..35a2ab489b9 100644 --- a/tests/MongoDB.Driver.Examples/Properties/AssemblyInfo.cs +++ b/tests/MongoDB.Driver.Examples/Properties/AssemblyInfo.cs @@ -14,8 +14,11 @@ */ using System.Runtime.InteropServices; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; [assembly: ComVisible(false)] [assembly: CollectionBehavior(DisableTestParallelization = true)] + +[assembly: TestFramework(XunitExtensionsConstants.TimeoutEnforcingXunitFramework, XunitExtensionsConstants.TimeoutEnforcingFrameworkAssembly)] diff --git a/tests/MongoDB.Driver.Examples/TransactionExamplesForDocs/WithTransactionExample1.cs b/tests/MongoDB.Driver.Examples/TransactionExamplesForDocs/WithTransactionExample1.cs index 6c4ab87e324..2a384355165 100644 --- a/tests/MongoDB.Driver.Examples/TransactionExamplesForDocs/WithTransactionExample1.cs +++ b/tests/MongoDB.Driver.Examples/TransactionExamplesForDocs/WithTransactionExample1.cs @@ -25,7 +25,7 @@ namespace MongoDB.Driver.Examples.TransactionExamplesForDocs { public class WithTransactionExample1 { - [SkippableFact] + [Fact] public void Example1() { RequireServer.Check().ClusterTypes(ClusterType.ReplicaSet, ClusterType.Sharded).Supports(Feature.Transactions); diff --git a/tests/MongoDB.Driver.Examples/UpdatePrimer.cs b/tests/MongoDB.Driver.Examples/UpdatePrimer.cs index 3fe3778115e..af423307167 100644 --- a/tests/MongoDB.Driver.Examples/UpdatePrimer.cs +++ b/tests/MongoDB.Driver.Examples/UpdatePrimer.cs @@ -23,7 +23,7 @@ namespace MongoDB.Driver.Examples { public class UpdatePrimer : PrimerTestFixture { - [SkippableFact] + [Fact] public async Task UpdateTopLevelFields() { RequireServer.Check(); @@ -74,7 +74,7 @@ public async Task UpdateEmbeddedField() // @end: update-embedded-field } - [SkippableFact] + [Fact] public async Task UpdateMultipleDocuments() { RequireServer.Check(); diff --git a/tests/MongoDB.Driver.GridFS.Tests/DelegatingStreamTests.cs b/tests/MongoDB.Driver.GridFS.Tests/DelegatingStreamTests.cs index dcd02487a56..2656fbb44c2 100644 --- a/tests/MongoDB.Driver.GridFS.Tests/DelegatingStreamTests.cs +++ b/tests/MongoDB.Driver.GridFS.Tests/DelegatingStreamTests.cs @@ -18,7 +18,7 @@ using System.Threading; using System.Threading.Tasks; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Moq; using Xunit; diff --git a/tests/MongoDB.Driver.GridFS.Tests/GridFSBucketOptionsTests.cs b/tests/MongoDB.Driver.GridFS.Tests/GridFSBucketOptionsTests.cs index 74d80659d7c..f8b68630834 100644 --- a/tests/MongoDB.Driver.GridFS.Tests/GridFSBucketOptionsTests.cs +++ b/tests/MongoDB.Driver.GridFS.Tests/GridFSBucketOptionsTests.cs @@ -19,7 +19,7 @@ using System.Text; using System.Threading.Tasks; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.GridFS.Tests diff --git a/tests/MongoDB.Driver.GridFS.Tests/GridFSBucketTests.cs b/tests/MongoDB.Driver.GridFS.Tests/GridFSBucketTests.cs index cad743c9580..36d0690b019 100644 --- a/tests/MongoDB.Driver.GridFS.Tests/GridFSBucketTests.cs +++ b/tests/MongoDB.Driver.GridFS.Tests/GridFSBucketTests.cs @@ -21,7 +21,7 @@ using System.Threading.Tasks; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; @@ -327,7 +327,7 @@ public void DownloadToStreamByName_should_throw_when_filename_is_null( action.ShouldThrow().And.ParamName.Should().Be("filename"); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Drop_should_drop_the_files_and_chunks_collections( [Values(false, true)] bool async) @@ -352,7 +352,7 @@ public void Drop_should_drop_the_files_and_chunks_collections( collectionNames.Should().NotContain("fs.chunks"); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Drop_should_throw_when_a_write_concern_error_occurss( [Values(false, true)] diff --git a/tests/MongoDB.Driver.GridFS.Tests/GridFSChunkExceptionTests.cs b/tests/MongoDB.Driver.GridFS.Tests/GridFSChunkExceptionTests.cs index eb83533c6f3..12c46e0d3af 100644 --- a/tests/MongoDB.Driver.GridFS.Tests/GridFSChunkExceptionTests.cs +++ b/tests/MongoDB.Driver.GridFS.Tests/GridFSChunkExceptionTests.cs @@ -65,9 +65,11 @@ public void Serialization_should_work() var formatter = new BinaryFormatter(); using (var stream = new MemoryStream()) { +#pragma warning disable SYSLIB0011 // BinaryFormatter serialization is obsolete formatter.Serialize(stream, subject); stream.Position = 0; var rehydrated = (GridFSChunkException)formatter.Deserialize(stream); +#pragma warning restore SYSLIB0011 // BinaryFormatter serialization is obsolete rehydrated.Message.Should().Be(subject.Message); } diff --git a/tests/MongoDB.Driver.GridFS.Tests/GridFSDownloadStreamBaseTests.cs b/tests/MongoDB.Driver.GridFS.Tests/GridFSDownloadStreamBaseTests.cs index 8eee31ef4a4..efce1a67b53 100644 --- a/tests/MongoDB.Driver.GridFS.Tests/GridFSDownloadStreamBaseTests.cs +++ b/tests/MongoDB.Driver.GridFS.Tests/GridFSDownloadStreamBaseTests.cs @@ -20,7 +20,7 @@ using System.Threading; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; @@ -220,7 +220,7 @@ public void FileInfo_should_return_expected_result() [Theory] [ParameterAttributeData] - public void Flush_should_throw( + public void Flush_should_not_throw( [Values(false, true)] bool async) { var bucket = CreateBucket(128); @@ -238,7 +238,7 @@ public void Flush_should_throw( action = () => subject.Flush(); } - action.ShouldThrow(); + action.ShouldNotThrow(); } [Fact] diff --git a/tests/MongoDB.Driver.GridFS.Tests/GridFSExceptionTests.cs b/tests/MongoDB.Driver.GridFS.Tests/GridFSExceptionTests.cs index 719af4335d7..7c6d6bbe8e9 100644 --- a/tests/MongoDB.Driver.GridFS.Tests/GridFSExceptionTests.cs +++ b/tests/MongoDB.Driver.GridFS.Tests/GridFSExceptionTests.cs @@ -49,9 +49,11 @@ public void Serialization_should_work() var formatter = new BinaryFormatter(); using (var stream = new MemoryStream()) { +#pragma warning disable SYSLIB0011 // BinaryFormatter serialization is obsolete formatter.Serialize(stream, subject); stream.Position = 0; var rehydrated = (GridFSException)formatter.Deserialize(stream); +#pragma warning restore SYSLIB0011 // BinaryFormatter serialization is obsolete rehydrated.Message.Should().Be(subject.Message); rehydrated.InnerException.Message.Should().Be(subject.InnerException.Message); diff --git a/tests/MongoDB.Driver.GridFS.Tests/GridFSFileInfoTests.cs b/tests/MongoDB.Driver.GridFS.Tests/GridFSFileInfoTests.cs index b09c32d921f..b8a28b31b51 100644 --- a/tests/MongoDB.Driver.GridFS.Tests/GridFSFileInfoTests.cs +++ b/tests/MongoDB.Driver.GridFS.Tests/GridFSFileInfoTests.cs @@ -22,7 +22,7 @@ using MongoDB.Bson; using MongoDB.Bson.IO; using MongoDB.Bson.Serialization; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.GridFS.Tests diff --git a/tests/MongoDB.Driver.GridFS.Tests/GridFSFileNotFoundExceptionTests.cs b/tests/MongoDB.Driver.GridFS.Tests/GridFSFileNotFoundExceptionTests.cs index ccb28074541..90ab9b27376 100644 --- a/tests/MongoDB.Driver.GridFS.Tests/GridFSFileNotFoundExceptionTests.cs +++ b/tests/MongoDB.Driver.GridFS.Tests/GridFSFileNotFoundExceptionTests.cs @@ -64,9 +64,11 @@ public void Serialization_should_work() var formatter = new BinaryFormatter(); using (var stream = new MemoryStream()) { +#pragma warning disable SYSLIB0011 // BinaryFormatter serialization is obsolete formatter.Serialize(stream, subject); stream.Position = 0; var rehydrated = (GridFSFileNotFoundException)formatter.Deserialize(stream); +#pragma warning restore SYSLIB0011 // BinaryFormatter serialization is obsolete rehydrated.Message.Should().Be(subject.Message); } diff --git a/tests/MongoDB.Driver.GridFS.Tests/GridFSFindOptionsTests.cs b/tests/MongoDB.Driver.GridFS.Tests/GridFSFindOptionsTests.cs index b8e3d3efe79..6532d091905 100644 --- a/tests/MongoDB.Driver.GridFS.Tests/GridFSFindOptionsTests.cs +++ b/tests/MongoDB.Driver.GridFS.Tests/GridFSFindOptionsTests.cs @@ -19,7 +19,7 @@ using System.Text; using System.Threading.Tasks; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.GridFS.Tests diff --git a/tests/MongoDB.Driver.GridFS.Tests/GridFSMD5ExceptionTests.cs b/tests/MongoDB.Driver.GridFS.Tests/GridFSMD5ExceptionTests.cs index e8486f02361..ed14d0b9ed7 100644 --- a/tests/MongoDB.Driver.GridFS.Tests/GridFSMD5ExceptionTests.cs +++ b/tests/MongoDB.Driver.GridFS.Tests/GridFSMD5ExceptionTests.cs @@ -47,9 +47,11 @@ public void Serialization_should_work() var formatter = new BinaryFormatter(); using (var stream = new MemoryStream()) { +#pragma warning disable SYSLIB0011 // BinaryFormatter serialization is obsolete formatter.Serialize(stream, subject); stream.Position = 0; var rehydrated = (GridFSMD5Exception)formatter.Deserialize(stream); +#pragma warning restore SYSLIB0011 // BinaryFormatter serialization is obsolete rehydrated.Message.Should().Be(subject.Message); } diff --git a/tests/MongoDB.Driver.GridFS.Tests/GridFSSeekableDownloadStreamTests.cs b/tests/MongoDB.Driver.GridFS.Tests/GridFSSeekableDownloadStreamTests.cs index cd6cd2e878e..379a4dfd955 100644 --- a/tests/MongoDB.Driver.GridFS.Tests/GridFSSeekableDownloadStreamTests.cs +++ b/tests/MongoDB.Driver.GridFS.Tests/GridFSSeekableDownloadStreamTests.cs @@ -20,7 +20,7 @@ using System.Threading; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; diff --git a/tests/MongoDB.Driver.GridFS.Tests/GridFSUploadOptionsTests.cs b/tests/MongoDB.Driver.GridFS.Tests/GridFSUploadOptionsTests.cs index 485819ed9e7..4a6fa3d46d2 100644 --- a/tests/MongoDB.Driver.GridFS.Tests/GridFSUploadOptionsTests.cs +++ b/tests/MongoDB.Driver.GridFS.Tests/GridFSUploadOptionsTests.cs @@ -20,7 +20,7 @@ using System.Threading.Tasks; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.GridFS.Tests diff --git a/tests/MongoDB.Driver.GridFS.Tests/GridFSUploadStreamTests.cs b/tests/MongoDB.Driver.GridFS.Tests/GridFSUploadStreamTests.cs index 1f3cbca7e46..ce063af2048 100644 --- a/tests/MongoDB.Driver.GridFS.Tests/GridFSUploadStreamTests.cs +++ b/tests/MongoDB.Driver.GridFS.Tests/GridFSUploadStreamTests.cs @@ -17,7 +17,7 @@ using System.IO; using System.Threading; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Tests; using Xunit; diff --git a/tests/MongoDB.Driver.GridFS.Tests/MongoDB.Driver.GridFS.Tests.csproj b/tests/MongoDB.Driver.GridFS.Tests/MongoDB.Driver.GridFS.Tests.csproj index af69688f67f..a63cc23f8c7 100644 --- a/tests/MongoDB.Driver.GridFS.Tests/MongoDB.Driver.GridFS.Tests.csproj +++ b/tests/MongoDB.Driver.GridFS.Tests/MongoDB.Driver.GridFS.Tests.csproj @@ -1,34 +1,16 @@ - - true - + - netcoreapp2.1;netcoreapp3.1;net472 - netcoreapp2.1;netcoreapp3.1 - 9 - true - - false ..\..\MongoDBTest.ruleset MongoDB.Driver.GridFS.Tests MongoDB.Driver.GridFS.Tests - MongoDB Inc. - Copyright © 2010-present MongoDB Inc. MongoDB.Driver.GridFS tests. - - 0.0.0-local - - - - TRACE - - 1701;1702; @@ -37,17 +19,12 @@ - + + - - - - - - - + @@ -61,11 +38,7 @@ - - - - - + diff --git a/tests/MongoDB.Driver.GridFS.Tests/Properties/AssemblyInfo.cs b/tests/MongoDB.Driver.GridFS.Tests/Properties/AssemblyInfo.cs index b9c458de29c..35a2ab489b9 100644 --- a/tests/MongoDB.Driver.GridFS.Tests/Properties/AssemblyInfo.cs +++ b/tests/MongoDB.Driver.GridFS.Tests/Properties/AssemblyInfo.cs @@ -14,11 +14,11 @@ */ using System.Runtime.InteropServices; -using MongoDB.Driver.Core.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; [assembly: ComVisible(false)] [assembly: CollectionBehavior(DisableTestParallelization = true)] -[assembly: TestFramework(XunitExtensionsConsts.TimeoutEnforcingXunitFramework, XunitExtensionsConsts.TimeoutEnforcingFrameworkAssembly)] +[assembly: TestFramework(XunitExtensionsConstants.TimeoutEnforcingXunitFramework, XunitExtensionsConstants.TimeoutEnforcingFrameworkAssembly)] diff --git a/tests/MongoDB.Driver.GridFS.Tests/Specifications/gridfs/GridFSTestRunner.cs b/tests/MongoDB.Driver.GridFS.Tests/Specifications/gridfs/GridFSTestRunner.cs index ff25181e5d7..481259cc4ca 100644 --- a/tests/MongoDB.Driver.GridFS.Tests/Specifications/gridfs/GridFSTestRunner.cs +++ b/tests/MongoDB.Driver.GridFS.Tests/Specifications/gridfs/GridFSTestRunner.cs @@ -18,17 +18,16 @@ using System.IO; using System.Linq; using System.Reflection; -using System.Threading.Tasks; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; using MongoDB.Driver.Tests; using Xunit; +using Xunit.Sdk; namespace MongoDB.Driver.GridFS.Tests.Specifications.gridfs { public class GridFSTestRunner { - [SkippableTheory] + [Theory] [ClassData(typeof(TestCaseSource))] [Trait("Category", "Specifications_gridfs")] public void RunTest(BsonDocument data, BsonDocument testDefinition) diff --git a/tests/MongoDB.Driver.GridFS.Tests/TargetFrameworkTests.cs b/tests/MongoDB.Driver.GridFS.Tests/TargetFrameworkTests.cs index bb924c853f6..a0b66e11af5 100644 --- a/tests/MongoDB.Driver.GridFS.Tests/TargetFrameworkTests.cs +++ b/tests/MongoDB.Driver.GridFS.Tests/TargetFrameworkTests.cs @@ -23,7 +23,7 @@ public class TargetFrameworkTests [Fact] public void TargetFramework_should_be_valid() { - var actualFramework = MongoDB.Driver.GridFS.TargetFramework.Moniker; + var actualFramework = TargetFramework.Moniker; var expectedFramework = GetExpectedTargetFramework(); actualFramework.Should().Be(expectedFramework); } @@ -33,7 +33,7 @@ private string GetExpectedTargetFramework() { #if NETCOREAPP2_1 return "netstandard20"; -#elif NETCOREAPP3_1 +#elif NETCOREAPP3_1_OR_GREATER return "netstandard21"; #elif NET472 return "net472"; diff --git a/tests/MongoDB.Driver.Legacy.TestHelpers/MongoDB.Driver.Legacy.TestHelpers.csproj b/tests/MongoDB.Driver.Legacy.TestHelpers/MongoDB.Driver.Legacy.TestHelpers.csproj index 1d7b5d0aa17..c4b3163dd11 100644 --- a/tests/MongoDB.Driver.Legacy.TestHelpers/MongoDB.Driver.Legacy.TestHelpers.csproj +++ b/tests/MongoDB.Driver.Legacy.TestHelpers/MongoDB.Driver.Legacy.TestHelpers.csproj @@ -1,44 +1,21 @@ - - true - + - netstandard2.0;netstandard2.1;net472 - netstandard2.0;netstandard2.1 - 7.3 - true - - false + $(StandardTargetFrameworks) ..\..\MongoDBLegacyTest.ruleset MongoDB.Driver.Legacy.TestHelpers MongoDB.Driver.Legacy.TestHelpers - MongoDB Inc. - Copyright © 2010-present MongoDB Inc. MongoDB.Driver.Legacy test helpers. - - 0.0.0-local - - - - TRACE - - - - - - - - diff --git a/tests/MongoDB.Driver.Legacy.Tests/Builders/IndexKeysBuilderTests.cs b/tests/MongoDB.Driver.Legacy.Tests/Builders/IndexKeysBuilderTests.cs index 82e28798e05..89a1a36136d 100644 --- a/tests/MongoDB.Driver.Legacy.Tests/Builders/IndexKeysBuilderTests.cs +++ b/tests/MongoDB.Driver.Legacy.Tests/Builders/IndexKeysBuilderTests.cs @@ -37,7 +37,7 @@ public IndexKeysBuilderTests() _primary = _server.Primary; } - [SkippableFact] + [Fact] public void CreateIndex_with_wildcard_index_should_create_expected_index() { RequireServer.Check().Supports(Feature.WildcardIndexes); @@ -54,7 +54,7 @@ public void CreateIndex_with_wildcard_index_should_create_expected_index() index["key"]["a.$**"].AsInt32.Should().Be(1); } - [SkippableFact] + [Fact] public void CreateIndex_with_wildcardProjection_should_create_expected_index() { RequireServer.Check().Supports(Feature.WildcardIndexes); @@ -70,7 +70,8 @@ public void CreateIndex_with_wildcardProjection_should_create_expected_index() var index = indexes.RawDocuments.Single(i => i["name"].AsString == "custom"); index["key"]["$**"].AsInt32.Should().Be(1); - if (CoreTestConfiguration.ServerVersion >= new SemanticVersion(4, 5, 0, "")) + var serverVersion = CoreTestConfiguration.ServerVersion; + if (serverVersion >= new SemanticVersion(4, 5, 0, "") && serverVersion <= new SemanticVersion(6, 2, 0, "")) { index["wildcardProjection"].Should().Be(BsonDocument.Parse("{ _id : true }")); } @@ -248,7 +249,7 @@ public void TestTextCombination() Assert.Equal(expected, key.ToJson()); } - [SkippableFact] + [Fact] public void TestTextIndexCreation() { RequireServer.Check().VersionGreaterThanOrEqualTo("2.6.0").ClusterTypes(ClusterType.Standalone, ClusterType.ReplicaSet); diff --git a/tests/MongoDB.Driver.Legacy.Tests/Builders/IndexKeysBuilderTypedTests.cs b/tests/MongoDB.Driver.Legacy.Tests/Builders/IndexKeysBuilderTypedTests.cs index 0e8b3af14ee..02290882c7a 100644 --- a/tests/MongoDB.Driver.Legacy.Tests/Builders/IndexKeysBuilderTypedTests.cs +++ b/tests/MongoDB.Driver.Legacy.Tests/Builders/IndexKeysBuilderTypedTests.cs @@ -60,7 +60,7 @@ private class Test public int Id { get; set; } } - [SkippableFact] + [Fact] public void CreateIndex_with_wildcard_index_should_create_expected_index() { RequireServer.Check().Supports(Feature.WildcardIndexes); @@ -74,7 +74,7 @@ public void CreateIndex_with_wildcard_index_should_create_expected_index() index["key"]["a.$**"].AsInt32.Should().Be(1); } - [SkippableFact] + [Fact] public void CreateIndex_with_wildcardProjection_should_create_expected_index() { RequireServer.Check().Supports(Feature.WildcardIndexes); @@ -89,7 +89,8 @@ public void CreateIndex_with_wildcardProjection_should_create_expected_index() var indexes = collection.GetIndexes(); var index = indexes.RawDocuments.Single(i => i["name"].AsString == "custom"); index["key"]["$**"].AsInt32.Should().Be(1); - if (CoreTestConfiguration.ServerVersion >= new SemanticVersion(4, 5, 0, "")) + var serverVersion = CoreTestConfiguration.ServerVersion; + if (serverVersion >= new SemanticVersion(4, 5, 0, "") && serverVersion <= new SemanticVersion(6, 2, 0, "")) { index["wildcardProjection"].Should().Be(BsonDocument.Parse("{ b : true, _id : false }")); } @@ -298,7 +299,7 @@ public void TestTextArrayNonArrayFields2() Assert.Equal(expected, keys.ToJson()); } - [SkippableFact] + [Fact] public void TestTextIndexCreation() { RequireServer.Check().VersionGreaterThanOrEqualTo("2.6.0").ClusterTypes(ClusterType.Standalone, ClusterType.ReplicaSet); diff --git a/tests/MongoDB.Driver.Legacy.Tests/ClientDocumentHelperTests.cs b/tests/MongoDB.Driver.Legacy.Tests/ClientDocumentHelperTests.cs index 13399a73803..36ac58c16e2 100644 --- a/tests/MongoDB.Driver.Legacy.Tests/ClientDocumentHelperTests.cs +++ b/tests/MongoDB.Driver.Legacy.Tests/ClientDocumentHelperTests.cs @@ -16,7 +16,7 @@ using System.Linq; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Connections; using Xunit; diff --git a/tests/MongoDB.Driver.Legacy.Tests/CommandResults/CollectionStatsResultTests.cs b/tests/MongoDB.Driver.Legacy.Tests/CommandResults/CollectionStatsResultTests.cs index 9aa38a69fb6..f5b81dada13 100644 --- a/tests/MongoDB.Driver.Legacy.Tests/CommandResults/CollectionStatsResultTests.cs +++ b/tests/MongoDB.Driver.Legacy.Tests/CommandResults/CollectionStatsResultTests.cs @@ -34,7 +34,7 @@ public CollectionStatsResultTests() _collection = LegacyTestConfiguration.Collection; } - [SkippableFact] + [Fact] public void Test() { RequireServer.Check().ClusterTypes(ClusterType.Standalone, ClusterType.ReplicaSet).StorageEngine("mmapv1"); diff --git a/tests/MongoDB.Driver.Legacy.Tests/CommandResults/DatabaseStatsResultTests.cs b/tests/MongoDB.Driver.Legacy.Tests/CommandResults/DatabaseStatsResultTests.cs index e0dfa1afddd..efc8a588e79 100644 --- a/tests/MongoDB.Driver.Legacy.Tests/CommandResults/DatabaseStatsResultTests.cs +++ b/tests/MongoDB.Driver.Legacy.Tests/CommandResults/DatabaseStatsResultTests.cs @@ -14,7 +14,7 @@ */ using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver; using MongoDB.Driver.Core; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; @@ -35,7 +35,7 @@ public DatabaseStatsResultTests() _collection = LegacyTestConfiguration.Collection; } - [SkippableFact] + [Fact] public void Test() { RequireServer.Check().StorageEngine("mmapv1"); diff --git a/tests/MongoDB.Driver.Legacy.Tests/DefaultLegacyOperationExecutorTests.cs b/tests/MongoDB.Driver.Legacy.Tests/DefaultLegacyOperationExecutorTests.cs index ba7b6c94fe8..27a6335c258 100644 --- a/tests/MongoDB.Driver.Legacy.Tests/DefaultLegacyOperationExecutorTests.cs +++ b/tests/MongoDB.Driver.Legacy.Tests/DefaultLegacyOperationExecutorTests.cs @@ -16,7 +16,7 @@ using System.Threading; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Operations; using Moq; diff --git a/tests/MongoDB.Driver.Legacy.Tests/GridFS/MongoGridFSExceptionTests.cs b/tests/MongoDB.Driver.Legacy.Tests/GridFS/MongoGridFSExceptionTests.cs index 9d7a82442a6..a292bbe1b8d 100644 --- a/tests/MongoDB.Driver.Legacy.Tests/GridFS/MongoGridFSExceptionTests.cs +++ b/tests/MongoDB.Driver.Legacy.Tests/GridFS/MongoGridFSExceptionTests.cs @@ -60,9 +60,11 @@ public void Serialization_should_work() var formatter = new BinaryFormatter(); using (var stream = new MemoryStream()) { +#pragma warning disable SYSLIB0011 // BinaryFormatter serialization is obsolete formatter.Serialize(stream, subject); stream.Position = 0; var rehydrated = (MongoGridFSException)formatter.Deserialize(stream); +#pragma warning restore SYSLIB0011 // BinaryFormatter serialization is obsolete rehydrated.ConnectionId.Should().Be(subject.ConnectionId); rehydrated.Message.Should().Be(subject.Message); diff --git a/tests/MongoDB.Driver.Legacy.Tests/Jira/CSharp112Tests.cs b/tests/MongoDB.Driver.Legacy.Tests/Jira/CSharp112Tests.cs index d14d31c7085..363cfa64dca 100644 --- a/tests/MongoDB.Driver.Legacy.Tests/Jira/CSharp112Tests.cs +++ b/tests/MongoDB.Driver.Legacy.Tests/Jira/CSharp112Tests.cs @@ -93,8 +93,7 @@ public void TestDeserializeDouble() { 0x7eeeeeeeeeeeeeee, 0xfeeeeeeeeeeeeeee, - Int64.MinValue + 1, // need some low order bits to see data loss - Int64.MaxValue + Int64.MinValue + 1 // need some low order bits to see data loss }; for (int i = 0; i < values.Length; i++) { diff --git a/tests/MongoDB.Driver.Legacy.Tests/Jira/CSharp140Tests.cs b/tests/MongoDB.Driver.Legacy.Tests/Jira/CSharp140Tests.cs index 44f003d6e6e..a1b983f07a3 100644 --- a/tests/MongoDB.Driver.Legacy.Tests/Jira/CSharp140Tests.cs +++ b/tests/MongoDB.Driver.Legacy.Tests/Jira/CSharp140Tests.cs @@ -14,12 +14,14 @@ */ using MongoDB.Bson; +using MongoDB.Bson.TestHelpers; using MongoDB.Driver.Builders; using MongoDB.Driver.Wrappers; using Xunit; namespace MongoDB.Driver.Tests.Jira.CSharp140 { + [Collection(RegisterObjectSerializerFixture.CollectionName)] public class CSharp140Tests { private class C diff --git a/tests/MongoDB.Driver.Legacy.Tests/Jira/CSharp216Tests.cs b/tests/MongoDB.Driver.Legacy.Tests/Jira/CSharp216Tests.cs index 2f6bb0a325b..ebd20190675 100644 --- a/tests/MongoDB.Driver.Legacy.Tests/Jira/CSharp216Tests.cs +++ b/tests/MongoDB.Driver.Legacy.Tests/Jira/CSharp216Tests.cs @@ -15,7 +15,7 @@ using System.Linq; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; @@ -32,7 +32,7 @@ public CSharp216Tests() _adminDatabase = LegacyTestConfiguration.Server.GetDatabase("admin"); } - [SkippableFact] + [Fact] public void TestAmbiguousEvalArguments() { RequireServer.Check().Supports(Feature.Eval); @@ -51,7 +51,7 @@ public void TestAmbiguousEvalArguments() #pragma warning restore } - [SkippableFact] + [Fact] public void TestNoLock() { RequireServer.Check().Supports(Feature.Eval); diff --git a/tests/MongoDB.Driver.Legacy.Tests/Jira/CSharp218Tests.cs b/tests/MongoDB.Driver.Legacy.Tests/Jira/CSharp218Tests.cs index 8d495c10dcc..0ca763a69c5 100644 --- a/tests/MongoDB.Driver.Legacy.Tests/Jira/CSharp218Tests.cs +++ b/tests/MongoDB.Driver.Legacy.Tests/Jira/CSharp218Tests.cs @@ -49,7 +49,7 @@ public CSharp218Tests() } [Fact] - public void TestDeserializeClassWithStructPropertyFails() + public void TestDeserializeClassWithStructPropertyWithoutConstructorFails() { _collection.RemoveAll(); var c = new C { Id = ObjectId.GenerateNewId(), P = new P { X = 1, Y = 2 } }; @@ -61,7 +61,7 @@ public void TestDeserializeClassWithStructPropertyFails() } catch (Exception ex) { - var expectedMessage = "An error occurred while deserializing the P field of class MongoDB.Driver.Tests.Jira.CSharp218.CSharp218Tests+C: Value class MongoDB.Driver.Tests.Jira.CSharp218.CSharp218Tests+P cannot be deserialized."; + var expectedMessage = "An error occurred while deserializing the P field of class MongoDB.Driver.Tests.Jira.CSharp218.CSharp218Tests+C: Value class MongoDB.Driver.Tests.Jira.CSharp218.CSharp218Tests+P cannot be deserialized without a constructor."; Assert.IsType(ex); Assert.IsType(ex.InnerException); Assert.Equal(expectedMessage, ex.Message); diff --git a/tests/MongoDB.Driver.Legacy.Tests/Jira/CSharp247Tests.cs b/tests/MongoDB.Driver.Legacy.Tests/Jira/CSharp247Tests.cs index db2fbbe018c..7a7b482f7a4 100644 --- a/tests/MongoDB.Driver.Legacy.Tests/Jira/CSharp247Tests.cs +++ b/tests/MongoDB.Driver.Legacy.Tests/Jira/CSharp247Tests.cs @@ -14,11 +14,12 @@ */ using MongoDB.Bson; -using MongoDB.Driver; +using MongoDB.Bson.TestHelpers; using Xunit; namespace MongoDB.Driver.Tests.Jira.CSharp247 { + [Collection(RegisterObjectSerializerFixture.CollectionName)] public class CSharp247Tests { public interface I diff --git a/tests/MongoDB.Driver.Legacy.Tests/Jira/CSharp269Tests.cs b/tests/MongoDB.Driver.Legacy.Tests/Jira/CSharp269Tests.cs index af706f43e90..89ceac2f059 100644 --- a/tests/MongoDB.Driver.Legacy.Tests/Jira/CSharp269Tests.cs +++ b/tests/MongoDB.Driver.Legacy.Tests/Jira/CSharp269Tests.cs @@ -43,7 +43,7 @@ public CSharp269Tests() _database.GridFS.Chunks.Drop(); } - [SkippableFact] + [Fact] public void TestUploadAndDownload() { RequireServer.Check().ClusterTypes(Core.Clusters.ClusterType.Standalone, Core.Clusters.ClusterType.ReplicaSet); diff --git a/tests/MongoDB.Driver.Legacy.Tests/Jira/CSharp365Tests.cs b/tests/MongoDB.Driver.Legacy.Tests/Jira/CSharp365Tests.cs index 01d4b1eb0c2..031b3f95cd9 100644 --- a/tests/MongoDB.Driver.Legacy.Tests/Jira/CSharp365Tests.cs +++ b/tests/MongoDB.Driver.Legacy.Tests/Jira/CSharp365Tests.cs @@ -23,7 +23,7 @@ namespace MongoDB.Driver.Tests.Jira.CSharp365 { public class CSharp365Tests { - [SkippableFact] + [Fact] public void TestExplainWithFieldsAndCoveredIndex() { RequireServer.Check().Supports(Feature.LegacyWireProtocol); diff --git a/tests/MongoDB.Driver.Legacy.Tests/Jira/CSharp958Tests.cs b/tests/MongoDB.Driver.Legacy.Tests/Jira/CSharp958Tests.cs index 5e53d73a7df..a02e1f5d77b 100644 --- a/tests/MongoDB.Driver.Legacy.Tests/Jira/CSharp958Tests.cs +++ b/tests/MongoDB.Driver.Legacy.Tests/Jira/CSharp958Tests.cs @@ -13,14 +13,13 @@ * limitations under the License. */ -using System; -using System.Linq; using MongoDB.Bson; -using MongoDB.Driver; +using MongoDB.Bson.TestHelpers; using Xunit; namespace MongoDB.Driver.Tests.Jira { + [Collection(RegisterObjectSerializerFixture.CollectionName)] public class CSharp958Tests { private interface IPerson { } diff --git a/tests/MongoDB.Driver.Legacy.Tests/Linq/BsonDocumentBackedClassSerializerTests.cs b/tests/MongoDB.Driver.Legacy.Tests/Linq/BsonDocumentBackedClassSerializerTests.cs index 386d84dbb3c..9ffc6e09498 100644 --- a/tests/MongoDB.Driver.Legacy.Tests/Linq/BsonDocumentBackedClassSerializerTests.cs +++ b/tests/MongoDB.Driver.Legacy.Tests/Linq/BsonDocumentBackedClassSerializerTests.cs @@ -21,7 +21,7 @@ using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.Serialization.Options; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Builders; using Xunit; diff --git a/tests/MongoDB.Driver.Legacy.Tests/Linq/ExplainTests.cs b/tests/MongoDB.Driver.Legacy.Tests/Linq/ExplainTests.cs index 062ddced971..9f4287b11cd 100644 --- a/tests/MongoDB.Driver.Legacy.Tests/Linq/ExplainTests.cs +++ b/tests/MongoDB.Driver.Legacy.Tests/Linq/ExplainTests.cs @@ -42,7 +42,7 @@ public ExplainTests() _collection = LegacyTestConfiguration.Collection; } - [SkippableFact] + [Fact] public void TestExplainFromLinqQueryEqualsExplainFromCursor() { RequireServer.Check().Supports(Feature.LegacyWireProtocol); @@ -64,7 +64,7 @@ public void TestExplainFromLinqQueryEqualsExplainFromCursor() } } - [SkippableFact] + [Fact] public void TestVerboseExplainFromLinqQueryEqualsVerboseExplainFromCursor() { RequireServer.Check().Supports(Feature.LegacyWireProtocol); diff --git a/tests/MongoDB.Driver.Legacy.Tests/Linq/SelectNullableTests.cs b/tests/MongoDB.Driver.Legacy.Tests/Linq/SelectNullableTests.cs index 6911aea7b69..611e1e0b96a 100644 --- a/tests/MongoDB.Driver.Legacy.Tests/Linq/SelectNullableTests.cs +++ b/tests/MongoDB.Driver.Legacy.Tests/Linq/SelectNullableTests.cs @@ -17,7 +17,7 @@ using System.Linq; using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver; using MongoDB.Driver.Linq; using Xunit; @@ -61,7 +61,7 @@ private static bool OneTimeSetup() return true; } - [SkippableFact] + [Fact] public void TestWhereEEqualsA() { RequireEnvironment.Check().EnvironmentVariable("MONO"); // Does not pass on Mono 3.2.5. Excluding for now. diff --git a/tests/MongoDB.Driver.Legacy.Tests/Linq/SelectOfTypeHierarchicalTests.cs b/tests/MongoDB.Driver.Legacy.Tests/Linq/SelectOfTypeHierarchicalTests.cs index 10947efab8d..e225d24fde7 100644 --- a/tests/MongoDB.Driver.Legacy.Tests/Linq/SelectOfTypeHierarchicalTests.cs +++ b/tests/MongoDB.Driver.Legacy.Tests/Linq/SelectOfTypeHierarchicalTests.cs @@ -250,7 +250,7 @@ where b is D Assert.Equal(1, Consume(query)); } - [SkippableFact] + [Fact] public void TestWhereBTypeEqualsB() { RequireServer.Check().VersionGreaterThanOrEqualTo("2.0.0"); diff --git a/tests/MongoDB.Driver.Legacy.Tests/Linq/SelectQueryTests.cs b/tests/MongoDB.Driver.Legacy.Tests/Linq/SelectQueryTests.cs index 59c31a82aec..eb865b5dc0b 100644 --- a/tests/MongoDB.Driver.Legacy.Tests/Linq/SelectQueryTests.cs +++ b/tests/MongoDB.Driver.Legacy.Tests/Linq/SelectQueryTests.cs @@ -4456,7 +4456,7 @@ where c.SA[0].Contains("o") Assert.Equal(1, Consume(query)); } - [SkippableFact] + [Fact] public void TestWhereSASub0ContainsONot() { var query = from c in __collection.AsQueryable() @@ -4502,7 +4502,7 @@ where c.SA[0].EndsWith("m") Assert.Equal(1, Consume(query)); } - [SkippableFact] + [Fact] public void TestWhereSASub0EndsWithMNot() { var query = from c in __collection.AsQueryable() @@ -4549,7 +4549,7 @@ where regex.IsMatch(c.SA[0]) Assert.Equal(1, Consume(query)); } - [SkippableFact] + [Fact] public void TestWhereSASub0IsMatchNot() { var regex = new Regex(@"^T"); @@ -4596,7 +4596,7 @@ where Regex.IsMatch(c.SA[0], "^T") Assert.Equal(1, Consume(query)); } - [SkippableFact] + [Fact] public void TestWhereSASub0IsMatchStaticNot() { var query = from c in __collection.AsQueryable() @@ -4665,7 +4665,7 @@ where c.SA[0].StartsWith("T") Assert.Equal(1, Consume(query)); } - [SkippableFact] + [Fact] public void TestWhereSASub0StartsWithTNot() { var query = from c in __collection.AsQueryable() diff --git a/tests/MongoDB.Driver.Legacy.Tests/Linq/WithIndexTests.cs b/tests/MongoDB.Driver.Legacy.Tests/Linq/WithIndexTests.cs index eed0c3c4b9d..8aafe399ff5 100644 --- a/tests/MongoDB.Driver.Legacy.Tests/Linq/WithIndexTests.cs +++ b/tests/MongoDB.Driver.Legacy.Tests/Linq/WithIndexTests.cs @@ -106,7 +106,7 @@ public void TestQueryWithIndexBeforeConditionHasIndexNameHint() Assert.Equal("{ \"a\" : 1, \"b\" : 3 }", selectQuery.BuildQuery().ToJson()); } - [SkippableFact] + [Fact] public void TestIndexNameHintIsUsedInQuery() { RequireServer.Check().Supports(Feature.LegacyWireProtocol); @@ -184,7 +184,7 @@ public void TestQueryWithIndexBeforeConditionHasIndexDocumentHint() Assert.Equal("{ \"a\" : 1, \"b\" : 3 }", selectQuery.BuildQuery().ToJson()); } - [SkippableFact] + [Fact] public void TestIndexDocumentHintIsUsedInQuery() { RequireServer.Check().Supports(Feature.LegacyWireProtocol); diff --git a/tests/MongoDB.Driver.Legacy.Tests/MongoCollectionTests.cs b/tests/MongoDB.Driver.Legacy.Tests/MongoCollectionTests.cs index 9830a72b8b0..fb2dde4aad7 100644 --- a/tests/MongoDB.Driver.Legacy.Tests/MongoCollectionTests.cs +++ b/tests/MongoDB.Driver.Legacy.Tests/MongoCollectionTests.cs @@ -26,7 +26,7 @@ using MongoDB.Driver.GeoJsonObjectModel; using FluentAssertions; using Xunit; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Misc; @@ -140,7 +140,7 @@ public void TestAggregateCursor() Assert.Equal(2, dictionary[3]); } - [SkippableFact] + [Fact] public void TestAggregateExplain() { RequireServer.Check().Supports(Feature.LegacyWireProtocol); @@ -184,7 +184,7 @@ public void TestAggregateMaxTime() } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void TestAggregateOutputToCollection( [Values("$out", "$merge")] string lastStageName, @@ -259,7 +259,7 @@ public void TestAggregateOutputToCollection( Assert.Equal(2, dictionary[3]); } - [SkippableFact] + [Fact] public void TestAggregateWriteConcern() { RequireServer.Check().ClusterType(ClusterType.ReplicaSet); @@ -441,7 +441,7 @@ public void TestCountZero() Assert.Equal(0, count); } - [SkippableFact] + [Fact] public void TestCountUsesImplicitSession() { RequireServer.Check(); @@ -484,7 +484,7 @@ public void TestCountWithMaxTime() } } - [SkippableFact] + [Fact] public void TestCountWithMaxTimeFromFind() { RequireServer.Check(); @@ -509,7 +509,7 @@ public void TestCountWithQuery() Assert.Equal(1, count); } - [SkippableFact] + [Fact] public void TestCountWithReadPreferenceFromFind() { RequireServer.Check().ClusterType(ClusterType.ReplicaSet); @@ -525,7 +525,7 @@ public void TestCountWithReadPreferenceFromFind() } } - [SkippableFact] + [Fact] public void TestCountWithHint() { RequireServer.Check(); @@ -542,7 +542,7 @@ public void TestCountWithHint() Assert.Equal(1, count); } - [SkippableFact] + [Fact] public void TestCountWithHintFromFind() { RequireServer.Check(); @@ -554,7 +554,7 @@ public void TestCountWithHintFromFind() Assert.Equal(1, count); } - [SkippableFact] + [Fact] public void TestCountWithHintAndLimitFromFind() { RequireServer.Check(); @@ -576,7 +576,7 @@ public void TestCreateCollection() Assert.True(collection.Exists()); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void TestCreateCollectionSetAutoIndexId( [Values(false, true)] @@ -596,7 +596,7 @@ public void TestCreateCollectionSetAutoIndexId( Assert.Equal(expectedIndexCount, indexCount); } - [SkippableFact] + [Fact] public void TestCreateCollectionSetCappedSetMaxDocuments() { RequireServer.Check().ClusterTypes(ClusterType.Standalone, ClusterType.ReplicaSet).StorageEngine("mmapv1"); @@ -612,7 +612,7 @@ public void TestCreateCollectionSetCappedSetMaxDocuments() Assert.True(stats.MaxDocuments == 1000); } - [SkippableFact] + [Fact] public void TestCreateCollectionSetCappedSetMaxSize() { RequireServer.Check().ClusterTypes(ClusterType.Standalone, ClusterType.ReplicaSet).StorageEngine("mmapv1"); @@ -627,7 +627,7 @@ public void TestCreateCollectionSetCappedSetMaxSize() Assert.True(stats.StorageSize >= 10000); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void TestCreateCollectionSetNoPadding( [Values(false, true)] @@ -648,7 +648,7 @@ public void TestCreateCollectionSetNoPadding( Assert.Equal(userFlags, stats.UserFlags); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void TestCreateCollectionSetUsePowerOf2Sizes( [Values(false, true)] @@ -764,7 +764,7 @@ void AssertNamespace(IndexInfo indexInfo) Assert.True(indexes[1].Version >= 0); } - [SkippableFact] + [Fact] public void TestCreateIndexWithStorageEngine() { RequireServer.Check().StorageEngine("wiredTiger"); @@ -780,7 +780,7 @@ public void TestCreateIndexWithStorageEngine() Assert.Equal(2, result.Count); } - [SkippableFact] + [Fact] public void TestCreateIndexWriteConcern() { RequireServer.Check().ClusterType(ClusterType.ReplicaSet); @@ -917,7 +917,7 @@ public void TestDropIndex() _collection.IndexExistsByName("x_1").Should().BeFalse(); } - [SkippableFact] + [Fact] public void TestDropIndexWriteConcern() { RequireServer.Check().ClusterType(ClusterType.ReplicaSet); @@ -949,7 +949,7 @@ public void TestCreateIndexTimeToLive() Assert.Equal(TimeSpan.FromHours(1), indexes[1].TimeToLive); } - [SkippableFact] + [Fact] public void TestExplain() { RequireServer.Check().Supports(Feature.LegacyWireProtocol); @@ -1098,7 +1098,7 @@ public void TestFindAndModifyUpsert() Assert.Equal(1, result.ModifiedDocument["count"].AsInt32); } - [SkippableFact] + [Fact] public void TestFindAndModifyReplaceWithWriteConcernError() { RequireServer.Check().ClusterType(ClusterType.ReplicaSet); @@ -1125,7 +1125,7 @@ public void TestFindAndModifyReplaceWithWriteConcernError() modifiedDocument.Should().Be("{ _id : 1, x : 2 }"); } - [SkippableFact] + [Fact] public void TestFindAndModifyUpdateWithWriteConcernError() { RequireServer.Check().ClusterType(ClusterType.ReplicaSet); @@ -1248,7 +1248,7 @@ public void TestFindAndRemoveWithMaxTime() } } - [SkippableFact] + [Fact] public void TestFindAndRemoveWithWriteConcernError() { RequireServer.Check().ClusterType(ClusterType.ReplicaSet); @@ -1547,7 +1547,7 @@ public void TestFindWithinRectangle() // note: the hits are unordered } - [SkippableFact] + [Fact] public void TestFindWithMaxScan() { RequireServer.Check().VersionLessThan("4.1.0-"); @@ -1588,7 +1588,7 @@ private class Place } #pragma warning restore - [SkippableFact] + [Fact] public void TestGeoHaystackSearch() { RequireServer.Check().VersionLessThan("4.8.0"); @@ -1625,7 +1625,7 @@ public void TestGeoHaystackSearch() } } - [SkippableFact] + [Fact] public void TestGeoHaystackSearchWithMaxTime() { RequireServer.Check().VersionLessThan("4.8.0"); @@ -1659,7 +1659,7 @@ public void TestGeoHaystackSearchWithMaxTime() } } - [SkippableFact] + [Fact] public void TestGeoHaystackSearch_Typed() { RequireServer.Check().VersionLessThan("4.8.0"); @@ -1695,7 +1695,7 @@ public void TestGeoHaystackSearch_Typed() } } - [SkippableFact] + [Fact] public void TestGeoNear() { RequireServer.Check().Supports(Feature.GeoNearCommand); @@ -1742,7 +1742,7 @@ public void TestGeoNear() Assert.Equal("Coffee", place.Type); } - [SkippableFact] + [Fact] public void TestGeoNearGeneric() { RequireServer.Check().Supports(Feature.GeoNearCommand); @@ -1789,7 +1789,7 @@ public void TestGeoNearGeneric() Assert.Equal("Coffee", place.Type); } - [SkippableFact] + [Fact] public void TestGeoNearSphericalFalse() { RequireServer.Check().Supports(Feature.GeoNearCommand); @@ -1845,7 +1845,7 @@ public void TestGeoNearSphericalFalse() Assert.Equal("Coffee", hit2.RawDocument["Type"].AsString); } - [SkippableFact] + [Fact] public void TestGeoNearSphericalTrue() { RequireServer.Check().Supports(Feature.GeoNearCommand); @@ -1901,7 +1901,7 @@ public void TestGeoNearSphericalTrue() Assert.Equal("Coffee", hit2.RawDocument["Type"].AsString); } - [SkippableFact] + [Fact] public void TestGeoNearWithGeoJsonPoints() { RequireServer.Check().Supports(Feature.GeoNearCommand); @@ -1939,7 +1939,7 @@ public void TestGeoNearWithGeoJsonPoints() Assert.Equal("Coffee", hit2.Type); } - [SkippableFact] + [Fact] public void TestGeoNearWithMaxTime() { RequireServer.Check().Supports(Feature.GeoNearCommand); @@ -2032,7 +2032,7 @@ public void TestGetMore() _collection.FindAll().ToList(); } - [SkippableFact] + [Fact] public void TestGroupWithFinalizeFunction() { RequireServer.Check().Supports(Feature.GroupCommand); @@ -2062,7 +2062,7 @@ public void TestGroupWithFinalizeFunction() Assert.Equal(-3, results[2]["count"].ToInt32()); } - [SkippableFact] + [Fact] public void TestGroupWithKeyFields() { RequireServer.Check().Supports(Feature.GroupCommand); @@ -2091,7 +2091,7 @@ public void TestGroupWithKeyFields() Assert.Equal(3, results[2]["count"].ToInt32()); } - [SkippableFact] + [Fact] public void TestGroupWithKeyFunction() { RequireServer.Check().Supports(Feature.GroupCommand); @@ -2120,7 +2120,7 @@ public void TestGroupWithKeyFunction() Assert.Equal(3, results[2]["count"].ToInt32()); } - [SkippableFact] + [Fact] public void TestGroupWithMaxTime() { RequireServer.Check().Supports(Feature.GroupCommand); @@ -2147,7 +2147,7 @@ public void TestGroupWithMaxTime() } } - [SkippableFact] + [Fact] public void TestGroupWithQuery() { RequireServer.Check().Supports(Feature.GroupCommand); @@ -2256,7 +2256,7 @@ public void TestInsertBatchContinueOnError() Assert.Equal(3, collection.Count()); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void TestInsertBatchMultipleBatchesWriteConcernDisabledContinueOnErrorFalse( [Values(false, true)] bool retryWrites) @@ -2297,7 +2297,7 @@ public void TestInsertBatchMultipleBatchesWriteConcernDisabledContinueOnErrorFal Assert.Equal(0, collection.Count(Query.EQ("_id", 5))); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void TestInsertBatchMultipleBatchesWriteConcernDisabledContinueOnErrorTrue( [Values(false, true)] bool retryWrites) @@ -2496,7 +2496,7 @@ public void TestInsertDuplicateKey() CheckExpectedResult(expectedResult, result); } - [SkippableFact] + [Fact] public void TestInsertWithWriteConcernError() { RequireServer.Check().ClusterType(ClusterType.ReplicaSet); @@ -2870,7 +2870,7 @@ public void TestMapReduceInlineWithQuery() } } - [SkippableFact] + [Fact] public void TestMapReduceWriteConcern() { RequireServer.Check().ClusterType(ClusterType.ReplicaSet); @@ -3023,7 +3023,7 @@ public void TestRemoveNoMatchingDocument() Assert.Equal(0, _collection.Count()); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void TestRemoveUnacknowledeged( [Values(false, true)] bool retryWrites) @@ -3044,7 +3044,7 @@ public void TestRemoveUnacknowledeged( } } - [SkippableFact] + [Fact] public void TestRemoveWithWriteConcernError() { RequireServer.Check().ClusterType(ClusterType.ReplicaSet); @@ -3126,7 +3126,7 @@ public void TestGetStats() _collection.GetStats(); } - [SkippableFact] + [Fact] public void TestGetStatsNoPadding() { RequireServer.Check().ClusterTypes(ClusterType.Standalone, ClusterType.ReplicaSet).StorageEngine("mmapv1"); @@ -3144,7 +3144,7 @@ public void TestGetStatsNoPadding() Assert.True((stats.UserFlags & CollectionUserFlags.NoPadding) != 0); } - [SkippableFact] + [Fact] public void TestGetStatsUsePowerOf2Sizes() { RequireServer.Check().ClusterTypes(ClusterType.Standalone, ClusterType.ReplicaSet).StorageEngine("mmapv1"); @@ -3297,7 +3297,7 @@ public void TestUpdateNullQuery() Assert.Equal(2, _collection.Count()); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void TestUpdateUnacknowledged( [Values(false, true)] bool retryWrites) @@ -3321,7 +3321,7 @@ public void TestUpdateUnacknowledged( } } - [SkippableFact] + [Fact] public void TestUpdateWithWriteConcernError() { RequireServer.Check().ClusterType(ClusterType.ReplicaSet); @@ -3395,7 +3395,7 @@ public void TestUpsertDuplicateKey() }); } - [SkippableFact] + [Fact] public void TestValidate() { RequireServer.Check().StorageEngine("mmapv1"); diff --git a/tests/MongoDB.Driver.Legacy.Tests/MongoDB.Driver.Legacy.Tests.csproj b/tests/MongoDB.Driver.Legacy.Tests/MongoDB.Driver.Legacy.Tests.csproj index 92c30a55698..1447131a67b 100644 --- a/tests/MongoDB.Driver.Legacy.Tests/MongoDB.Driver.Legacy.Tests.csproj +++ b/tests/MongoDB.Driver.Legacy.Tests/MongoDB.Driver.Legacy.Tests.csproj @@ -1,34 +1,16 @@ - - true - + - netcoreapp2.1;netcoreapp3.1;net472 - netcoreapp2.1;netcoreapp3.1 - 9 - true - - false ..\..\MongoDBLegacyTest.ruleset MongoDB.Driver.Legacy.Tests MongoDB.Driver.Legacy.Tests - MongoDB Inc. - Copyright © 2010-present MongoDB Inc. MongoDB.Driver.Legacy tests. - - 0.0.0-local - - - - TRACE - - @@ -48,13 +30,8 @@ - - - - - - + diff --git a/tests/MongoDB.Driver.Legacy.Tests/MongoDatabaseTests.cs b/tests/MongoDB.Driver.Legacy.Tests/MongoDatabaseTests.cs index aee0bb74b94..1fe01451f27 100644 --- a/tests/MongoDB.Driver.Legacy.Tests/MongoDatabaseTests.cs +++ b/tests/MongoDB.Driver.Legacy.Tests/MongoDatabaseTests.cs @@ -18,7 +18,7 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver; using MongoDB.Driver.Builders; using MongoDB.Driver.Core; @@ -82,7 +82,7 @@ public void TestCreateCollection() Assert.True(_database.CollectionExists(collectionName)); } - [SkippableFact] + [Fact] public void TestCreateCollectionSetIndexOptionDefaults() { RequireServer.Check().ClusterTypes(ClusterType.Standalone, ClusterType.ReplicaSet); @@ -102,7 +102,7 @@ public void TestCreateCollectionSetIndexOptionDefaults() Assert.Equal(expectedIndexOptionDefaultsDocument, collectionInfo["options"]["indexOptionDefaults"]); } - [SkippableFact] + [Fact] public void TestCreateCollectionSetStorageEngine() { RequireServer.Check().VersionGreaterThanOrEqualTo("2.7.0"); @@ -125,7 +125,7 @@ public void TestCreateCollectionSetStorageEngine() Assert.Equal(storageEngineOptions, resultCollection["options"]["storageEngine"]); } - [SkippableFact] + [Fact] public void TestCreateCollectionSetValidator() { RequireServer.Check(); @@ -146,7 +146,7 @@ public void TestCreateCollectionSetValidator() Assert.Equal("strict", collectionInfo["options"]["validationLevel"].AsString); } - [SkippableFact] + [Fact] public void TestCreateCollectionWriteConcern() { RequireServer.Check().ClusterType(ClusterType.ReplicaSet); @@ -160,7 +160,7 @@ public void TestCreateCollectionWriteConcern() exception.Should().BeOfType(); } - [SkippableFact] + [Fact] public void TestCreateViewWriteConcern() { RequireServer.Check().ClusterType(ClusterType.ReplicaSet); @@ -189,7 +189,7 @@ public void TestDropCollection() Assert.False(_database.CollectionExists(collectionName)); } - [SkippableFact] + [Fact] public void TestDropCollectionWriteConcern() { RequireServer.Check().ClusterType(ClusterType.ReplicaSet); @@ -203,7 +203,7 @@ public void TestDropCollectionWriteConcern() exception.Should().BeOfType(); } - [SkippableFact] + [Fact] public void TestEvalNoArgs() { RequireServer.Check().Supports(Feature.Eval); @@ -217,7 +217,7 @@ public void TestEvalNoArgs() #pragma warning restore } - [SkippableFact] + [Fact] public void TestEvalNoArgsNoLock() { RequireServer.Check().Supports(Feature.Eval); @@ -231,7 +231,7 @@ public void TestEvalNoArgsNoLock() #pragma warning restore } - [SkippableFact] + [Fact] public void TestEvalWithMaxTime() { RequireServer.Check().Supports(Feature.Eval); @@ -255,7 +255,7 @@ public void TestEvalWithMaxTime() #pragma warning restore } - [SkippableFact] + [Fact] public void TestEvalWithOneArg() { RequireServer.Check().Supports(Feature.Eval); @@ -269,7 +269,7 @@ public void TestEvalWithOneArg() #pragma warning restore } - [SkippableFact] + [Fact] public void TestEvalWithOneArgNoLock() { RequireServer.Check().Supports(Feature.Eval); @@ -283,7 +283,7 @@ public void TestEvalWithOneArgNoLock() #pragma warning restore } - [SkippableFact] + [Fact] public void TestEvalWithTwoArgs() { RequireServer.Check().Supports(Feature.Eval); @@ -297,7 +297,7 @@ public void TestEvalWithTwoArgs() #pragma warning restore } - [SkippableFact] + [Fact] public void TestEvalWithTwoArgsNoLock() { RequireServer.Check().Supports(Feature.Eval); @@ -368,7 +368,7 @@ public void TestGetCollectionNames() Assert.Equal(new[] { "a", "b", "c" }, collectionNames.Where(n => n != "system.indexes")); } - [SkippableFact] + [Fact] public void TestGetCurrentOp() { RequireServer.Check().ClusterTypes(ClusterType.Standalone, ClusterType.ReplicaSet); @@ -452,7 +452,7 @@ public void TestRenameCollectionDropTarget() Assert.True(_database.CollectionExists(collectionName2)); } - [SkippableFact] + [Fact] public void TestRenameCollectionWriteConcern() { RequireServer.Check().ClusterType(ClusterType.ReplicaSet); @@ -505,7 +505,7 @@ public void TestSetProfilingLevel() } } - [SkippableTheory] + [Theory] [InlineData("user1", "pass1", true)] [InlineData("user2", "pass2", false)] public void TestUserMethods(string username, string password, bool isReadOnly) diff --git a/tests/MongoDB.Driver.Legacy.Tests/MongoServerSettingsTests.cs b/tests/MongoDB.Driver.Legacy.Tests/MongoServerSettingsTests.cs index e35cdf77ff2..34fe6befce6 100644 --- a/tests/MongoDB.Driver.Legacy.Tests/MongoServerSettingsTests.cs +++ b/tests/MongoDB.Driver.Legacy.Tests/MongoServerSettingsTests.cs @@ -111,7 +111,9 @@ public void TestClone() settings.Credential = MongoCredential.CreateMongoCRCredential("database", "username", "password").WithMechanismProperty("SERVICE_NAME", "other"); #pragma warning restore 618 settings.SslSettings = new SslSettings { CheckCertificateRevocation = !url.TlsDisableCertificateRevocationCheck }; +#pragma warning disable CS0618 // Type or member is obsolete settings.SdamLogFilename = "unimatrix-zero"; +#pragma warning restore CS0618 // Type or member is obsolete var clone = settings.Clone(); @@ -222,7 +224,9 @@ public void TestDefaults() Assert.Equal(true, settings.RetryReads); Assert.Equal(true, settings.RetryWrites); Assert.Equal(ConnectionStringScheme.MongoDB, settings.Scheme); +#pragma warning disable CS0618 // Type or member is obsolete Assert.Equal(null, settings.SdamLogFilename); +#pragma warning restore CS0618 // Type or member is obsolete Assert.Equal(_localHost, settings.Server); Assert.Equal(_localHost, settings.Servers.First()); Assert.Equal(1, settings.Servers.Count()); @@ -444,7 +448,9 @@ public void TestEquals() Assert.False(clone.Equals(settings)); clone = settings.Clone(); +#pragma warning disable CS0618 // Type or member is obsolete clone.SdamLogFilename = "osiris"; +#pragma warning restore CS0618 // Type or member is obsolete Assert.False(clone.Equals(settings)); clone = settings.Clone(); @@ -560,7 +566,9 @@ public void TestFromClientSettings() var builder = new MongoUrlBuilder(connectionString); var url = builder.ToMongoUrl(); var clientSettings = MongoClientSettings.FromUrl(url); +#pragma warning disable CS0618 // Type or member is obsolete clientSettings.SdamLogFilename = "section-31"; +#pragma warning restore CS0618 // Type or member is obsolete var settings = MongoServerSettings.FromClientSettings(clientSettings); @@ -598,7 +606,9 @@ public void TestFromClientSettings() Assert.Equal(url.RetryReads, settings.RetryReads); Assert.Equal(url.RetryWrites, settings.RetryWrites); Assert.Equal(url.Scheme, settings.Scheme); +#pragma warning disable CS0618 // Type or member is obsolete Assert.Equal(clientSettings.SdamLogFilename, settings.SdamLogFilename); +#pragma warning restore CS0618 // Type or member is obsolete Assert.True(url.Servers.SequenceEqual(settings.Servers)); Assert.Equal(url.ServerSelectionTimeout, settings.ServerSelectionTimeout); Assert.Equal(url.SocketTimeout, settings.SocketTimeout); @@ -986,6 +996,7 @@ public void TestScheme() Assert.Throws(() => { settings.Scheme = ConnectionStringScheme.MongoDBPlusSrv; }); } +#pragma warning disable CS0618 // Type or member is obsolete [Fact] public void TestSdamLogFileName() { @@ -1000,6 +1011,7 @@ public void TestSdamLogFileName() Assert.Same(sdamLogFileName, settings.SdamLogFilename); Assert.Throws(() => { settings.SdamLogFilename = sdamLogFileName; }); } +#pragma warning restore CS0618 // Type or member is obsolete [Fact] public void TestServer() @@ -1277,6 +1289,7 @@ public void ToClusterKey_should_copy_relevant_values() EnabledSslProtocols = SslProtocols.Tls }; +#pragma warning disable CS0618 // Type or member is obsolete var subject = new MongoServerSettings { AllowInsecureTls = false, @@ -1297,7 +1310,9 @@ public void ToClusterKey_should_copy_relevant_values() MinConnectionPoolSize = 5, ReplicaSetName = "rs", Scheme = ConnectionStringScheme.MongoDBPlusSrv, +#pragma warning disable CS0618 // Type or member is obsolete SdamLogFilename = "navi", +#pragma warning restore CS0618 // Type or member is obsolete Servers = servers, ServerSelectionTimeout = TimeSpan.FromSeconds(6), SocketTimeout = TimeSpan.FromSeconds(4), @@ -1338,7 +1353,9 @@ public void ToClusterKey_should_copy_relevant_values() result.ReceiveBufferSize.Should().Be(MongoDefaults.TcpReceiveBufferSize); result.ReplicaSetName.Should().Be(subject.ReplicaSetName); result.Scheme.Should().Be(subject.Scheme); +#pragma warning disable CS0618 // Type or member is obsolete result.SdamLogFilename.Should().Be(subject.SdamLogFilename); +#pragma warning restore CS0618 // Type or member is obsolete result.SendBufferSize.Should().Be(MongoDefaults.TcpSendBufferSize); result.Servers.Should().Equal(subject.Servers); result.ServerSelectionTimeout.Should().Be(subject.ServerSelectionTimeout); diff --git a/tests/MongoDB.Driver.Legacy.Tests/MongoServerTests.cs b/tests/MongoDB.Driver.Legacy.Tests/MongoServerTests.cs index 45d3053e446..42768187407 100644 --- a/tests/MongoDB.Driver.Legacy.Tests/MongoServerTests.cs +++ b/tests/MongoDB.Driver.Legacy.Tests/MongoServerTests.cs @@ -18,7 +18,7 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; @@ -109,7 +109,7 @@ public void TestDropDatabase() Assert.False(databaseNames.Contains(database.Name)); } - [SkippableFact] + [Fact] public void TestDropDatabaseWriteConcern() { RequireServer.Check().ClusterType(ClusterType.ReplicaSet); @@ -212,7 +212,7 @@ public void TestPrimary() Assert.True(instance.IsPrimary); } - [SkippableFact] + [Fact] public void TestReconnect() { RequireEnvironment.Check().EnvironmentVariable("EXPLICIT"); diff --git a/tests/MongoDB.Driver.Legacy.Tests/Operations/BulkWriteOperationTests.cs b/tests/MongoDB.Driver.Legacy.Tests/Operations/BulkWriteOperationTests.cs index 349730a6ae5..98f837697cc 100644 --- a/tests/MongoDB.Driver.Legacy.Tests/Operations/BulkWriteOperationTests.cs +++ b/tests/MongoDB.Driver.Legacy.Tests/Operations/BulkWriteOperationTests.cs @@ -17,7 +17,7 @@ using System.Linq; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Builders; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Misc; @@ -40,7 +40,7 @@ public BulkWriteOperationTests() _collection = LegacyTestConfiguration.Database.GetCollection(GetType().Name); } - [SkippableFact] + [Fact] public void TestBatchSplittingBySizeWithErrorsOrdered() { RequirePlatform.Check().SkipWhen(SupportedOperatingSystem.MacOS); @@ -81,7 +81,7 @@ public void TestBatchSplittingBySizeWithErrorsOrdered() Assert.Equal(expectedDocuments, _collection.FindAll()); } - [SkippableFact] + [Fact] public void TestBatchSplittingBySizeWithErrorsUnordered() { RequirePlatform.Check().SkipWhen(SupportedOperatingSystem.MacOS); @@ -121,7 +121,7 @@ public void TestBatchSplittingBySizeWithErrorsUnordered() _collection.FindAll().Should().BeEquivalentTo(expectedDocuments); } - [SkippableTheory] + [Theory] [InlineData(-1)] [InlineData(0)] [InlineData(1)] @@ -144,7 +144,7 @@ public void TestBatchSplittingDeletesNearMaxWriteBatchCount(int maxBatchCountDel Assert.Equal(0, _collection.Count()); } - [SkippableTheory] + [Theory] [InlineData(-1)] [InlineData(0)] [InlineData(1)] @@ -166,7 +166,7 @@ public void TestBatchSplittingInsertsNearMaxWriteBatchCount(int maxBatchCountDel Assert.Equal(count, _collection.Count()); } - [SkippableTheory] + [Theory] [InlineData(-1)] [InlineData(0)] [InlineData(1)] @@ -191,7 +191,7 @@ public void TestBatchSplittingUpdatesNearMaxWriteBatchCount(int maxBatchCountDel Assert.Equal(count, _collection.Count(Query.EQ("n", 1))); } - [SkippableTheory] + [Theory] [InlineData(false)] [InlineData(true)] public void TestExecuteTwice(bool ordered) @@ -205,7 +205,7 @@ public void TestExecuteTwice(bool ordered) Assert.Throws(() => bulk.Execute()); } - [SkippableTheory] + [Theory] [InlineData(false, 0)] [InlineData(false, 1)] [InlineData(true, 0)] @@ -235,7 +235,7 @@ public void TestExecuteWithExplicitWriteConcern(bool ordered, int w) } } - [SkippableTheory] + [Theory] [InlineData(false)] [InlineData(true)] public void TestExecuteWithNoRequests(bool ordered) @@ -247,7 +247,7 @@ public void TestExecuteWithNoRequests(bool ordered) Assert.Throws(() => bulk.Execute()); } - [SkippableTheory] + [Theory] [InlineData(false)] [InlineData(true)] public void TestFindAfterExecute(bool ordered) @@ -261,7 +261,7 @@ public void TestFindAfterExecute(bool ordered) Assert.Throws(() => bulk.Find(new QueryDocument())); } - [SkippableTheory] + [Theory] [InlineData(false)] [InlineData(true)] public void TestFindWithNullQuery(bool ordered) @@ -273,7 +273,7 @@ public void TestFindWithNullQuery(bool ordered) Assert.Throws(() => bulk.Find(null)); } - [SkippableTheory] + [Theory] [InlineData(false)] [InlineData(true)] public void TestInsertAfterExecute(bool ordered) @@ -287,7 +287,7 @@ public void TestInsertAfterExecute(bool ordered) Assert.Throws(() => bulk.Insert(new BsonDocument())); } - [SkippableTheory] + [Theory] [InlineData(false)] [InlineData(true)] public void TestInsertDollarPrefixedKeyRejectedPre50(bool ordered) @@ -301,7 +301,7 @@ public void TestInsertDollarPrefixedKeyRejectedPre50(bool ordered) Assert.Throws>(() => bulk.Execute()); } - [SkippableTheory] + [Theory] [InlineData(false)] [InlineData(true)] public void TestInsertDollarPrefixedKeyAcceptedPost50(bool ordered) @@ -322,7 +322,7 @@ public void TestInsertDollarPrefixedKeyAcceptedPost50(bool ordered) _collection.FindAll().Should().BeEquivalentTo(expectedDocuments); } - [SkippableTheory] + [Theory] [InlineData(false)] [InlineData(true)] public void TestInsertMultipleDocuments(bool ordered) @@ -350,7 +350,7 @@ public void TestInsertMultipleDocuments(bool ordered) _collection.FindAll().Should().BeEquivalentTo(documents); } - [SkippableTheory] + [Theory] [InlineData(false)] [InlineData(true)] public void TestInsertOneDocument(bool ordered) @@ -371,7 +371,7 @@ public void TestInsertOneDocument(bool ordered) _collection.FindAll().Should().BeEquivalentTo(new[] { document }); } - [SkippableFact] + [Fact] public void TestMixedOperationsOrdered() { RequirePlatform.Check().SkipWhen(SupportedOperatingSystem.MacOS); @@ -411,7 +411,7 @@ public void TestMixedOperationsOrdered() _collection.FindAll().SetFields(Fields.Exclude("_id")).Should().BeEquivalentTo(expectedDocuments); } - [SkippableFact] + [Fact] public void TestMixedOperationsUnordered() { RequirePlatform.Check().SkipWhen(SupportedOperatingSystem.MacOS); @@ -453,7 +453,7 @@ public void TestMixedOperationsUnordered() _collection.FindAll().SetFields(Fields.Exclude("_id")).Should().BeEquivalentTo(expectedDocuments); } - [SkippableFact] + [Fact] public void TestMixedUpsertsOrdered() { RequirePlatform.Check().SkipWhen(SupportedOperatingSystem.MacOS); @@ -482,7 +482,7 @@ public void TestMixedUpsertsOrdered() _collection.FindAll().Should().BeEquivalentTo(expectedDocuments); } - [SkippableFact] + [Fact] public void TestMixedUpsertsUnordered() { RequirePlatform.Check().SkipWhen(SupportedOperatingSystem.MacOS); @@ -511,7 +511,7 @@ public void TestMixedUpsertsUnordered() _collection.FindAll().Should().BeEquivalentTo(expectedDocuments); } - [SkippableTheory] + [Theory] [InlineData(false)] [InlineData(true)] public void TestNonDefaultWriteConcern(bool ordered) @@ -534,7 +534,7 @@ public void TestNonDefaultWriteConcern(bool ordered) Assert.Equal(0, _collection.Count()); } - [SkippableFact] + [Fact] public void TestOrderedBatchWithErrors() { RequirePlatform.Check().SkipWhen(SupportedOperatingSystem.MacOS); @@ -579,7 +579,7 @@ public void TestOrderedBatchWithErrors() _collection.FindAll().SetFields(Fields.Exclude("_id")).Should().BeEquivalentTo(expectedDocuments); } - [SkippableTheory] + [Theory] [InlineData(false)] [InlineData(true)] public void TestRemoveMultiple(bool ordered) @@ -610,7 +610,7 @@ public void TestRemoveMultiple(bool ordered) _collection.FindAll().Should().BeEquivalentTo(expectedDocuments); } - [SkippableTheory] + [Theory] [InlineData(false)] [InlineData(true)] public void TestRemoveOneOnlyRemovesOneDocument(bool ordered) @@ -632,7 +632,7 @@ public void TestRemoveOneOnlyRemovesOneDocument(bool ordered) _collection.FindAll().SetFields(Fields.Exclude("_id")).Should().BeEquivalentTo(expectedDocuments); } - [SkippableTheory] + [Theory] [InlineData(false)] [InlineData(true)] public void TestRemoveWithEmptyQueryRemovesAllDocuments(bool ordered) @@ -653,7 +653,7 @@ public void TestRemoveWithEmptyQueryRemovesAllDocuments(bool ordered) Assert.Equal(0, _collection.Count()); } - [SkippableTheory] + [Theory] [InlineData(false)] [InlineData(true)] public void TestRemoveWithQueryRemovesOnlyMatchingDocuments(bool ordered) @@ -675,7 +675,7 @@ public void TestRemoveWithQueryRemovesOnlyMatchingDocuments(bool ordered) _collection.FindAll().SetFields(Fields.Exclude("_id")).Should().BeEquivalentTo(expectedDocuments); } - [SkippableTheory] + [Theory] [InlineData(false)] [InlineData(true)] public void TestReplaceOneKeyValidation(bool ordered) @@ -692,7 +692,7 @@ public void TestReplaceOneKeyValidation(bool ordered) Assert.Throws(() => bulk.Execute()); } - [SkippableTheory] + [Theory] [InlineData(false)] [InlineData(true)] public void TestReplaceOneWithMultipleMatchingDocuments(bool ordered) @@ -724,7 +724,7 @@ public void TestReplaceOneWithMultipleMatchingDocuments(bool ordered) _collection.FindAll().SetFields(Fields.Exclude("_id")).Should().BeEquivalentTo(expectedDocuments); } - [SkippableFact] + [Fact] public void TestUnorderedBatchWithErrors() { RequirePlatform.Check().SkipWhen(SupportedOperatingSystem.MacOS); @@ -775,7 +775,7 @@ public void TestUnorderedBatchWithErrors() _collection.FindAll().SetFields(Fields.Exclude("_id")).Should().BeEquivalentTo(expectedDocuments); } - [SkippableTheory] + [Theory] [InlineData(false)] [InlineData(true)] public void TestUpdateChecksThatAllTopLevelFieldNamesAreOperators(bool ordered) @@ -790,7 +790,7 @@ public void TestUpdateChecksThatAllTopLevelFieldNamesAreOperators(bool ordered) Assert.Throws(() => bulk.Execute()); } - [SkippableTheory] + [Theory] [InlineData(false)] [InlineData(true)] public void TestUpdateOneBasic(bool ordered) @@ -821,7 +821,7 @@ public void TestUpdateOneBasic(bool ordered) _collection.FindAll().SetFields(Fields.Exclude("_id")).Should().BeEquivalentTo(expectedDocuments); } - [SkippableTheory] + [Theory] [InlineData(false)] [InlineData(true)] public void TestUpdateOneKeyValidation(bool ordered) @@ -844,7 +844,7 @@ public void TestUpdateOneKeyValidation(bool ordered) } } - [SkippableTheory] + [Theory] [InlineData(false)] [InlineData(true)] public void TestUpdateOnlyAffectsDocumentsThatMatch(bool ordered) @@ -878,7 +878,7 @@ public void TestUpdateOnlyAffectsDocumentsThatMatch(bool ordered) _collection.FindAll().SetFields(Fields.Exclude("_id")).Should().BeEquivalentTo(expectedDocuments); } - [SkippableTheory] + [Theory] [InlineData(false)] [InlineData(true)] public void TestUpdateUpdatesAllMatchingDocuments(bool ordered) @@ -909,7 +909,7 @@ public void TestUpdateUpdatesAllMatchingDocuments(bool ordered) _collection.FindAll().SetFields(Fields.Exclude("_id")).Should().BeEquivalentTo(expectedDocuments); } - [SkippableTheory] + [Theory] [InlineData(false)] [InlineData(true)] public void TestUpsertOneVeryLargeDocument(bool ordered) @@ -937,7 +937,7 @@ public void TestUpsertOneVeryLargeDocument(bool ordered) _collection.FindAll().Should().BeEquivalentTo(expectedDocuments); } - [SkippableTheory] + [Theory] [InlineData(false)] [InlineData(true)] public void TestUpsertReplaceOneDoesNotAffectNonUpsertsInTheSameOperation(bool ordered) @@ -963,7 +963,7 @@ public void TestUpsertReplaceOneDoesNotAffectNonUpsertsInTheSameOperation(bool o _collection.FindAll().SetFields(Fields.Exclude("_id")).Should().BeEquivalentTo(expectedDocuments); } - [SkippableTheory] + [Theory] [InlineData(false)] [InlineData(true)] public void TestUpsertReplaceOneOnlyReplacesOneMatchingDocument(bool ordered) @@ -995,7 +995,7 @@ public void TestUpsertReplaceOneOnlyReplacesOneMatchingDocument(bool ordered) _collection.FindAll().SetFields(Fields.Exclude("_id")).Should().BeEquivalentTo(expectedDocuments); } - [SkippableTheory] + [Theory] [InlineData(false)] [InlineData(true)] public void TestUpsertUpdateOneDoesNotAffectNonUpsertsInTheSameOperation(bool ordered) @@ -1036,7 +1036,7 @@ public void TestUpsertUpdateOneDoesNotAffectNonUpsertsInTheSameOperation(bool or _collection.FindAll().SetFields(Fields.Exclude("_id")).Should().BeEquivalentTo(expectedDocuments); } - [SkippableTheory] + [Theory] [InlineData(false)] [InlineData(true)] public void TestUpsertUpdateOneOnlyAffectsOneMatchingDocument(bool ordered) @@ -1067,7 +1067,7 @@ public void TestUpsertUpdateOneOnlyAffectsOneMatchingDocument(bool ordered) _collection.FindAll().SetFields(Fields.Exclude("_id")).Should().BeEquivalentTo(expectedDocuments); } - [SkippableTheory] + [Theory] [InlineData(false)] [InlineData(true)] public void TestUpsertUpdateUpsertsAndDoesNotAffectNonUpsertsInTheSameOperation(bool ordered) @@ -1108,7 +1108,7 @@ public void TestUpsertUpdateUpsertsAndDoesNotAffectNonUpsertsInTheSameOperation( _collection.FindAll().SetFields(Fields.Exclude("_id")).Should().BeEquivalentTo(expectedDocuments); } - [SkippableTheory] + [Theory] [InlineData(false)] [InlineData(true)] public void TestUpsertWithMultipleMatchingDocuments(bool ordered) @@ -1141,7 +1141,7 @@ public void TestUpsertWithMultipleMatchingDocuments(bool ordered) _collection.FindAll().Should().BeEquivalentTo(expectedDocuments); } - [SkippableTheory] + [Theory] [InlineData(false)] [InlineData(true)] public void TestUpsertWithNoMatchingDocument(bool ordered) @@ -1174,7 +1174,7 @@ public void TestUpsertWithNoMatchingDocument(bool ordered) _collection.FindAll().Should().BeEquivalentTo(expectedDocuments); } - [SkippableTheory] + [Theory] [InlineData(false)] [InlineData(true)] public void TestUpsertWithOneMatchingDocument(bool ordered) @@ -1207,7 +1207,7 @@ public void TestUpsertWithOneMatchingDocument(bool ordered) _collection.FindAll().Should().BeEquivalentTo(expectedDocuments); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void TestW0DoesNotReportErrors( [Values(false, true)] bool retryWrites, @@ -1242,7 +1242,7 @@ public void TestW0DoesNotReportErrors( } } - [SkippableTheory] + [Theory] [InlineData(false)] [InlineData(true)] public void TestW2AgainstStandalone(bool ordered) @@ -1263,7 +1263,7 @@ public void TestW2AgainstStandalone(bool ordered) } } - [SkippableFact] + [Fact] public void TestWTimeoutPlusDuplicateKeyError() { RequireEnvironment.Check().EnvironmentVariable("EXPLICIT"); diff --git a/tests/MongoDB.Driver.Legacy.Tests/Operations/CurrentOpOperationTests.cs b/tests/MongoDB.Driver.Legacy.Tests/Operations/CurrentOpOperationTests.cs index 542494dc6d0..66a1b0ae6a5 100644 --- a/tests/MongoDB.Driver.Legacy.Tests/Operations/CurrentOpOperationTests.cs +++ b/tests/MongoDB.Driver.Legacy.Tests/Operations/CurrentOpOperationTests.cs @@ -16,7 +16,7 @@ using System; using System.Threading.Tasks; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Misc; @@ -57,7 +57,7 @@ public void constructor_should_throw_when_databaseNamespace_is_null() action.ShouldThrow().And.ParamName.Should().Be("databaseNamespace"); } - [SkippableFact] + [Fact] public void Execute_should_return_expected_result() { RequireServer.Check(); diff --git a/tests/MongoDB.Driver.Legacy.Tests/Operations/CurrentOpUsingCommandOperation.cs b/tests/MongoDB.Driver.Legacy.Tests/Operations/CurrentOpUsingCommandOperation.cs index 769139d3ffe..715cce000cf 100644 --- a/tests/MongoDB.Driver.Legacy.Tests/Operations/CurrentOpUsingCommandOperation.cs +++ b/tests/MongoDB.Driver.Legacy.Tests/Operations/CurrentOpUsingCommandOperation.cs @@ -18,7 +18,7 @@ using System.Threading.Tasks; using FluentAssertions; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Misc; @@ -72,7 +72,7 @@ public void CreateOperation_should_return_expected_result() result.ResultSerializer.Should().BeSameAs(BsonDocumentSerializer.Instance); } - [SkippableFact] + [Fact] public void Execute_should_return_expected_result() { RequireServer.Check(); diff --git a/tests/MongoDB.Driver.Legacy.Tests/Properties/AssemblyInfo.cs b/tests/MongoDB.Driver.Legacy.Tests/Properties/AssemblyInfo.cs index 3aba0e8f41b..d0af98a9d42 100644 --- a/tests/MongoDB.Driver.Legacy.Tests/Properties/AssemblyInfo.cs +++ b/tests/MongoDB.Driver.Legacy.Tests/Properties/AssemblyInfo.cs @@ -14,11 +14,11 @@ */ using System.Runtime.InteropServices; -using MongoDB.Driver.Core.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; [assembly: ComVisible(false)] [assembly: CollectionBehavior(DisableTestParallelization = true)] -[assembly: TestFramework(XunitExtensionsConsts.TimeoutEnforcingXunitFramework, XunitExtensionsConsts.TimeoutEnforcingFrameworkAssembly)] +[assembly: TestFramework(XunitExtensionsConstants.TimeoutEnforcingXunitFramework, XunitExtensionsConstants.TimeoutEnforcingFrameworkAssembly)] diff --git a/tests/MongoDB.Driver.Legacy.Tests/RegisterObjectSerializerCollection.cs b/tests/MongoDB.Driver.Legacy.Tests/RegisterObjectSerializerCollection.cs new file mode 100644 index 00000000000..bd4fcb327db --- /dev/null +++ b/tests/MongoDB.Driver.Legacy.Tests/RegisterObjectSerializerCollection.cs @@ -0,0 +1,25 @@ +/* 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 MongoDB.Bson.TestHelpers; +using Xunit; + +namespace MongoDB.Driver.Legacy.Tests +{ + [CollectionDefinition(RegisterObjectSerializerFixture.CollectionName)] + public sealed class RegisterObjectSerializerCollection : ICollectionFixture + { + } +} diff --git a/tests/MongoDB.Driver.Legacy.Tests/TargetFrameworkTests.cs b/tests/MongoDB.Driver.Legacy.Tests/TargetFrameworkTests.cs index 55514076b87..04265b1176e 100644 --- a/tests/MongoDB.Driver.Legacy.Tests/TargetFrameworkTests.cs +++ b/tests/MongoDB.Driver.Legacy.Tests/TargetFrameworkTests.cs @@ -23,7 +23,7 @@ public class TargetFrameworkTests [Fact] public void TargetFramework_should_be_valid() { - var actualFramework = MongoDB.Driver.Legacy.TargetFramework.Moniker; + var actualFramework = Legacy.TargetFramework.Moniker; var expectedFramework = GetExpectedTargetFramework(); actualFramework.Should().Be(expectedFramework); } @@ -33,7 +33,7 @@ private string GetExpectedTargetFramework() { #if NETCOREAPP2_1 return "netstandard20"; -#elif NETCOREAPP3_1 +#elif NETCOREAPP3_1_OR_GREATER return "netstandard21"; #elif NET472 return "net472"; diff --git a/tests/MongoDB.Driver.TestConsoleApplication/MongoDB.Driver.TestConsoleApplication.csproj b/tests/MongoDB.Driver.TestConsoleApplication/MongoDB.Driver.TestConsoleApplication.csproj index 66e3e378b8d..8687c61d210 100644 --- a/tests/MongoDB.Driver.TestConsoleApplication/MongoDB.Driver.TestConsoleApplication.csproj +++ b/tests/MongoDB.Driver.TestConsoleApplication/MongoDB.Driver.TestConsoleApplication.csproj @@ -1,27 +1,16 @@ - - true - - + + Exe - netcoreapp2.1;netcoreapp3.1;net472 - netcoreapp2.1;netcoreapp3.1 - 9 MongoDB.Driver.TestConsoleApplication MongoDB.Driver.TestConsoleApplication - MongoDB Inc. - Copyright © 2010-present MongoDB Inc. MongoDB.Driver test console application. - - 0.0.0-local - - @@ -36,7 +25,7 @@ CA2007 - + diff --git a/tests/MongoDB.Driver.TestHelpers/DisposableMongoClient.cs b/tests/MongoDB.Driver.TestHelpers/DisposableMongoClient.cs index 36aee54c51b..4edae8527df 100644 --- a/tests/MongoDB.Driver.TestHelpers/DisposableMongoClient.cs +++ b/tests/MongoDB.Driver.TestHelpers/DisposableMongoClient.cs @@ -16,9 +16,10 @@ using System; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using MongoDB.Bson; using MongoDB.Driver.Core.Clusters; -using MongoDB.Driver.Core.TestHelpers.Logging; +using MongoDB.Driver.Core.Logging; namespace MongoDB.Driver.TestHelpers { @@ -33,9 +34,7 @@ public class DisposableMongoClient : IMongoClient, IDisposable public DisposableMongoClient(IMongoClient wrapped, ILogger logger) { this.wrapped = wrapped; - - _logger = logger.Decorate($"_cluster:{wrapped.Cluster.ClusterId}"); - _logger.Debug("Created"); + _logger = logger; } public ICluster Cluster => wrapped.Cluster; @@ -243,11 +242,11 @@ public IMongoClient WithWriteConcern(WriteConcern writeConcern) public void Dispose() { - _logger.Debug("Disposing"); + _logger?.LogDebug(wrapped.Cluster.ClusterId, "Disposing"); ClusterRegistry.Instance.UnregisterAndDisposeCluster(wrapped.Cluster); - _logger.Debug("Cluster unregistered and disposed"); + _logger?.LogDebug("Cluster unregistered and disposed"); if (wrapped is MongoClient mongoClient) { @@ -261,7 +260,7 @@ public void Dispose() } } - _logger.Debug("Disposed"); + _logger?.LogDebug(wrapped.Cluster.ClusterId, "Disposed"); } } } diff --git a/tests/MongoDB.Driver.TestHelpers/DriverTestConfiguration.cs b/tests/MongoDB.Driver.TestHelpers/DriverTestConfiguration.cs index d07488592b5..ca180abd692 100644 --- a/tests/MongoDB.Driver.TestHelpers/DriverTestConfiguration.cs +++ b/tests/MongoDB.Driver.TestHelpers/DriverTestConfiguration.cs @@ -17,12 +17,12 @@ using System.Collections.Generic; using System.Linq; using System.Threading; -using FluentAssertions; +using Microsoft.Extensions.Logging; using MongoDB.Driver.Core; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Configuration; +using MongoDB.Driver.Core.Logging; using MongoDB.Driver.Core.Servers; -using MongoDB.Driver.Core.TestHelpers.Logging; using MongoDB.Driver.Linq; using MongoDB.Driver.TestHelpers; @@ -34,24 +34,24 @@ namespace MongoDB.Driver.Tests public static class DriverTestConfiguration { // private static fields - private static Lazy __client; private static Lazy __clientWithMultipleShardRouters; private static CollectionNamespace __collectionNamespace; private static DatabaseNamespace __databaseNamespace; private static Lazy> __directClientsToShardRouters; + private static Lazy __linq2Client; private static Lazy __linq3Client; // static constructor static DriverTestConfiguration() { - __client = new Lazy(() => new MongoClient(GetClientSettings()), true); + __linq2Client = new Lazy(CreateLinq2Client, isThreadSafe: true); + __linq3Client = new Lazy(CreateLinq3Client, isThreadSafe: true); __clientWithMultipleShardRouters = new Lazy(() => CreateClient(useMultipleShardRouters: true), true); __databaseNamespace = CoreTestConfiguration.DatabaseNamespace; __directClientsToShardRouters = new Lazy>( () => CreateDirectClientsToHostsInConnectionString(CoreTestConfiguration.ConnectionStringWithMultipleShardRouters).ToList().AsReadOnly(), isThreadSafe: true); __collectionNamespace = new CollectionNamespace(__databaseNamespace, "testcollection"); - __linq3Client = new Lazy(CreateLinq3Client, isThreadSafe: true); } // public static properties @@ -60,7 +60,7 @@ static DriverTestConfiguration() /// public static MongoClient Client { - get { return __client.Value; } + get { return Linq3Client; } } /// @@ -101,6 +101,14 @@ public static DatabaseNamespace DatabaseNamespace get { return __databaseNamespace; } } + /// + /// Gets the LINQ2 test client. + /// + public static MongoClient Linq2Client + { + get { return __linq2Client.Value; } + } + /// /// Gets the LINQ3 test client. /// @@ -125,14 +133,14 @@ public static IEnumerable CreateDirectClientsToHostsInConnectionSt return CreateDirectClientsToServersInClientSettings(MongoClientSettings.FromConnectionString(connectionString.ToString())); } - public static DisposableMongoClient CreateDisposableClient(ILogger logger = null) + public static DisposableMongoClient CreateDisposableClient(LoggingSettings loggingSettings = null) { - return CreateDisposableClient((MongoClientSettings s) => { }, logger); + return CreateDisposableClient((MongoClientSettings s) => { }, loggingSettings); } - public static DisposableMongoClient CreateDisposableClient(Action clusterConfigurator, ILogger logger = null) + public static DisposableMongoClient CreateDisposableClient(Action clusterConfigurator, LoggingSettings loggingSettings = null) { - return CreateDisposableClient((MongoClientSettings s) => s.ClusterConfigurator = clusterConfigurator, logger); + return CreateDisposableClient((MongoClientSettings s) => s.ClusterConfigurator = clusterConfigurator, loggingSettings); } public static MongoClient CreateClient( @@ -158,32 +166,44 @@ public static MongoClient CreateClient( public static DisposableMongoClient CreateDisposableClient( Action clientSettingsConfigurator, - ILogger logger, + LoggingSettings loggingSettings, bool useMultipleShardRouters = false) { Action compositeClientSettingsConfigurator = s => { EnsureUniqueCluster(s); + s.LoggingSettings = loggingSettings; + clientSettingsConfigurator?.Invoke(s); }; + var client = CreateClient(compositeClientSettingsConfigurator, useMultipleShardRouters); - return new DisposableMongoClient(client, logger); + + return new DisposableMongoClient(client, loggingSettings.ToInternalLoggerFactory()?.CreateLogger()); } - public static DisposableMongoClient CreateDisposableClient(EventCapturer capturer, ILogger logger = null) + public static DisposableMongoClient CreateDisposableClient(EventCapturer capturer, LoggingSettings loggingSettings = null) { - return CreateDisposableClient((ClusterBuilder c) => c.Subscribe(capturer), logger); + return CreateDisposableClient((ClusterBuilder c) => c.Subscribe(capturer), loggingSettings); } - public static DisposableMongoClient CreateDisposableClient(MongoClientSettings settings, ILogger logger = null) + public static DisposableMongoClient CreateDisposableClient(MongoClientSettings settings) { EnsureUniqueCluster(settings); - return new DisposableMongoClient(new MongoClient(settings), logger); + + return new DisposableMongoClient(new MongoClient(settings), settings.LoggingSettings.ToInternalLoggerFactory()?.CreateLogger()); + } + + private static MongoClient CreateLinq2Client() + { + var linq2ClientSettings = GetClientSettings(); + linq2ClientSettings.LinqProvider = LinqProvider.V2; + return new MongoClient(linq2ClientSettings); } private static MongoClient CreateLinq3Client() { - var linq3ClientSettings = Client.Settings.Clone(); + var linq3ClientSettings = GetClientSettings(); linq3ClientSettings.LinqProvider = LinqProvider.V3; return new MongoClient(linq3ClientSettings); } @@ -205,6 +225,11 @@ public static MongoClientSettings GetClientSettings() return clientSettings; } + public static MongoClient GetLinqClient(LinqProvider linqProvider) + { + return linqProvider == LinqProvider.V2 ? Linq2Client : Linq3Client; + } + public static bool IsReplicaSet(IMongoClient client) { var clusterTypeIsKnown = SpinWait.SpinUntil(() => client.Cluster.Description.Type != ClusterType.Unknown, TimeSpan.FromSeconds(10)); diff --git a/tests/MongoDB.Driver.TestHelpers/MongoDB.Driver.TestHelpers.csproj b/tests/MongoDB.Driver.TestHelpers/MongoDB.Driver.TestHelpers.csproj index e67daa6dfc3..ec3880ba1a3 100644 --- a/tests/MongoDB.Driver.TestHelpers/MongoDB.Driver.TestHelpers.csproj +++ b/tests/MongoDB.Driver.TestHelpers/MongoDB.Driver.TestHelpers.csproj @@ -1,43 +1,21 @@ - - true - + - netstandard2.0;netstandard2.1;net472 - netstandard2.0;netstandard2.1 - 9 - true - - false + $(StandardTargetFrameworks) ..\..\MongoDBTest.ruleset MongoDB.Driver.TestHelpers MongoDB.Driver.TestHelpers - MongoDB Inc. - Copyright © 2010-present MongoDB Inc. MongoDB.Driver test helpers. - - 0.0.0-local - - - - TRACE - - - - - - - diff --git a/tests/MongoDB.Driver.Tests/AggregateFluentBucketAutoTests.cs b/tests/MongoDB.Driver.Tests/AggregateFluentBucketAutoTests.cs index d2e0755f75e..b6936b3d959 100644 --- a/tests/MongoDB.Driver.Tests/AggregateFluentBucketAutoTests.cs +++ b/tests/MongoDB.Driver.Tests/AggregateFluentBucketAutoTests.cs @@ -14,6 +14,7 @@ */ using System; +using System.Collections.Generic; using System.Linq; using FluentAssertions; using MongoDB.Bson; @@ -21,13 +22,15 @@ using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Bson.TestHelpers; -using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; +using MongoDB.Driver.Linq; +using MongoDB.Driver.Tests.Linq.Linq3ImplementationTests; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.Tests { - public class AggregateFluentBucketAutoTests + public class AggregateFluentBucketAutoTests : Linq3IntegrationTest { #region static // private static fields @@ -84,7 +87,7 @@ public void BucketAuto_should_add_expected_stage() renderedStage.Document.Should().Be("{ $bucketAuto : { groupBy : \"$year\", buckets : 4 } }"); } - [SkippableFact] + [Fact] public void BucketAuto_should_return_expected_result() { RequireServer.Check(); @@ -120,7 +123,7 @@ public void BucketAuto_with_granularity_should_add_expected_stage() renderedStage.Document.Should().Be("{ $bucketAuto : { groupBy : \"$_id\", buckets : 4, granularity : 'POWERSOF2' } }"); } - [SkippableFact] + [Fact] public void BucketAuto_with_granularity_should_return_expected_result() { RequireServer.Check(); @@ -156,7 +159,7 @@ public void BucketAuto_with_output_should_add_expected_stage() renderedStage.Document.Should().Be("{ $bucketAuto : { groupBy : \"$year\", buckets : 4, output : { years : { $push : \"$year\" }, count : { $sum : 1 } } } }"); } - [SkippableFact] + [Fact] public void BucketAuto_with_output_should_return_expected_result() { RequireServer.Check(); @@ -192,7 +195,7 @@ public void BucketAuto_typed_should_add_expected_stage() renderedStage.Document.Should().Be("{ $bucketAuto : { groupBy : \"$year\", buckets : 4 } }"); } - [SkippableFact] + [Fact] public void BucketAuto_typed_should_return_expected_result() { RequireServer.Check(); @@ -211,48 +214,90 @@ public void BucketAuto_typed_should_return_expected_result() new AggregateBucketAutoResult(1926, 1926, 1)); } - [Fact] - public void BucketAuto_typed_with_output_should_add_expected_stage() + [Theory] + [ParameterAttributeData] + public void BucketAuto_typed_with_output_should_add_expected_stage( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { - var collection = __database.GetCollection(__collectionNamespace.CollectionName); + var collection = GetCollection(linqProvider: linqProvider); var subject = collection.Aggregate(); var buckets = 4; - var result = subject.BucketAuto( - e => e.Year, - buckets, - g => new { _id = default(AggregateBucketAutoResultId), Years = g.Select(e => e.Year), Count = g.Count() }); - - var stage = result.Stages.Single(); - var serializerRegistry = BsonSerializer.SerializerRegistry; - var exhibitSerializer = serializerRegistry.GetSerializer(); - var renderedStage = stage.Render(exhibitSerializer, serializerRegistry); - renderedStage.Document.Should().Be("{ $bucketAuto : { groupBy : \"$year\", buckets : 4, output : { Years : { $push : \"$year\" }, Count : { $sum : 1 } } } }"); + if (linqProvider == LinqProvider.V2) + { + var result = subject.BucketAutoForLinq2( + e => e.Year, + buckets, + g => new { _id = default(AggregateBucketAutoResultId), Years = g.Select(e => e.Year), Count = g.Count() }); + + var stage = result.Stages.Single(); + var serializerRegistry = BsonSerializer.SerializerRegistry; + var exhibitSerializer = serializerRegistry.GetSerializer(); + var renderedStage = stage.Render(exhibitSerializer, serializerRegistry, linqProvider); + renderedStage.Document.Should().Be("{ $bucketAuto : { groupBy : \"$year\", buckets : 4, output : { Years : { $push : \"$year\" }, Count : { $sum : 1 } } } }"); + } + else + { + var result = subject.BucketAuto( + e => (int?)e.Year, + buckets, + g => new { Key = g.Key, Years = g.Select(e => e.Year), Count = g.Count() }); + + var stage = result.Stages.Single(); + var serializerRegistry = BsonSerializer.SerializerRegistry; + var exhibitSerializer = serializerRegistry.GetSerializer(); + var renderedStage = stage.Render(exhibitSerializer, serializerRegistry, linqProvider); + renderedStage.Documents.Should().HaveCount(2); + renderedStage.Documents[0].Should().Be("{ $bucketAuto : { groupBy : '$year', buckets : 4, output : { __agg0 : { $push : '$year' }, __agg1 : { $sum : 1 } } } }"); + renderedStage.Documents[1].Should().Be("{ $project : { Key : '$_id', Years : '$__agg0', Count : '$__agg1', _id : 0 } }"); + } } - [SkippableFact] - public void BucketAuto_typed_with_output_should_return_expected_result() + [Theory] + [ParameterAttributeData] + public void BucketAuto_typed_with_output_should_return_expected_result( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { RequireServer.Check(); EnsureTestData(); - var collection = __database.GetCollection(__collectionNamespace.CollectionName); + var collection = GetCollection(linqProvider: linqProvider); var subject = collection.Aggregate(); var buckets = 4; - var result = subject - .BucketAuto( - e => e.Year, - buckets, - g => new { _id = default(AggregateBucketAutoResultId), Years = g.Select(e => e.Year), Count = g.Count() }) - .ToList(); - - result.Select(r => r._id.Min).Should().Equal(null, 1902, 1925, 1926); - result.Select(r => r._id.Max).Should().Equal(1902, 1925, 1926, 1926); - result[0].Years.Should().Equal(new int[0]); - result[1].Years.Should().Equal(new int[] { 1902 }); - result[2].Years.Should().Equal(new int[] { 1925 }); - result[3].Years.Should().Equal(new int[] { 1926 }); - result.Select(r => r.Count).Should().Equal(1, 1, 1, 1); + if (linqProvider == LinqProvider.V2) + { + var result = subject + .BucketAutoForLinq2( + e => e.Year, + buckets, + g => new { _id = default(AggregateBucketAutoResultId), Years = g.Select(e => e.Year), Count = g.Count() }) + .ToList(); + + result.Select(r => r._id.Min).Should().Equal(null, 1902, 1925, 1926); + result.Select(r => r._id.Max).Should().Equal(1902, 1925, 1926, 1926); + result[0].Years.Should().Equal(new int[0]); + result[1].Years.Should().Equal(new int[] { 1902 }); + result[2].Years.Should().Equal(new int[] { 1925 }); + result[3].Years.Should().Equal(new int[] { 1926 }); + result.Select(r => r.Count).Should().Equal(1, 1, 1, 1); + } + else + { + var result = subject + .BucketAuto( + e => (int?)e.Year, + buckets, + g => new { Key = g.Key, Years = g.Select(e => e.Year), Count = g.Count() }) + .ToList(); + + result.Select(r => r.Key.Min).Should().Equal(null, 1902, 1925, 1926); + result.Select(r => r.Key.Max).Should().Equal(1902, 1925, 1926, 1926); + result[0].Years.Should().Equal(new int[0]); + result[1].Years.Should().Equal(new int[] { 1902 }); + result[2].Years.Should().Equal(new int[] { 1925 }); + result[3].Years.Should().Equal(new int[] { 1926 }); + result.Select(r => r.Count).Should().Equal(1, 1, 1, 1); + } } // nested types diff --git a/tests/MongoDB.Driver.Tests/AggregateFluentBucketTests.cs b/tests/MongoDB.Driver.Tests/AggregateFluentBucketTests.cs index 453981cedf1..006f493ac8d 100644 --- a/tests/MongoDB.Driver.Tests/AggregateFluentBucketTests.cs +++ b/tests/MongoDB.Driver.Tests/AggregateFluentBucketTests.cs @@ -21,13 +21,15 @@ using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Bson.TestHelpers; -using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; +using MongoDB.Driver.Linq; +using MongoDB.Driver.Tests.Linq.Linq3ImplementationTests; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.Tests { - public class AggregateFluentBucketTests + public class AggregateFluentBucketTests : Linq3IntegrationTest { #region static // private static fields @@ -85,7 +87,7 @@ public void Bucket_should_add_expected_stage() renderedStage.Document.Should().Be("{ $bucket : { groupBy : \"$year\", boundaries : [ 1900, 1920, 1950 ], default : \"Unknown\" } }"); } - [SkippableFact] + [Fact] public void Bucket_should_return_expected_result() { RequireServer.Check(); @@ -122,7 +124,7 @@ public void Bucket_with_output_should_add_expected_stage() renderedStage.Document.Should().Be("{ $bucket : { groupBy : \"$year\", boundaries : [ 1900, 1920, 1950 ], default : \"Unknown\", output : { years : { $push : \"$year\" }, count : { $sum : 1 } } } }"); } - [SkippableFact] + [Fact] public void Bucket_with_output_should_return_expected_result() { RequireServer.Check(); @@ -159,7 +161,7 @@ public void Bucket_typed_should_add_expected_stage() renderedStage.Document.Should().Be("{ $bucket : { groupBy : \"$year\", boundaries : [ 1900, 1920, 1950 ], default : \"Unknown\" } }"); } - [SkippableFact] + [Fact] public void Bucket_typed_should_return_expected_result() { RequireServer.Check(); @@ -178,50 +180,92 @@ public void Bucket_typed_should_return_expected_result() new AggregateBucketResult("Unknown", 1)); } - [Fact] - public void Bucket_typed_with_output_should_add_expected_stage() + [Theory] + [ParameterAttributeData] + public void Bucket_typed_with_output_should_add_expected_stage( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { - var collection = __database.GetCollection(__collectionNamespace.CollectionName); + var collection = GetCollection(linqProvider: linqProvider); var subject = collection.Aggregate(); var boundaries = new BsonValue[] { 1900, 1920, 1950 }; var options = new AggregateBucketOptions { DefaultBucket = (BsonValue)"Unknown" }; - var result = subject.Bucket( - e => e.Year, - boundaries, - g => new { _id = default(BsonValue), Years = g.Select(e => e.Year), Count = g.Count() }, - options); - - var stage = result.Stages.Single(); - var serializerRegistry = BsonSerializer.SerializerRegistry; - var exhibitSerializer = serializerRegistry.GetSerializer(); - var renderedStage = stage.Render(exhibitSerializer, serializerRegistry); - renderedStage.Document.Should().Be("{ $bucket : { groupBy : \"$year\", boundaries : [ 1900, 1920, 1950 ], default : \"Unknown\", output : { Years : { $push : \"$year\" }, Count : { $sum : 1 } } } }"); + if (linqProvider == LinqProvider.V2) + { + var result = subject.Bucket( + e => e.Year, + boundaries, + g => new { _id = default(BsonValue), Years = g.Select(e => e.Year), Count = g.Count() }, + options); + + var stage = result.Stages.Single(); + var serializerRegistry = BsonSerializer.SerializerRegistry; + var exhibitSerializer = serializerRegistry.GetSerializer(); + var renderedStage = stage.Render(exhibitSerializer, serializerRegistry, linqProvider); + renderedStage.Document.Should().Be("{ $bucket : { groupBy : \"$year\", boundaries : [ 1900, 1920, 1950 ], default : \"Unknown\", output : { Years : { $push : \"$year\" }, Count : { $sum : 1 } } } }"); + } + else + { + var result = subject.Bucket( + e => e.Year, + boundaries, + g => new { Key = g.Key, Years = g.Select(e => e.Year), Count = g.Count() }, + options); + + var stage = result.Stages.Single(); + var serializerRegistry = BsonSerializer.SerializerRegistry; + var exhibitSerializer = serializerRegistry.GetSerializer(); + var renderedStage = stage.Render(exhibitSerializer, serializerRegistry, linqProvider); + renderedStage.Documents.Should().HaveCount(2); + renderedStage.Documents[0].Should().Be("{ $bucket : { groupBy : '$year', boundaries : [ 1900, 1920, 1950 ], default : 'Unknown', output : { __agg0 : {$push : '$year' }, __agg1 : { $sum : 1 } } } }"); + renderedStage.Documents[1].Should().Be("{ $project : { Key : '$_id', Years : '$__agg0', Count : '$__agg1', _id : 0 } }"); + } } - [SkippableFact] - public void Bucket_typed_with_output_should_return_expected_result() + [Theory] + [ParameterAttributeData] + public void Bucket_typed_with_output_should_return_expected_result( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { RequireServer.Check(); EnsureTestData(); - var collection = __database.GetCollection(__collectionNamespace.CollectionName); + var collection = GetCollection(linqProvider: linqProvider); var subject = collection.Aggregate(); var boundaries = new BsonValue[] { 1900, 1920, 1950 }; var options = new AggregateBucketOptions { DefaultBucket = (BsonValue)"Unknown" }; - var result = subject - .Bucket( - e => e.Year, - boundaries, - g => new { _id = default(BsonValue), Years = g.Select(e => e.Year), Count = g.Count() }, - options) - .ToList(); - - result.Select(b => b._id).Should().Equal(1900, 1920, "Unknown"); - result[0].Years.Should().Equal(new[] { 1902 }); - result[1].Years.Should().Equal(new[] { 1926, 1925 }); - result[2].Years.Should().Equal(new int[0]); - result.Select(b => b.Count).Should().Equal(1, 2, 1); + if (linqProvider== LinqProvider.V2) + { + var result = subject + .Bucket( + e => e.Year, + boundaries, + g => new { _id = default(BsonValue), Years = g.Select(e => e.Year), Count = g.Count() }, + options) + .ToList(); + + result.Select(b => b._id).Should().Equal(1900, 1920, "Unknown"); + result[0].Years.Should().Equal(new[] { 1902 }); + result[1].Years.Should().Equal(new[] { 1926, 1925 }); + result[2].Years.Should().Equal(new int[0]); + result.Select(b => b.Count).Should().Equal(1, 2, 1); + } + else + { + var result = subject + .Bucket( + e => e.Year, + boundaries, + g => new { Key = g.Key, Years = g.Select(e => e.Year), Count = g.Count() }, + options) + .ToList(); + + result.Select(b => b.Key).Should().Equal(1900, 1920, "Unknown"); + result[0].Years.Should().Equal(new[] { 1902 }); + result[1].Years.Should().Equal(new[] { 1926, 1925 }); + result[2].Years.Should().Equal(new int[0]); + result.Select(b => b.Count).Should().Equal(1, 2, 1); + } } // nested types diff --git a/tests/MongoDB.Driver.Tests/AggregateFluentFacetTests.cs b/tests/MongoDB.Driver.Tests/AggregateFluentFacetTests.cs index f6898679aba..68b67c2c854 100644 --- a/tests/MongoDB.Driver.Tests/AggregateFluentFacetTests.cs +++ b/tests/MongoDB.Driver.Tests/AggregateFluentFacetTests.cs @@ -96,7 +96,7 @@ public void Facet_with_1_facet_should_add_the_expected_stage() }"); } - [SkippableFact] + [Fact] public void Facet_with_1_facet_should_return_expected_result() { RequireServer.Check(); @@ -159,7 +159,7 @@ public void Facet_with_2_facets_should_add_the_expected_stage() }"); } - [SkippableFact] + [Fact] public void Facet_with_2_facets_should_return_expected_result() { RequireServer.Check(); @@ -237,7 +237,7 @@ public void Facet_with_3_facets_should_add_the_expected_stage() }"); } - [SkippableFact] + [Fact] public void Facet_with_3_facets_should_return_expected_result() { RequireServer.Check(); @@ -310,7 +310,7 @@ public void Facet_typed_with_1_facet_should_add_the_expected_stage() }"); } - [SkippableFact] + [Fact] public void Facet_typed_with_1_facet_should_return_expected_result() { RequireServer.Check(); @@ -374,7 +374,7 @@ public void Facet_typed_with_2_facets_should_add_the_expected_stage() }"); } - [SkippableFact] + [Fact] public void Facet_typed_with_2_facets_should_return_expected_result() { RequireServer.Check(); @@ -453,7 +453,7 @@ public void Facet_typed_with_3_facets_should_add_the_expected_stage() }"); } - [SkippableFact] + [Fact] public void Facet_typed_with_3_facets_should_return_expected_result() { RequireServer.Check(); diff --git a/tests/MongoDB.Driver.Tests/AggregateFluentGraphLookupWithAirportsCollectionTests.cs b/tests/MongoDB.Driver.Tests/AggregateFluentGraphLookupWithAirportsCollectionTests.cs index 354ecbc9d42..e421cef7c7e 100644 --- a/tests/MongoDB.Driver.Tests/AggregateFluentGraphLookupWithAirportsCollectionTests.cs +++ b/tests/MongoDB.Driver.Tests/AggregateFluentGraphLookupWithAirportsCollectionTests.cs @@ -110,7 +110,7 @@ public void GraphLookup_should_add_expected_stage() }"); } - [SkippableFact] + [Fact] public void GraphLookup_should_return_expected_result() { RequireServer.Check(); @@ -190,7 +190,7 @@ public void GraphLookup_typed_should_add_expected_stage() }"); } - [SkippableFact] + [Fact] public void GraphLookup_typed_should_return_expected_result() { RequireServer.Check(); @@ -268,7 +268,7 @@ public void GraphLookup_typed_with_array_valued_start_with_should_add_expected_s }"); } - [SkippableFact] + [Fact] public void GraphLookup_typed_with_array_valued_start_with_should_return_expected_result() { RequireServer.Check(); diff --git a/tests/MongoDB.Driver.Tests/AggregateFluentGraphLookupWithEmployeeCollectionTests.cs b/tests/MongoDB.Driver.Tests/AggregateFluentGraphLookupWithEmployeeCollectionTests.cs index 2eaae36ef88..0a8885ca701 100644 --- a/tests/MongoDB.Driver.Tests/AggregateFluentGraphLookupWithEmployeeCollectionTests.cs +++ b/tests/MongoDB.Driver.Tests/AggregateFluentGraphLookupWithEmployeeCollectionTests.cs @@ -95,7 +95,7 @@ public void GraphLookup_should_add_expected_stage() }"); } - [SkippableFact] + [Fact] public void GraphLookup_should_return_expected_result() { RequireServer.Check(); @@ -150,7 +150,7 @@ public void GraphLookup_with_restrictSearchWithMatch_should_add_expected_stage() }"); } - [SkippableFact] + [Fact] public void GraphLookup_with_restrictSearchWithMatch_should_return_expected_result() { RequireServer.Check(); @@ -205,7 +205,7 @@ public void GraphLookup_with_expressions_should_add_expected_stage() }"); } - [SkippableFact] + [Fact] public void GraphLookup_with_expressions_should_return_expected_result() { RequireServer.Check(); @@ -252,7 +252,7 @@ public void GraphLookup_untyped_based_should_add_expected_stage() }"); } - [SkippableFact] + [Fact] public void GraphLookup_untyped_based_should_return_expected_result() { RequireServer.Check(); diff --git a/tests/MongoDB.Driver.Tests/AggregateFluentTests.cs b/tests/MongoDB.Driver.Tests/AggregateFluentTests.cs index b6bd0f88e0a..5f538d76e00 100644 --- a/tests/MongoDB.Driver.Tests/AggregateFluentTests.cs +++ b/tests/MongoDB.Driver.Tests/AggregateFluentTests.cs @@ -18,7 +18,7 @@ using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; using Moq; @@ -165,7 +165,7 @@ public void Count_should_add_the_expected_stage( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Count_should_return_the_expected_result( [Values(false, true)] @@ -196,7 +196,7 @@ public void Count_should_return_the_expected_result( result.Should().Be(1); } - [SkippableFact] + [Fact] public void Function_should_return_expected_result() { RequireServer.Check().Supports(Feature.AggregateFunction); @@ -262,7 +262,7 @@ public void Function_should_return_expected_result() result[2].Should().Be("{ _id : 3, name : 'Mrs. Eppie Delta', scores : [ 9, 8, 8 ], isFound : false, message : 'Hello Mrs. Eppie Delta. Your total score is 25.' }"); } - [SkippableFact] + [Fact] public void Group_with_accumulator_should_return_expected_result() { RequireServer.Check().Supports(Feature.AggregateAccumulator); @@ -315,7 +315,7 @@ public void Group_with_accumulator_should_return_expected_result() result[1].Should().Be("{ _id : 'Homer', minCopies : 10, avgCopies : 10.0, maxCopies : 10 }"); } - [SkippableFact] + [Fact] public void Lookup_with_let_and_bsondocuments_params_should_return_the_expected_result() { RequireServer.Check(); @@ -413,7 +413,7 @@ public class StockData public int Instock { get; set; } } - [SkippableFact] + [Fact] public void Lookup_with_let_should_return_the_expected_result() { RequireServer.Check(); @@ -481,7 +481,7 @@ public void Lookup_with_let_should_return_the_expected_result() result[2].Should().Be("{ 'item' : 'cookies', 'price' : 10, 'ordered' : 60, 'stockdata' : [{ 'instock' : 80 }] }"); } - [SkippableFact] + [Fact] public void Lookup_with_let_and_mismatched_pipeline_condition_should_return_the_expected_result() { RequireServer.Check(); @@ -548,7 +548,7 @@ public void Lookup_with_let_and_mismatched_pipeline_condition_should_return_the_ result[2].Should().Be("{ 'item' : 'cookies', 'price' : 10, 'ordered' : 60, 'stockdata' : [] }"); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Lookup_without_let_should_return_the_expected_result([Values(null, "{}")] string emptyLetValue) { @@ -1229,7 +1229,7 @@ public class ItemResult public string Name { get; set; } } - [SkippableFact] + [Fact] public void UnionWith_with_different_schemas_and_projection_should_return_the_expected_result() { RequireServer.Check().Supports(Feature.AggregateUnionWith); diff --git a/tests/MongoDB.Driver.Tests/AggregateGraphLookupEnumerableFromOrToTests.cs b/tests/MongoDB.Driver.Tests/AggregateGraphLookupEnumerableFromOrToTests.cs index 1af563945fe..fee02d81a62 100644 --- a/tests/MongoDB.Driver.Tests/AggregateGraphLookupEnumerableFromOrToTests.cs +++ b/tests/MongoDB.Driver.Tests/AggregateGraphLookupEnumerableFromOrToTests.cs @@ -25,7 +25,7 @@ namespace MongoDB.Driver.Tests public class AggregateGraphLookupEnumerableFromOrToTests { // public methods - [SkippableFact] + [Fact] public void GraphLookup_with_many_to_one_parameters_should_return_expected_result() { RequireServer.Check(); @@ -66,7 +66,7 @@ public void GraphLookup_with_many_to_one_parameters_should_return_expected_resul result[1].ToBsonDocument().Should().Be(expectedResult[1].ToBsonDocument()); } - [SkippableFact] + [Fact] public void GraphLookup_with_one_to_many_parameters_should_return_expected_result() { RequireServer.Check(); @@ -107,7 +107,7 @@ public void GraphLookup_with_one_to_many_parameters_should_return_expected_resul result[1].ToBsonDocument().Should().Be(expectedResult[1].ToBsonDocument()); } - [SkippableFact] + [Fact] public void GraphLookup_with_one_to_one_parameters_should_return_expected_result() { RequireServer.Check(); diff --git a/tests/MongoDB.Driver.Tests/AsyncCursorTests.cs b/tests/MongoDB.Driver.Tests/AsyncCursorTests.cs index de6c749b949..66600b03cf9 100644 --- a/tests/MongoDB.Driver.Tests/AsyncCursorTests.cs +++ b/tests/MongoDB.Driver.Tests/AsyncCursorTests.cs @@ -20,7 +20,7 @@ using FluentAssertions.Common; using MongoDB.Bson; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core; using MongoDB.Driver.Core.Events; using MongoDB.Driver.Core.Misc; @@ -33,7 +33,7 @@ namespace MongoDB.Driver.Tests public class AsyncCursorTests { //public methods - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Cursor_should_not_throw_exception_after_double_close([Values(false, true)] bool async) { @@ -60,7 +60,7 @@ public void Cursor_should_not_throw_exception_after_double_close([Values(false, } } - [SkippableFact] + [Fact] public void KillCursor_should_actually_work() { RequireServer.Check(); @@ -91,7 +91,7 @@ public void KillCursor_should_actually_work() } } - [SkippableFact] + [Fact] public void Tailable_cursor_should_be_able_to_be_cancelled_from_a_different_thread_with_expected_result() { RequireServer.Check(); diff --git a/tests/MongoDB.Driver.Tests/AuthenticationTests.cs b/tests/MongoDB.Driver.Tests/AuthenticationTests.cs index e37b805fb41..f8ae926047e 100644 --- a/tests/MongoDB.Driver.Tests/AuthenticationTests.cs +++ b/tests/MongoDB.Driver.Tests/AuthenticationTests.cs @@ -19,7 +19,7 @@ using System.Threading; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters.ServerSelectors; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; @@ -32,7 +32,7 @@ namespace MongoDB.Driver.Tests /// public class AuthenticationTests { - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Authentication_fails_when_user_has_Scram_Sha_1_mechanism_and_mechanism_is_Scram_Sha_256( [Values(false, true)] bool async) @@ -51,7 +51,7 @@ public void Authentication_fails_when_user_has_Scram_Sha_1_mechanism_and_mechani AssertAuthenticationFails(settings, async); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Authentication_fails_when_user_has_Scram_Sha_256_mechanism_and_mechanism_is_Scram_Sha_1( [Values(false, true)] bool async) @@ -69,7 +69,7 @@ public void Authentication_fails_when_user_has_Scram_Sha_256_mechanism_and_mecha AssertAuthenticationFails(settings, async); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Authentication_fails_when_user_is_non_extant_and_mechanism_is_not_specified( [Values(false, true)] bool async) @@ -86,7 +86,7 @@ public void Authentication_fails_when_user_is_non_extant_and_mechanism_is_not_sp AssertAuthenticationFails(settings, async); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Authentication_succeeds_when_user_has_both_Scram_Sha_mechanisms_and_mechanism_is_not_specified( [Values(false, true)] bool async) @@ -104,7 +104,7 @@ public void Authentication_succeeds_when_user_has_both_Scram_Sha_mechanisms_and_ AssertAuthenticationSucceeds(settings, async); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Authentication_succeeds_when_user_has_both_scram_sha_mechanisms_and_mechanism_is_Scram_Sha_256( [Values(false, true)] bool async) @@ -122,7 +122,7 @@ public void Authentication_succeeds_when_user_has_both_scram_sha_mechanisms_and_ AssertAuthenticationSucceeds(settings, async); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Authentication_succeeds_when_user_has_Scram_Sha_1_Mechanism_and_mechanism_is_not_specified( [Values(false, true)] bool async) @@ -141,7 +141,7 @@ public void Authentication_succeeds_when_user_has_Scram_Sha_1_Mechanism_and_mech AssertAuthenticationSucceeds(settings, async, speculativeAuthenticatationShouldSucceedIfPossible: false); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Authentication_succeeds_when_user_has_Scram_Sha_256_mechanism_and_mechanism_is_not_specified( [Values(false, true)] bool async) @@ -159,7 +159,7 @@ public void Authentication_succeeds_when_user_has_Scram_Sha_256_mechanism_and_me AssertAuthenticationSucceeds(settings, async); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Authentication_succeeds_when_user_has_Scram_Sha_1_mechanism_and_mechanism_is_Scram_Sha_1( [Values(false, true)] bool async) @@ -178,7 +178,7 @@ public void Authentication_succeeds_when_user_has_Scram_Sha_1_mechanism_and_mech AssertAuthenticationSucceeds(settings, async); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Authentication_succeeds_when_user_has_Scram_Sha_256_mechanism_and_mechanism_is_Scram_Sha_256( [Values(false, true)] bool async) @@ -195,7 +195,7 @@ public void Authentication_succeeds_when_user_has_Scram_Sha_256_mechanism_and_me AssertAuthenticationSucceeds(settings, async); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Authentication_succeeds_when_user_has_multiple_credentials_and_mechanism_is_not_specified( [Values(false, true)] bool async) @@ -222,7 +222,7 @@ public void Authentication_succeeds_when_user_has_multiple_credentials_and_mecha AssertAuthenticationSucceeds(settings, async); } - [SkippableTheory] + [Theory] [InlineData("IX", "IX", "\u2168", "\u2163", false)] // "IX", "IX", Roman numeral nine, Roman numeral four [InlineData("IX", "IX", "\u2168", "\u2163", true)] // "IX", "IX", Roman numeral nine, Roman numeral four public void Authentication_succeeds_with_Ascii_username_and_Ascii_password_when_SaslPrep_equivalent_username_exists( @@ -245,7 +245,7 @@ public void Authentication_succeeds_with_Ascii_username_and_Ascii_password_when_ AssertAuthenticationSucceeds(settings, async); } - [SkippableTheory] + [Theory] [InlineData("IX", "IX", "I\u00ADX", "\u2168", "\u2163", false)] // "IX", "IX", "I-X", Roman numeral nine, Roman numeral four [InlineData("IX", "IX", "I\u00ADX", "\u2168", "\u2163", true)] // "IX", "IX", "I-X", Roman numeral nine, Roman numeral four public void Authentication_succeeds_with_Ascii_username_and_nonSaslPrepped_password_when_SaslPrep_equivalent_username_exists( @@ -269,7 +269,7 @@ public void Authentication_succeeds_with_Ascii_username_and_nonSaslPrepped_passw AssertAuthenticationSucceeds(settings, async); } - [SkippableTheory] + [Theory] [InlineData("IX", "IX", "\u2168", "\u2163", "I\u00ADV", false)] // "IX", "IX", Roman numeral nine, Roman numeral four, I-V [InlineData("IX", "IX", "\u2168", "\u2163", "I\u00ADV", true)] // "IX", "IX", Roman numeral nine, Roman numeral four, I-V public void Authentication_succeeds_with_Unicode_username_and_nonSaslPrepped_password_when_SaslPrep_equivalent_username_exists( @@ -293,7 +293,7 @@ public void Authentication_succeeds_with_Unicode_username_and_nonSaslPrepped_pas AssertAuthenticationSucceeds(settings, async); } - [SkippableTheory] + [Theory] [InlineData("IX", "IX", "\u2168", "\u2163", false)] // "IX", "IX", Roman numeral nine, Roman numeral four [InlineData("IX", "IX", "\u2168", "\u2163", true)] // "IX", "IX", Roman numeral nine, Roman numeral four public void Authentication_succeeds_with_Unicode_username_and_Unicode_password_when_SaslPrep_equivalent_username_exists( @@ -316,7 +316,7 @@ public void Authentication_succeeds_with_Unicode_username_and_Unicode_password_w AssertAuthenticationSucceeds(settings, async); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Authentication_succeeds_with_MONGODB_X509_mechanism( [Values(false, true)] bool async) diff --git a/tests/MongoDB.Driver.Tests/CausalConsistencyTests.cs b/tests/MongoDB.Driver.Tests/CausalConsistencyTests.cs index 6bf8e3a34af..d321e7a377e 100644 --- a/tests/MongoDB.Driver.Tests/CausalConsistencyTests.cs +++ b/tests/MongoDB.Driver.Tests/CausalConsistencyTests.cs @@ -19,7 +19,7 @@ using System.Linq; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core; using MongoDB.Driver.Core.Events; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; @@ -30,7 +30,7 @@ namespace MongoDB.Driver.Tests { public class CausalConsistencyTests { - [SkippableFact] + [Fact] public void OperationTime_should_have_no_value_on_a_newly_created_ClientSession() { RequireServer.Check().SupportsCausalConsistency(); @@ -42,7 +42,7 @@ public void OperationTime_should_have_no_value_on_a_newly_created_ClientSession( } } - [SkippableFact] + [Fact] public void AfterClusterTime_should_be_empty_on_the_first_operation() { RequireServer.Check().SupportsCausalConsistency(); @@ -62,7 +62,7 @@ public void AfterClusterTime_should_be_empty_on_the_first_operation() } } - [SkippableFact] + [Fact] public void Session_OperationTime_should_get_updated_after_an_operation() { RequireServer.Check().SupportsCausalConsistency(); @@ -87,7 +87,7 @@ public void Session_OperationTime_should_get_updated_after_an_operation() } } - [SkippableFact] + [Fact] public void AfterClusterTime_should_be_sent_after_the_first_read_operation() { RequireServer.Check().SupportsCausalConsistency(); @@ -117,7 +117,7 @@ public void AfterClusterTime_should_be_sent_after_the_first_read_operation() } } - [SkippableFact] + [Fact] public void AfterClusterTime_should_be_sent_after_the_first_write_operation() { RequireServer.Check().SupportsCausalConsistency(); @@ -147,7 +147,7 @@ public void AfterClusterTime_should_be_sent_after_the_first_write_operation() } } - [SkippableFact] + [Fact] public void AfterClusterTime_should_not_be_sent_when_the_session_is_not_causally_consistent() { RequireServer.Check().SupportsCausalConsistency(); @@ -168,7 +168,7 @@ public void AfterClusterTime_should_not_be_sent_when_the_session_is_not_causally } } - [SkippableFact] + [Fact] public void ReadConcern_should_not_include_level_when_using_the_server_default() { RequireServer.Check().SupportsCausalConsistency(); @@ -194,7 +194,7 @@ public void ReadConcern_should_not_include_level_when_using_the_server_default() } } - [SkippableFact] + [Fact] public void ReadConcern_should_include_level_when_not_using_the_server_default() { RequireServer.Check().SupportsCausalConsistency(); diff --git a/tests/MongoDB.Driver.Tests/ClientSessionHandleTests.cs b/tests/MongoDB.Driver.Tests/ClientSessionHandleTests.cs index 086587149fa..153d66eb3f2 100644 --- a/tests/MongoDB.Driver.Tests/ClientSessionHandleTests.cs +++ b/tests/MongoDB.Driver.Tests/ClientSessionHandleTests.cs @@ -20,7 +20,7 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Misc; diff --git a/tests/MongoDB.Driver.Tests/ClientSessionOptionsTests.cs b/tests/MongoDB.Driver.Tests/ClientSessionOptionsTests.cs index ea13f0cddd9..b6282ba7932 100644 --- a/tests/MongoDB.Driver.Tests/ClientSessionOptionsTests.cs +++ b/tests/MongoDB.Driver.Tests/ClientSessionOptionsTests.cs @@ -14,7 +14,7 @@ */ using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.Tests diff --git a/tests/MongoDB.Driver.Tests/ClusterKeyTests.cs b/tests/MongoDB.Driver.Tests/ClusterKeyTests.cs index 8383e34fe67..69a124096f3 100644 --- a/tests/MongoDB.Driver.Tests/ClusterKeyTests.cs +++ b/tests/MongoDB.Driver.Tests/ClusterKeyTests.cs @@ -19,7 +19,7 @@ using System.Security.Authentication; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Compression; using MongoDB.Driver.Core.Configuration; @@ -204,6 +204,7 @@ private ClusterKey CreateSubject(string notEqualFieldName = null) var kmsProviders = new Dictionary>(); var loadBalanced = true; var localThreshold = TimeSpan.FromMilliseconds(20); + var loggingSettings = new LoggingSettings(); var maxConnecting = 3; var maxConnectionIdleTime = TimeSpan.FromSeconds(2); var maxConnectionLifeTime = TimeSpan.FromSeconds(3); @@ -305,6 +306,7 @@ private ClusterKey CreateSubject(string notEqualFieldName = null) ipv6, loadBalanced, localThreshold, + loggingSettings, maxConnecting, maxConnectionIdleTime, maxConnectionLifeTime, @@ -353,6 +355,7 @@ internal ClusterKey CreateSubjectWith( var kmsProviders = kmsProvidersValue ?? new Dictionary>(); var loadBalanced = true; var localThreshold = TimeSpan.FromMilliseconds(20); + var loggingSettings = new LoggingSettings(); var maxConnecting = 3; var maxConnectionIdleTime = TimeSpan.FromSeconds(2); var maxConnectionLifeTime = TimeSpan.FromSeconds(3); @@ -394,6 +397,7 @@ internal ClusterKey CreateSubjectWith( ipv6, loadBalanced, localThreshold, + loggingSettings, maxConnecting, maxConnectionIdleTime, maxConnectionLifeTime, diff --git a/tests/MongoDB.Driver.Tests/ClusterRegistryTests.cs b/tests/MongoDB.Driver.Tests/ClusterRegistryTests.cs index 8ddc3fd78b1..ae95e543cf0 100644 --- a/tests/MongoDB.Driver.Tests/ClusterRegistryTests.cs +++ b/tests/MongoDB.Driver.Tests/ClusterRegistryTests.cs @@ -25,12 +25,18 @@ using MongoDB.Driver.Core.Compression; using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.Misc; +using MongoDB.Driver.Core.TestHelpers.Logging; using Xunit; +using Xunit.Abstractions; namespace MongoDB.Driver.Tests { - public class ClusterRegistryTests + public class ClusterRegistryTests : LoggableTestClass { + public ClusterRegistryTests(ITestOutputHelper output) : base(output) + { + } + #if WINDOWS [Fact] public void Instance_should_return_the_same_instance_every_time() @@ -90,6 +96,7 @@ public void GetOrCreateCluster_should_return_a_cluster_with_the_correct_settings ipv6: true, loadBalanced: false, localThreshold: TimeSpan.FromSeconds(4), + loggingSettings: null, maxConnecting: 3, maxConnectionIdleTime: TimeSpan.FromSeconds(5), maxConnectionLifeTime: TimeSpan.FromSeconds(6), diff --git a/tests/MongoDB.Driver.Tests/ClusterTests.cs b/tests/MongoDB.Driver.Tests/ClusterTests.cs index dcb15e7fd91..194e0219f0a 100644 --- a/tests/MongoDB.Driver.Tests/ClusterTests.cs +++ b/tests/MongoDB.Driver.Tests/ClusterTests.cs @@ -21,7 +21,7 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters; @@ -30,13 +30,15 @@ using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; using MongoDB.Driver.Core.TestHelpers; +using MongoDB.Driver.Core.TestHelpers.Logging; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; using MongoDB.Driver.TestHelpers; using Xunit; +using Xunit.Abstractions; namespace MongoDB.Driver.Tests { - public class ClusterTests + public class ClusterTests : LoggableTestClass { private static readonly HashSet __commandsToNotCapture = new HashSet { @@ -52,11 +54,15 @@ public class ClusterTests private const string _collectionName = "test"; private const string _databaseName = "test"; + public ClusterTests(ITestOutputHelper output) : base(output) + { + } + /// /// Test that starting a new transaction on a pinned ClientSession unpins the /// session and normal server selection is performed for the next operation. /// - [SkippableTheory] + [Theory] [ParameterAttributeData] public void SelectServer_loadbalancing_prose_test([Values(false, true)] bool async) { @@ -170,7 +176,7 @@ private DisposableMongoClient CreateDisposableClient(EventCapturer eventCapturer settings.ClusterConfigurator = c => c.Subscribe(eventCapturer); settings.LocalThreshold = TimeSpan.FromMilliseconds(1000); }, - logger: null, + LoggingSettings, true); var timeOut = TimeSpan.FromSeconds(60); bool AllServersConnected() => client.Cluster.Description.Servers.All(s => s.State == ServerState.Connected); diff --git a/tests/MongoDB.Driver.Tests/Communication/Security/AwsAuthenticationTests.cs b/tests/MongoDB.Driver.Tests/Communication/Security/AwsAuthenticationTests.cs index 9d46ada23d2..cde9bd5b544 100644 --- a/tests/MongoDB.Driver.Tests/Communication/Security/AwsAuthenticationTests.cs +++ b/tests/MongoDB.Driver.Tests/Communication/Security/AwsAuthenticationTests.cs @@ -13,17 +13,32 @@ * limitations under the License. */ +using System; +using Amazon.Runtime; +using Amazon.Runtime.CredentialManagement; +using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.Tests.Communication.Security { + /// + /// AWS.SDK notes: + /// 1. run-aws-auth-test-with-regular-aws-credentials: only driver side logic + /// 2. run-aws-auth-test-with-assume-role-credentials: only driver side logic + /// The below use AWS.SDK calls + /// 3. run-aws-auth-test-with-aws-credentials-as-environment-variables: EnvironmentVariablesAWSCredentials + /// 4. run-aws-auth-test-with-aws-credentials-and-session-token-as-environment-variables: EnvironmentVariablesAWSCredentials + /// 5. run-aws-auth-test-with-aws-EC2-credentials: DefaultInstanceProfileAWSCredentials.Instance (EC2) + /// 6. run-aws-auth-test-with-aws-ECS-credentials: ECSTaskCredentials + /// 7. run-aws-auth-test-with-aws-web-identity-credentials: AssumeRoleWithWebIdentityCredentials.FromEnvironmentVariables + /// [Trait("Category", "Authentication")] [Trait("Category", "AwsMechanism")] public class AwsAuthenticationTests { - [SkippableFact] + [Fact] public void Aws_authentication_should_should_have_expected_result() { RequireEnvironment.Check().EnvironmentVariable("AWS_TESTS_ENABLED"); @@ -41,5 +56,113 @@ public void Aws_authentication_should_should_have_expected_result() var count = collection.CountDocuments(FilterDefinition.Empty); } } + + [Fact] + public void Ecs_should_fill_AWS_CONTAINER_CREDENTIALS_RELATIVE_URI() + { + var isEcs = Environment.GetEnvironmentVariable("AWS_ECS_ENABLED") != null; + var awsContainerUri = Environment.GetEnvironmentVariable("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI") ?? Environment.GetEnvironmentVariable("AWS_CONTAINER_CREDENTIALS_FULL_URI"); + (awsContainerUri != null).Should().Be(isEcs); + } + + [Fact] + public void AwsSdk_should_support_all_required_handlers() + { + var credentialsGeneratorsDelegatesEnumerator = FallbackCredentialsFactory.CredentialsGenerators.GetEnumerator(); + + // AppConfigAWSCredentials + AWSCredentials credentials = null; + if (Type.GetType("Amazon.Runtime.AppConfigAWSCredentials, AWSSDK.Core", throwOnError: false) != null) // app.config/web.config does not present on windows + { + var appConfigAWSCredentialsException = Record.Exception(() => RunTestCase()); + // app.config/web.config case is based on ConfigurationManager. This is not configured for this test + appConfigAWSCredentialsException.Message.Should().Contain("The app.config/web.config files for the application did not contain credential information"); + } + + // AssumeRoleWithWebIdentityCredentials.FromEnvironmentVariables() + var exception = Record.Exception(() => RunTestCase()); + if (Environment.GetEnvironmentVariable("AWS_WEB_IDENTITY_TOKEN_FILE") != null) + { + // aws-web-identity-credentials is configured + exception.Should().BeNull(); + credentials.Should().BeOfType(); + } + else + { + // otherwise fail + exception.Message.Should().Contain("webIdentityTokenFile"); + } + + // GetAWSCredentials (Profile) + exception = Record.Exception(() => RunTestCase()); + if (IsWithAwsProfileOnMachine()) + { + // current machine contains configured aws profile, which may include: + // 1. BasicAWSCredentials (aws_access_key_id and aws_secret_access_key) + // 2. SessionAWSCredentials (aws_access_key_id, aws_secret_access_key, aws_session_token) + exception.Should().BeNull(); + credentials.Should().Match(x => x is BasicAWSCredentials || x is SessionAWSCredentials); + } + else + { + // otherwise fail + exception.Message.Should().Contain("Credential").And.Subject.Should().Contain("profile"); + } + + // EnvironmentVariablesAWSCredentials + exception = Record.Exception(() => RunTestCase()); + if (Environment.GetEnvironmentVariable("AWS_ACCESS_KEY_ID") != null && Environment.GetEnvironmentVariable("AWS_SECRET_ACCESS_KEY") != null) + { + // environment variables code path + exception.Should().BeNull(); + credentials.Should().BeOfType(); + } + else + { + // otherwise fail + exception.Message.Should().Contain("The environment variables").And.Subject.Contains("were not set with AWS credentials"); + } + + // ECSEC2CredentialsWrapper + exception = Record.Exception(() => RunTestCase()); + if (Environment.GetEnvironmentVariable("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI") != null || Environment.GetEnvironmentVariable("AWS_CONTAINER_CREDENTIALS_FULL_URI") != null) + { + exception.Should().BeNull(); + credentials.Should().BeOfType(); + } + else + { + exception.Should().BeNull(); + credentials.GetType().Name.Should().Contain("DefaultInstanceProfileAWSCredentials"); // EC2 case + } + + credentialsGeneratorsDelegatesEnumerator.MoveNext().Should().BeFalse(); // no more handlers + + bool IsWithAwsProfileOnMachine() + { + var credentialProfileChain = new CredentialProfileStoreChain(); + if (credentialProfileChain.TryGetProfile(Environment.GetEnvironmentVariable("AWS_PROFILE") ?? "default", out var profile)) + { + try + { + _ = profile.GetAWSCredentials(credentialProfileChain); + return true; + } + catch + { + return false; + } + } + + return false; + } + + void RunTestCase() + { + credentials = null; + credentialsGeneratorsDelegatesEnumerator.MoveNext().Should().BeTrue(); + credentials = credentialsGeneratorsDelegatesEnumerator.Current(); + } + } } } diff --git a/tests/MongoDB.Driver.Tests/Communication/Security/GssapiAuthenticationTests.cs b/tests/MongoDB.Driver.Tests/Communication/Security/GssapiAuthenticationTests.cs index 54f34af7952..1781b522cd4 100644 --- a/tests/MongoDB.Driver.Tests/Communication/Security/GssapiAuthenticationTests.cs +++ b/tests/MongoDB.Driver.Tests/Communication/Security/GssapiAuthenticationTests.cs @@ -16,7 +16,7 @@ using System; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.Tests.Communication.Security @@ -27,7 +27,7 @@ public class GssapiAuthenticationTests { private static readonly string __collectionName = "test"; - [SkippableFact] + [Fact] public void TestNoCredentials() { RequireEnvironment.Check().EnvironmentVariable("GSSAPI_TESTS_ENABLED"); @@ -44,7 +44,7 @@ public void TestNoCredentials() } - [SkippableFact] + [Fact] public void TestSuccessfulAuthentication() { RequireEnvironment.Check().EnvironmentVariable("GSSAPI_TESTS_ENABLED"); @@ -60,7 +60,7 @@ public void TestSuccessfulAuthentication() result.Should().NotBeNull(); } - [SkippableFact] + [Fact] public void TestBadPassword() { RequireEnvironment.Check().EnvironmentVariable("GSSAPI_TESTS_ENABLED"); diff --git a/tests/MongoDB.Driver.Tests/Communication/Security/PlainAuthenticationTests.cs b/tests/MongoDB.Driver.Tests/Communication/Security/PlainAuthenticationTests.cs index e309fc4ccfd..572191aebb8 100644 --- a/tests/MongoDB.Driver.Tests/Communication/Security/PlainAuthenticationTests.cs +++ b/tests/MongoDB.Driver.Tests/Communication/Security/PlainAuthenticationTests.cs @@ -16,7 +16,7 @@ using System; using System.Linq; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver; using Xunit; @@ -35,7 +35,7 @@ public PlainAuthenticationTests() _settings = MongoClientSettings.FromUrl(new MongoUrl(CoreTestConfiguration.ConnectionString.ToString())); } - [SkippableFact] + [Fact] public void TestNoCredentials() { RequireEnvironment.Check().EnvironmentVariable("PLAIN_AUTH_TESTS_ENABLED"); @@ -53,7 +53,7 @@ public void TestNoCredentials() }); } - [SkippableFact] + [Fact] public void TestSuccessfulAuthentication() { RequireEnvironment.Check().EnvironmentVariable("PLAIN_AUTH_TESTS_ENABLED"); @@ -68,7 +68,7 @@ public void TestSuccessfulAuthentication() Assert.NotNull(result); } - [SkippableFact] + [Fact] public void TestBadPassword() { RequireEnvironment.Check().EnvironmentVariable("PLAIN_AUTH_TESTS_ENABLED"); diff --git a/tests/MongoDB.Driver.Tests/ConnectionsSurvivePrimaryStepDownTests.cs b/tests/MongoDB.Driver.Tests/ConnectionsSurvivePrimaryStepDownTests.cs index 71c1f4a49b0..24b52988ea6 100644 --- a/tests/MongoDB.Driver.Tests/ConnectionsSurvivePrimaryStepDownTests.cs +++ b/tests/MongoDB.Driver.Tests/ConnectionsSurvivePrimaryStepDownTests.cs @@ -18,7 +18,7 @@ using System.Net; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters; @@ -37,7 +37,7 @@ public class ConnectionsSurvivePrimaryStepDownTests private readonly string _collectionName = "step-down"; private readonly string _databaseName = "step-down"; - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Connection_pool_should_be_cleared_when_Shutdown_exceptions( [Values( @@ -74,7 +74,7 @@ public void Connection_pool_should_be_cleared_when_Shutdown_exceptions( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Connection_pool_should_not_be_cleared_when_replSetStepDown_and_GetMore([Values(false, true)] bool async) { @@ -143,7 +143,7 @@ void RunOnSecondary(IMongoClient primaryClient, EndPoint secondaryEndpoint, Bson } } - [SkippableFact] + [Fact] public void Connection_pool_should_work_as_expected_when_NonPrimary_exception() { RequireServer.Check().Supports(Feature.FailPointsFailCommand).ClusterType(ClusterType.ReplicaSet); diff --git a/tests/MongoDB.Driver.Tests/CreateManyIndexOptionsTest.cs b/tests/MongoDB.Driver.Tests/CreateManyIndexOptionsTest.cs index edcf3e7891d..5ed06f04ce7 100644 --- a/tests/MongoDB.Driver.Tests/CreateManyIndexOptionsTest.cs +++ b/tests/MongoDB.Driver.Tests/CreateManyIndexOptionsTest.cs @@ -15,7 +15,7 @@ using System; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.Tests diff --git a/tests/MongoDB.Driver.Tests/CreateOneIndexOptionsTest.cs b/tests/MongoDB.Driver.Tests/CreateOneIndexOptionsTest.cs index 1ee8cb739d3..e0b8d0dd4f2 100644 --- a/tests/MongoDB.Driver.Tests/CreateOneIndexOptionsTest.cs +++ b/tests/MongoDB.Driver.Tests/CreateOneIndexOptionsTest.cs @@ -15,7 +15,7 @@ using System; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.Tests diff --git a/tests/MongoDB.Driver.Tests/CreateViewOptionsTests.cs b/tests/MongoDB.Driver.Tests/CreateViewOptionsTests.cs index e7e1d3b5483..6ba3faad10f 100644 --- a/tests/MongoDB.Driver.Tests/CreateViewOptionsTests.cs +++ b/tests/MongoDB.Driver.Tests/CreateViewOptionsTests.cs @@ -17,7 +17,7 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.Tests diff --git a/tests/MongoDB.Driver.Tests/CustomServerSelectorTests.cs b/tests/MongoDB.Driver.Tests/CustomServerSelectorTests.cs index 77771cfa7fa..1ab4420cbc1 100644 --- a/tests/MongoDB.Driver.Tests/CustomServerSelectorTests.cs +++ b/tests/MongoDB.Driver.Tests/CustomServerSelectorTests.cs @@ -19,15 +19,20 @@ using MongoDB.Driver.Core; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Clusters.ServerSelectors; -using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.Events; using MongoDB.Driver.Core.Servers; +using MongoDB.Driver.Core.TestHelpers.Logging; using Xunit; +using Xunit.Abstractions; namespace MongoDB.Driver.Tests { - public class CustomServerSelectorTests + public class CustomServerSelectorTests : LoggableTestClass { + public CustomServerSelectorTests(ITestOutputHelper output) : base(output) + { + } + [Fact] public void Should_call_custom_server_selector() { @@ -43,7 +48,7 @@ public void Should_call_custom_server_selector() c.ConfigureCluster(s => s.With(postServerSelector: customServerSelector)); c.Subscribe(eventCapturer); }, - logger: null)) + LoggingSettings)) { var collection = client .GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName) diff --git a/tests/MongoDB.Driver.Tests/DecryptedSecureStringTests.cs b/tests/MongoDB.Driver.Tests/DecryptedSecureStringTests.cs index b12eee1a875..81d139470aa 100644 --- a/tests/MongoDB.Driver.Tests/DecryptedSecureStringTests.cs +++ b/tests/MongoDB.Driver.Tests/DecryptedSecureStringTests.cs @@ -20,7 +20,7 @@ using System.Security; using FluentAssertions; using MongoDB.Bson.IO; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.Tests diff --git a/tests/MongoDB.Driver.Tests/DropIndexOptionsTest.cs b/tests/MongoDB.Driver.Tests/DropIndexOptionsTest.cs index e860bca5baa..39933afe653 100644 --- a/tests/MongoDB.Driver.Tests/DropIndexOptionsTest.cs +++ b/tests/MongoDB.Driver.Tests/DropIndexOptionsTest.cs @@ -15,7 +15,7 @@ using System; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.Tests diff --git a/tests/MongoDB.Driver.Tests/Encryption/AutoEncryptionOptionsTests.cs b/tests/MongoDB.Driver.Tests/Encryption/AutoEncryptionOptionsTests.cs index bbb46fcf6f2..75fb468d505 100644 --- a/tests/MongoDB.Driver.Tests/Encryption/AutoEncryptionOptionsTests.cs +++ b/tests/MongoDB.Driver.Tests/Encryption/AutoEncryptionOptionsTests.cs @@ -19,7 +19,7 @@ using System.Security.Cryptography.X509Certificates; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Encryption; using Moq; using Xunit; @@ -48,6 +48,18 @@ public void constructor_should_throw_when_kmsProviders_is_null() .Should().Be("kmsProviders"); } + [Fact] + public void constructor_should_handle_empty_kmsProviderOptions_correctly() + { + var emptyAwsKmsProviders = new Dictionary>() + { + { "aws", new Dictionary() } + }; + _ = new AutoEncryptionOptions( + keyVaultNamespace: __keyVaultNamespace, + kmsProviders: emptyAwsKmsProviders); + } + [Theory] [InlineData("mongocryptdURI", "test", false)] [InlineData("mongocryptdURI", 1, true)] diff --git a/tests/MongoDB.Driver.Tests/Encryption/AutoEncryptionTests.cs b/tests/MongoDB.Driver.Tests/Encryption/AutoEncryptionTests.cs index 3d985fd25df..9027da0ed13 100644 --- a/tests/MongoDB.Driver.Tests/Encryption/AutoEncryptionTests.cs +++ b/tests/MongoDB.Driver.Tests/Encryption/AutoEncryptionTests.cs @@ -20,7 +20,7 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers.Logging; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; @@ -47,7 +47,7 @@ public AutoEncryptionTests(ITestOutputHelper testOutputHelper) { } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void CryptClient_should_be_initialized([Values(false, true)] bool withAutoEncryption) { @@ -68,7 +68,7 @@ public void CryptClient_should_be_initialized([Values(false, true)] bool withAut } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public async Task Mongocryptd_should_be_initialized_when_auto_encryption([Values(false, true)] bool withAutoEncryption, [Values(false, true)] bool async) { @@ -107,19 +107,19 @@ public async Task Mongocryptd_should_be_initialized_when_auto_encryption([Values } } - [SkippableFact] - public void Shared_library_should_by_loaded_when_CRYPT_SHARED_LIB_PATH_is_set() + [Fact] + public void Shared_library_should_be_loaded_when_CRYPT_SHARED_LIB_PATH_is_set() { RequireServer.Check().Supports(Feature.ClientSideEncryption); - RequireEnvironment.Check().EnvironmentVariable("CRYPT_SHARED_LIB_PATH", isDefined: true); + RequireEnvironment.Check().EnvironmentVariable("CRYPT_SHARED_LIB_PATH", isDefined: true, allowEmpty: false); - Ensure.That(File.Exists(Environment.GetEnvironmentVariable("CRYPT_SHARED_LIB_PATH")), "CRYPT_SHARED_LIB_PATH should exist"); + Ensure.That(File.Exists(Environment.GetEnvironmentVariable("CRYPT_SHARED_LIB_PATH")), "CRYPT_SHARED_LIB_PATH should exist."); using (var client = GetClient(withAutoEncryption: true)) { var libMongoCryptController = ((MongoClient)client.Wrapped).LibMongoCryptController; var cryptClient = libMongoCryptController._cryptClient(); - cryptClient.CryptSharedLibraryVersion.Should().Be("mongo_crypt_v1-dev-6.0.0-rc13"); + cryptClient.CryptSharedLibraryVersion.Should().NotBeNull(); } } diff --git a/tests/MongoDB.Driver.Tests/Encryption/ClientEncryptionTests.cs b/tests/MongoDB.Driver.Tests/Encryption/ClientEncryptionTests.cs index 7c3623165a6..fe3170485bf 100644 --- a/tests/MongoDB.Driver.Tests/Encryption/ClientEncryptionTests.cs +++ b/tests/MongoDB.Driver.Tests/Encryption/ClientEncryptionTests.cs @@ -18,13 +18,16 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; using MongoDB.Driver.Encryption; using MongoDB.Driver.Tests.Specifications.client_side_encryption; using MongoDB.Libmongocrypt; using Xunit; +using Moq; +using System.Collections.Generic; +using System.Threading; namespace MongoDB.Driver.Tests.Encryption { @@ -35,7 +38,7 @@ public class ClientEncryptionTests private static readonly CollectionNamespace __keyVaultCollectionNamespace = CollectionNamespace.FromFullName("datakeys.keyvault"); #endregion - [SkippableFact] + [Fact] public async Task AddAlternateKeyName_should_correctly_handle_input_arguments() { RequireServer.Check().Supports(Feature.ClientSideEncryption); @@ -49,7 +52,7 @@ public async Task AddAlternateKeyName_should_correctly_handle_input_arguments() } } - [SkippableFact] + [Fact] public async Task CreateDataKey_should_correctly_handle_input_arguments() { RequireServer.Check().Supports(Feature.ClientSideEncryption); @@ -64,8 +67,151 @@ public async Task CreateDataKey_should_correctly_handle_input_arguments() } } + [Fact] + public async Task CreateEncryptedCollection_should_handle_input_arguments() + { + const string kmsProvider = "local"; + const string collectionName = "collName"; + var createCollectionOptions = new CreateCollectionOptions(); + var database = Mock.Of(); + + var dataKeyOptions = new DataKeyOptions(); + + using (var subject = CreateSubject()) + { + ShouldBeArgumentException(Record.Exception(() => subject.CreateEncryptedCollection(database: null, collectionName, createCollectionOptions, kmsProvider, dataKeyOptions)), expectedParamName: "database"); + ShouldBeArgumentException(await Record.ExceptionAsync(() => subject.CreateEncryptedCollectionAsync(database: null, collectionName, createCollectionOptions, kmsProvider, dataKeyOptions)), expectedParamName: "database"); + + ShouldBeArgumentException(Record.Exception(() => subject.CreateEncryptedCollection(database, collectionName: null, createCollectionOptions, kmsProvider, dataKeyOptions)), expectedParamName: "collectionName"); + ShouldBeArgumentException(await Record.ExceptionAsync(() => subject.CreateEncryptedCollectionAsync(database, collectionName: null, createCollectionOptions, kmsProvider, dataKeyOptions)), expectedParamName: "collectionName"); + + ShouldBeArgumentException(Record.Exception(() => subject.CreateEncryptedCollection(database, collectionName: collectionName, createCollectionOptions: null, kmsProvider, dataKeyOptions)), expectedParamName: "createCollectionOptions"); + ShouldBeArgumentException(await Record.ExceptionAsync(() => subject.CreateEncryptedCollectionAsync(database, collectionName, createCollectionOptions: null, kmsProvider, dataKeyOptions)), expectedParamName: "createCollectionOptions"); + + ShouldBeArgumentException(Record.Exception(() => subject.CreateEncryptedCollection(database, collectionName: collectionName, createCollectionOptions, kmsProvider: null, dataKeyOptions)), expectedParamName: "kmsProvider"); + ShouldBeArgumentException(await Record.ExceptionAsync(() => subject.CreateEncryptedCollectionAsync(database, collectionName, createCollectionOptions, kmsProvider: null, dataKeyOptions)), expectedParamName: "kmsProvider"); + + ShouldBeArgumentException(Record.Exception(() => subject.CreateEncryptedCollection(database, collectionName: collectionName, createCollectionOptions, kmsProvider, dataKeyOptions: null)), expectedParamName: "dataKeyOptions"); + ShouldBeArgumentException(await Record.ExceptionAsync(() => subject.CreateEncryptedCollectionAsync(database, collectionName, createCollectionOptions, kmsProvider, dataKeyOptions: null)), expectedParamName: "dataKeyOptions"); + } + } + + [Fact] + public async Task CreateEncryptedCollection_should_handle_save_generated_key_when_second_key_failed() + { + const string kmsProvider = "local"; + const string collectionName = "collName"; + const string encryptedFieldsStr = "{ fields : [{ keyId : null }, { keyId : null }] }"; + var database = Mock.Of(d => d.DatabaseNamespace == new DatabaseNamespace("db")); + + var dataKeyOptions = new DataKeyOptions(); + + var mockCollection = new Mock>(); + mockCollection + .SetupSequence(c => c.InsertOne(It.IsAny(), It.IsAny(), It.IsAny())) + .Pass() + .Throws(new Exception("test")); + mockCollection + .SetupSequence(c => c.InsertOneAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(Task.CompletedTask) + .Throws(new Exception("test")); + var mockDatabase = new Mock(); + mockDatabase.Setup(c => c.GetCollection(It.IsAny(), It.IsAny())).Returns(mockCollection.Object); + var client = new Mock(); + client.Setup(c => c.GetDatabase(It.IsAny(), It.IsAny())).Returns(mockDatabase.Object); + + using (var subject = CreateSubject(client.Object)) + { + var createCollectionOptions = new CreateCollectionOptions() { EncryptedFields = BsonDocument.Parse(encryptedFieldsStr) }; + var exception = Record.Exception(() => subject.CreateEncryptedCollection(database, collectionName, createCollectionOptions, kmsProvider, dataKeyOptions)); + AssertResults(exception, createCollectionOptions); + + exception = await Record.ExceptionAsync(() => subject.CreateEncryptedCollectionAsync(database, collectionName, createCollectionOptions, kmsProvider, dataKeyOptions)); + AssertResults(exception, createCollectionOptions); + } + + void AssertResults(Exception ex, CreateCollectionOptions createCollectionOptions) + { + var createCollectionException = ex.Should().BeOfType().Subject; + createCollectionException + .InnerException + .Should().BeOfType().Subject.InnerException + .Should().BeOfType().Which.Message + .Should().Be("test"); + var fields = createCollectionException.EncryptedFields["fields"].AsBsonArray; + fields[0].AsBsonDocument["keyId"].Should().BeOfType(); // pass + /* + - If generating `D` resulted in an error `E`, the entire + `CreateEncryptedCollection` must now fail with error `E`. Return the + partially-formed `EF'` with the error so that the caller may know what + datakeys have already been created by the helper. + */ + fields[1].AsBsonDocument["keyId"].Should().BeOfType(); // throw + } + } + + [Theory] + [InlineData(null, "There are no encrypted fields defined for the collection.")] + [InlineData("{}", "{}")] + [InlineData("{ a : 1 }", "{ a : 1 }")] + [InlineData("{ fields : { } }", "{ fields: { } }")] + [InlineData("{ fields : [] }", "{ fields: [] }")] + [InlineData("{ fields : [{ a : 1 }] }", "{ fields: [{ a : 1 }] }")] + [InlineData("{ fields : [{ keyId : 1 }] }", "{ fields: [{ keyId : 1 }] }")] + [InlineData("{ fields : [{ keyId : null }] }", "{ fields: [{ keyId : '#binary_generated#' }] }")] + [InlineData("{ fields : [{ keyId : null }, { keyId : null }] }", "{ fields: [{ keyId : '#binary_generated#' }, { keyId : '#binary_generated#' }] }")] + [InlineData("{ fields : [{ keyId : 3 }, { keyId : null }] }", "{ fields: [{ keyId : 3 }, { keyId : '#binary_generated#' }] }")] + public async Task CreateEncryptedCollection_should_handle_various_encryptedFields(string encryptedFieldsStr, string expectedResult) + { + const string kmsProvider = "local"; + const string collectionName = "collName"; + var database = Mock.Of(d => d.DatabaseNamespace == new DatabaseNamespace("db")); + + var dataKeyOptions = new DataKeyOptions(); + + using (var subject = CreateSubject()) + { + var createCollectionOptions = new CreateCollectionOptions() { EncryptedFields = encryptedFieldsStr != null ? BsonDocument.Parse(encryptedFieldsStr) : null }; + + if (BsonDocument.TryParse(expectedResult, out var encryptedFields)) + { + var createCollectionResult = subject.CreateEncryptedCollection(database, collectionName, createCollectionOptions, kmsProvider, dataKeyOptions); + createCollectionResult.EncryptedFields.WithComparer(new EncryptedFieldsComparer()).Should().Be(encryptedFields.DeepClone()); + + createCollectionResult = await subject.CreateEncryptedCollectionAsync(database, collectionName, createCollectionOptions, kmsProvider, dataKeyOptions); + createCollectionResult.EncryptedFields.WithComparer(new EncryptedFieldsComparer()).Should().Be(encryptedFields.DeepClone()); + } + else + { + AssertInvalidOperationException(Record.Exception(() => subject.CreateEncryptedCollection(database, collectionName, createCollectionOptions, kmsProvider, dataKeyOptions)), expectedResult); + AssertInvalidOperationException(await Record.ExceptionAsync(() => subject.CreateEncryptedCollectionAsync(database, collectionName, createCollectionOptions, kmsProvider, dataKeyOptions)), expectedResult); + } + } + + void AssertInvalidOperationException(Exception ex, string message) => + ex + .Should().BeOfType().Subject.InnerException + .Should().BeOfType().Which.Message.Should().Be(message); + } + + private sealed class EncryptedFieldsComparer : IEqualityComparer + { + public bool Equals(BsonDocument x, BsonDocument y) => + BsonValueEquivalencyComparer.Compare( + x, y, + massageAction: (a, b) => + { + if (a is BsonDocument aDocument && aDocument.TryGetValue("keyId", out var aKeyId) && aKeyId.IsBsonBinaryData && + b is BsonDocument bDocument && bDocument.TryGetValue("keyId", out var bKeyId) && bKeyId == "#binary_generated#") + { + bDocument["keyId"] = aDocument["keyId"]; + } + }); + + public int GetHashCode(BsonDocument obj) => obj.GetHashCode(); + } - [SkippableFact] + [Fact] public void CryptClient_should_be_initialized() { RequireServer.Check().Supports(Feature.ClientSideEncryption); @@ -77,7 +223,7 @@ public void CryptClient_should_be_initialized() } } - [SkippableFact] + [Fact] public async Task Decrypt_should_correctly_handle_input_arguments() { RequireServer.Check().Supports(Feature.ClientSideEncryption); @@ -89,7 +235,7 @@ public async Task Decrypt_should_correctly_handle_input_arguments() } } - [SkippableFact] + [Fact] public async Task Encrypt_should_correctly_handle_input_arguments() { RequireServer.Check().Supports(Feature.ClientSideEncryption); @@ -104,7 +250,7 @@ public async Task Encrypt_should_correctly_handle_input_arguments() } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public async Task Encryption_should_use_correct_binarySubType([Values(false, true)] bool async) { @@ -125,7 +271,7 @@ public async Task Encryption_should_use_correct_binarySubType([Values(false, tru } } - [SkippableFact] + [Fact] public async Task GetKeyByAlternateKeyName_should_correctly_handle_input_arguments() { RequireServer.Check().Supports(Feature.ClientSideEncryption); @@ -137,7 +283,7 @@ public async Task GetKeyByAlternateKeyName_should_correctly_handle_input_argumen } } - [SkippableFact] + [Fact] public async Task RemoveAlternateKeyName_should_correctly_handle_input_arguments() { RequireServer.Check().Supports(Feature.ClientSideEncryption); @@ -151,7 +297,7 @@ public async Task RemoveAlternateKeyName_should_correctly_handle_input_arguments } } - [SkippableFact] + [Fact] public async Task RewrapManyDataKey_should_correctly_handle_input_arguments() { RequireServer.Check().Supports(Feature.ClientSideEncryption); @@ -167,10 +313,10 @@ public async Task RewrapManyDataKey_should_correctly_handle_input_arguments() } // private methods - private ClientEncryption CreateSubject() + private ClientEncryption CreateSubject(IMongoClient client = null) { var clientEncryptionOptions = new ClientEncryptionOptions( - DriverTestConfiguration.Client, + client ?? DriverTestConfiguration.Client, __keyVaultCollectionNamespace, kmsProviders: EncryptionTestHelper.GetKmsProviders(filter: "local")); diff --git a/tests/MongoDB.Driver.Tests/Encryption/EncryptOptionsTests.cs b/tests/MongoDB.Driver.Tests/Encryption/EncryptOptionsTests.cs index 95bcf662d4b..c814350cf88 100644 --- a/tests/MongoDB.Driver.Tests/Encryption/EncryptOptionsTests.cs +++ b/tests/MongoDB.Driver.Tests/Encryption/EncryptOptionsTests.cs @@ -34,7 +34,7 @@ public void Constructor_should_fail_when_contentionFactor_and_algorithm_is_not_i { var exception = Record.Exception(() => new EncryptOptions(algorithm: "test", contentionFactor: 1, keyId: Guid.NewGuid())); var e = exception.Should().BeOfType().Subject; - e.Message.Should().Be("ContentionFactor only applies for Indexed algorithm."); + e.Message.Should().Be("ContentionFactor only applies for Indexed or RangePreview algorithm."); } [Fact] @@ -58,7 +58,15 @@ public void Constructor_should_fail_when_queryType_and_algorithm_is_not_indexed( { var exception = Record.Exception(() => new EncryptOptions(algorithm: "test", queryType: "equality", keyId: Guid.NewGuid())); var e = exception.Should().BeOfType().Subject; - e.Message.Should().Be("QueryType only applies for Indexed algorithm."); + e.Message.Should().Be("QueryType only applies for Indexed or RangePreview algorithm."); + } + + [Fact] + public void Constructor_should_fail_when_rangeOptions_and_algorithm_is_not_rangePreview() + { + var exception = Record.Exception(() => new EncryptOptions(algorithm: "test", keyId: Guid.NewGuid(), rangeOptions: new RangeOptions(0))); + var e = exception.Should().BeOfType().Subject; + e.Message.Should().Be("RangeOptions only applies for RangePreview algorithm."); } [Theory] @@ -75,10 +83,14 @@ public void Constructor_should_fail_when_queryType_and_algorithm_is_not_indexed( [InlineData("TEST_random", "TEST_random")] // just a random value in enum form [InlineData((EncryptionAlgorithm)99, "99")] + // indexed algorithm [InlineData(EncryptionAlgorithm.Indexed, "Indexed")] [InlineData("Indexed", "Indexed")] [InlineData(EncryptionAlgorithm.Unindexed, "Unindexed")] [InlineData("Unindexed", "Unindexed")] + // range preview algorithm + [InlineData(EncryptionAlgorithm.RangePreview, "RangePreview")] + [InlineData("RangePreview", "RangePreview")] public void Constructor_should_support_different_algorithm_representations(object algorithm, string expectedAlgorithmRepresentation) { var alternateKeyName = "test"; @@ -132,11 +144,18 @@ public void With_should_set_correct_values() var fle2State = 2; subject = CreateConfiguredSubject(state: fle2State); subject = subject.With(contentionFactor: newContention); - AssertValues(subject, EncryptionAlgorithm.Indexed.ToString(), expectedKeyId: originalKeyId, expectedContentionFactor: newContention); + AssertValues(subject, expectedAlgorithm: originalAlgorithm, expectedKeyId: originalKeyId, expectedContentionFactor: newContention); + newAlgorithm = EncryptionAlgorithm.Indexed.ToString(); subject = CreateConfiguredSubject(state: fle2State); subject = subject.With(queryType: newQueryType); - AssertValues(subject, EncryptionAlgorithm.Indexed.ToString(), expectedKeyId: originalKeyId, expectedQueryType: newQueryType); + AssertValues(subject, expectedAlgorithm: newAlgorithm, expectedKeyId: originalKeyId, expectedQueryType: newQueryType); + + newQueryType = "rangePreview"; + newAlgorithm = EncryptionAlgorithm.RangePreview.ToString(); + subject = CreateConfiguredSubject(state: fle2State); + subject = subject.With(algorithm: EncryptionAlgorithm.RangePreview.ToString(), queryType: newQueryType); + AssertValues(subject, expectedAlgorithm: newAlgorithm, expectedKeyId: originalKeyId, expectedQueryType: newQueryType); static void AssertValues(EncryptOptions subject, string expectedAlgorithm, Guid? expectedKeyId = null, string expectedAlternateKeyName = null, string expectedQueryType = null, long? expectedContentionFactor = null) { diff --git a/tests/MongoDB.Driver.Tests/Encryption/MongoEncryptionCreateCollectionExceptionTests.cs b/tests/MongoDB.Driver.Tests/Encryption/MongoEncryptionCreateCollectionExceptionTests.cs new file mode 100644 index 00000000000..3223eb3a7a9 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Encryption/MongoEncryptionCreateCollectionExceptionTests.cs @@ -0,0 +1,47 @@ +/* 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.IO; +using System.Runtime.Serialization.Formatters.Binary; +using FluentAssertions; +using MongoDB.Bson; +using MongoDB.Driver.Encryption; +using Xunit; + +namespace MongoDB.Driver.Tests.Encryption +{ + public class MongoEncryptionCreateCollectionExceptionTests + { + [Fact] + public void Serialization_should_work() + { + var subject = new MongoEncryptionCreateCollectionException(new Exception("inner"), new BsonDocument("value", 1)); + + var formatter = new BinaryFormatter(); + using (var stream = new MemoryStream()) + { +#pragma warning disable SYSLIB0011 // BinaryFormatter serialization is obsolete + formatter.Serialize(stream, subject); + stream.Position = 0; + var rehydrated = (MongoEncryptionCreateCollectionException)formatter.Deserialize(stream); +#pragma warning restore SYSLIB0011 // BinaryFormatter serialization is obsolete + + rehydrated.InnerException.Message.Should().Be(subject.InnerException.Message); + rehydrated.EncryptedFields.Should().Be(subject.EncryptedFields).And.Should().NotBeNull(); + } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Encryption/MongoEncryptionExceptionTests.cs b/tests/MongoDB.Driver.Tests/Encryption/MongoEncryptionExceptionTests.cs index 4cae2583cd0..54b56bdf84b 100644 --- a/tests/MongoDB.Driver.Tests/Encryption/MongoEncryptionExceptionTests.cs +++ b/tests/MongoDB.Driver.Tests/Encryption/MongoEncryptionExceptionTests.cs @@ -32,9 +32,11 @@ public void Serialization_should_work() var formatter = new BinaryFormatter(); using (var stream = new MemoryStream()) { +#pragma warning disable SYSLIB0011 // BinaryFormatter serialization is obsolete formatter.Serialize(stream, subject); stream.Position = 0; var rehydrated = (MongoEncryptionException)formatter.Deserialize(stream); +#pragma warning restore SYSLIB0011 // BinaryFormatter serialization is obsolete rehydrated.InnerException.Message.Should().Be(subject.InnerException.Message); } diff --git a/tests/MongoDB.Driver.Tests/EstimatedDocumentCountOptionsTests.cs b/tests/MongoDB.Driver.Tests/EstimatedDocumentCountOptionsTests.cs index 69dafc95bf0..dae0b51ff0b 100644 --- a/tests/MongoDB.Driver.Tests/EstimatedDocumentCountOptionsTests.cs +++ b/tests/MongoDB.Driver.Tests/EstimatedDocumentCountOptionsTests.cs @@ -15,7 +15,7 @@ using System; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.Tests diff --git a/tests/MongoDB.Driver.Tests/FieldDefinitionTests.cs b/tests/MongoDB.Driver.Tests/FieldDefinitionTests.cs index 1aa0d9e09ef..dbbac9fa4c8 100644 --- a/tests/MongoDB.Driver.Tests/FieldDefinitionTests.cs +++ b/tests/MongoDB.Driver.Tests/FieldDefinitionTests.cs @@ -231,7 +231,7 @@ public void Should_resolve_an_array_field_with_field_lambda() var renderedField = subject.Render(BsonSerializer.SerializerRegistry.GetSerializer(), BsonSerializer.SerializerRegistry); renderedField.FieldName.Should().Be("gs"); - renderedField.UnderlyingSerializer.Should().BeOfType, List>>(); + renderedField.UnderlyingSerializer.Should().BeOfType, Gender, List>>(); renderedField.FieldSerializer.Should().BeSameAs(renderedField.UnderlyingSerializer); renderedField.ValueSerializer.Should().BeSameAs(renderedField.FieldSerializer); @@ -245,7 +245,7 @@ public void Should_resolve_an_array_field_with_field_name() var renderedField = subject.Render(BsonSerializer.SerializerRegistry.GetSerializer(), BsonSerializer.SerializerRegistry); renderedField.FieldName.Should().Be("gs"); - renderedField.UnderlyingSerializer.Should().BeOfType, List>>(); + renderedField.UnderlyingSerializer.Should().BeOfType, Gender, List>>(); renderedField.FieldSerializer.Should().BeSameAs(renderedField.UnderlyingSerializer); renderedField.ValueSerializer.Should().BeSameAs(renderedField.FieldSerializer); @@ -259,7 +259,7 @@ public void Should_resolve_an_array_field_with_field_name_and_scalar_value_and_s var renderedField = subject.Render(BsonSerializer.SerializerRegistry.GetSerializer(), BsonSerializer.SerializerRegistry, allowScalarValueForArrayField: true); renderedField.FieldName.Should().Be("gs"); - renderedField.UnderlyingSerializer.Should().BeOfType, List>>(); + renderedField.UnderlyingSerializer.Should().BeOfType, Gender, List>>(); renderedField.FieldSerializer.Should().BeNull(); renderedField.ValueSerializer.Should().BeOfType>(); @@ -273,7 +273,7 @@ public void Should_resolve_an_array_field_with_field_name_and_scalar_value_and_s var renderedField = subject.Render(BsonSerializer.SerializerRegistry.GetSerializer(), BsonSerializer.SerializerRegistry, allowScalarValueForArrayField: false); renderedField.FieldName.Should().Be("gs"); - renderedField.UnderlyingSerializer.Should().BeOfType, List>>(); + renderedField.UnderlyingSerializer.Should().BeOfType, Gender, List>>(); renderedField.FieldSerializer.Should().BeNull(); renderedField.ValueSerializer.ValueType.Should().Be(typeof(Gender)); renderedField.ValueSerializer.GetType().Name.Should().Be("ConvertIfPossibleSerializer`2"); diff --git a/tests/MongoDB.Driver.Tests/FilterDefinitionBuilderEnumComparedToNullableEnumTests.cs b/tests/MongoDB.Driver.Tests/FilterDefinitionBuilderEnumComparedToNullableEnumTests.cs index e5e76526041..4855b953238 100644 --- a/tests/MongoDB.Driver.Tests/FilterDefinitionBuilderEnumComparedToNullableEnumTests.cs +++ b/tests/MongoDB.Driver.Tests/FilterDefinitionBuilderEnumComparedToNullableEnumTests.cs @@ -15,6 +15,7 @@ using FluentAssertions; using MongoDB.Bson.Serialization; +using MongoDB.Driver.Linq; using Xunit; namespace MongoDB.Driver.Tests diff --git a/tests/MongoDB.Driver.Tests/FilterDefinitionBuilderEnumComparedToNullableEnumWithStringRepresentationTests.cs b/tests/MongoDB.Driver.Tests/FilterDefinitionBuilderEnumComparedToNullableEnumWithStringRepresentationTests.cs index 826efdd67cb..ebafb08a130 100644 --- a/tests/MongoDB.Driver.Tests/FilterDefinitionBuilderEnumComparedToNullableEnumWithStringRepresentationTests.cs +++ b/tests/MongoDB.Driver.Tests/FilterDefinitionBuilderEnumComparedToNullableEnumWithStringRepresentationTests.cs @@ -17,6 +17,7 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver.Linq; using Xunit; namespace MongoDB.Driver.Tests diff --git a/tests/MongoDB.Driver.Tests/FilterDefinitionBuilderTests.cs b/tests/MongoDB.Driver.Tests/FilterDefinitionBuilderTests.cs index ac20e6be083..a9164295ac8 100644 --- a/tests/MongoDB.Driver.Tests/FilterDefinitionBuilderTests.cs +++ b/tests/MongoDB.Driver.Tests/FilterDefinitionBuilderTests.cs @@ -20,7 +20,7 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Attributes; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; using MongoDB.Driver.GeoJsonObjectModel; using Xunit; @@ -1244,7 +1244,7 @@ private static bool CreateTestData() } #endregion - [SkippableTheory] + [Theory] [InlineData(0U, new[] { 2, 3, 4, 5, 6, 7 }, "{ $or : [ { X : { $gt : 0 } }, { X : { $lt : 0 } } ] }")] [InlineData(1U, new[] { 3, 4, 5, 6, 7 }, "{ $or : [ { X : { $gt : 1 } }, { X : { $lt : 0 } } ] }")] [InlineData(0x7fffffffU, new[] { 4, 5, 6, 7 }, "{ $or : [ { X : { $gt : 2147483647 } }, { X : { $lt : 0 } } ] }")] @@ -1265,7 +1265,7 @@ public void Gt_UInt32(uint value, int[] expectedIds, string expectedFilter) ids.Should().Equal(expectedIds); } - [SkippableTheory] + [Theory] [InlineData(0U, new[] { 2, 3, 4, 5, 6, 7 }, "{ $or : [ { X : { $gt : 0 } }, { X : { $lt : 0 } } ] }")] [InlineData(1U, new[] { 3, 4, 5, 6, 7 }, "{ $or : [ { X : { $gt : 1 } }, { X : { $lt : 0 } } ] }")] [InlineData(0x7fffffffU, new[] { 4, 5, 6, 7 }, "{ $or : [ { X : { $gt : 2147483647 } }, { X : { $lt : 0 } } ] }")] @@ -1286,7 +1286,7 @@ public void Gt_UInt32_typed(uint value, int[] expectedIds, string expectedFilter ids.Should().Equal(expectedIds); } - [SkippableTheory] + [Theory] [InlineData(0U, new[] { 1, 2, 3, 4, 5, 6, 7 }, "{ $or : [ { X : { $gte : 0 } }, { X : { $lt : 0 } } ] }")] [InlineData(1U, new[] { 2, 3, 4, 5, 6, 7 }, "{ $or : [ { X : { $gte : 1 } }, { X : { $lt : 0 } } ] }")] [InlineData(0x7fffffffU, new[] { 3, 4, 5, 6, 7 }, "{ $or : [ { X : { $gte : 2147483647 } }, { X : { $lt : 0 } } ] }")] @@ -1307,7 +1307,7 @@ public void Gte_UInt32(uint value, int[] expectedIds, string expectedFilter) ids.Should().Equal(expectedIds); } - [SkippableTheory] + [Theory] [InlineData(0U, new[] { 1, 2, 3, 4, 5, 6, 7 }, "{ $or : [ { X : { $gte : 0 } }, { X : { $lt : 0 } } ] }")] [InlineData(1U, new[] { 2, 3, 4, 5, 6, 7 }, "{ $or : [ { X : { $gte : 1 } }, { X : { $lt : 0 } } ] }")] [InlineData(0x7fffffffU, new[] { 3, 4, 5, 6, 7 }, "{ $or : [ { X : { $gte : 2147483647 } }, { X : { $lt : 0 } } ] }")] @@ -1328,7 +1328,7 @@ public void Gte_UInt32_typed(uint value, int[] expectedIds, string expectedFilte ids.Should().Equal(expectedIds); } - [SkippableTheory] + [Theory] [InlineData(0U, new int[0], "{ $and : [ { X : { $gte : 0 } }, { X : { $lt : 0 } } ] }")] [InlineData(1U, new[] { 1 }, "{ $and : [ { X : { $gte : 0 } }, { X : { $lt : 1 } } ] }")] [InlineData(0x7fffffffU, new[] { 1, 2 }, "{ $and : [ { X : { $gte : 0 } }, { X : { $lt : 2147483647 } } ] }")] @@ -1349,7 +1349,7 @@ public void Lt_UInt32(uint value, int[] expectedIds, string expectedFilter) ids.Should().Equal(expectedIds); } - [SkippableTheory] + [Theory] [InlineData(0U, new int[0], "{ $and : [ { X : { $gte : 0 } }, { X : { $lt : 0 } } ] }")] [InlineData(1U, new[] { 1 }, "{ $and : [ { X : { $gte : 0 } }, { X : { $lt : 1 } } ] }")] [InlineData(0x7fffffffU, new[] { 1, 2 }, "{ $and : [ { X : { $gte : 0 } }, { X : { $lt : 2147483647 } } ] }")] @@ -1370,7 +1370,7 @@ public void Lt_UInt32_typed(uint value, int[] expectedIds, string expectedFilter ids.Should().Equal(expectedIds); } - [SkippableTheory] + [Theory] [InlineData(0U, new int[] { 1 }, "{ $and : [ { X : { $gte : 0 } }, { X : { $lte : 0 } } ] }")] [InlineData(1U, new[] { 1, 2 }, "{ $and : [ { X : { $gte : 0 } }, { X : { $lte : 1 } } ] }")] [InlineData(0x7fffffffU, new[] { 1, 2, 3 }, "{ $and : [ { X : { $gte : 0 } }, { X : { $lte : 2147483647 } } ] }")] @@ -1391,7 +1391,7 @@ public void Lte_UInt32(uint value, int[] expectedIds, string expectedFilter) ids.Should().Equal(expectedIds); } - [SkippableTheory] + [Theory] [InlineData(0U, new int[] { 1 }, "{ $and : [ { X : { $gte : 0 } }, { X : { $lte : 0 } } ] }")] [InlineData(1U, new[] { 1, 2 }, "{ $and : [ { X : { $gte : 0 } }, { X : { $lte : 1 } } ] }")] [InlineData(0x7fffffffU, new[] { 1, 2, 3 }, "{ $and : [ { X : { $gte : 0 } }, { X : { $lte : 2147483647 } } ] }")] @@ -1465,7 +1465,7 @@ private static bool CreateTestData() } #endregion - [SkippableTheory] + [Theory] [InlineData(0UL, new[] { 2, 3, 4, 5, 6, 7 }, "{ $or : [ { X : { $gt : NumberLong(0) } }, { X : { $lt : 0 } } ] }")] [InlineData(1UL, new[] { 3, 4, 5, 6, 7 }, "{ $or : [ { X : { $gt : NumberLong(1) } }, { X : { $lt : 0 } } ] }")] [InlineData(0x7fffffffffffffffUL, new[] { 4, 5, 6, 7 }, "{ $or : [ { X : { $gt : NumberLong(9223372036854775807) } }, { X : { $lt : 0 } } ] }")] @@ -1486,7 +1486,7 @@ public void Gt_UInt64(ulong value, int[] expectedIds, string expectedFilter) ids.Should().Equal(expectedIds); } - [SkippableTheory] + [Theory] [InlineData(0UL, new[] { 2, 3, 4, 5, 6, 7 }, "{ $or : [ { X : { $gt : NumberLong(0) } }, { X : { $lt : 0 } } ] }")] [InlineData(1UL, new[] { 3, 4, 5, 6, 7 }, "{ $or : [ { X : { $gt : NumberLong(1) } }, { X : { $lt : 0 } } ] }")] [InlineData(0x7fffffffffffffffUL, new[] { 4, 5, 6, 7 }, "{ $or : [ { X : { $gt : NumberLong(9223372036854775807) } }, { X : { $lt : 0 } } ] }")] @@ -1507,7 +1507,7 @@ public void Gt_UInt64_typed(ulong value, int[] expectedIds, string expectedFilte ids.Should().Equal(expectedIds); } - [SkippableTheory] + [Theory] [InlineData(0UL, new[] { 1, 2, 3, 4, 5, 6, 7 }, "{ $or : [ { X : { $gte : NumberLong(0) } }, { X : { $lt : 0 } } ] }")] [InlineData(1UL, new[] { 2, 3, 4, 5, 6, 7 }, "{ $or : [ { X : { $gte : NumberLong(1) } }, { X : { $lt : 0 } } ] }")] [InlineData(0x7fffffffffffffffUL, new[] { 3, 4, 5, 6, 7 }, "{ $or : [ { X : { $gte : NumberLong(9223372036854775807) } }, { X : { $lt : 0 } } ] }")] @@ -1528,7 +1528,7 @@ public void Gte_UInt64(ulong value, int[] expectedIds, string expectedFilter) ids.Should().Equal(expectedIds); } - [SkippableTheory] + [Theory] [InlineData(0UL, new[] { 1, 2, 3, 4, 5, 6, 7 }, "{ $or : [ { X : { $gte : NumberLong(0) } }, { X : { $lt : 0 } } ] }")] [InlineData(1UL, new[] { 2, 3, 4, 5, 6, 7 }, "{ $or : [ { X : { $gte : NumberLong(1) } }, { X : { $lt : 0 } } ] }")] [InlineData(0x7fffffffffffffffUL, new[] { 3, 4, 5, 6, 7 }, "{ $or : [ { X : { $gte : NumberLong(9223372036854775807) } }, { X : { $lt : 0 } } ] }")] @@ -1549,7 +1549,7 @@ public void Gte_UInt64_typed(ulong value, int[] expectedIds, string expectedFilt ids.Should().Equal(expectedIds); } - [SkippableTheory] + [Theory] [InlineData(0UL, new int[0], "{ $and : [ { X : { $gte : 0 } }, { X : { $lt : NumberLong(0) } } ] }")] [InlineData(1UL, new[] { 1 }, "{ $and : [ { X : { $gte : 0 } }, { X : { $lt : NumberLong(1) } } ] }")] [InlineData(0x7fffffffffffffffUL, new[] { 1, 2 }, "{ $and : [ { X : { $gte : 0 } }, { X : { $lt : NumberLong(9223372036854775807) } } ] }")] @@ -1570,7 +1570,7 @@ public void Lt_UInt64(ulong value, int[] expectedIds, string expectedFilter) ids.Should().Equal(expectedIds); } - [SkippableTheory] + [Theory] [InlineData(0UL, new int[0], "{ $and : [ { X : { $gte : 0 } }, { X : { $lt : NumberLong(0) } } ] }")] [InlineData(1UL, new[] { 1 }, "{ $and : [ { X : { $gte : 0 } }, { X : { $lt : NumberLong(1) } } ] }")] [InlineData(0x7fffffffffffffffUL, new[] { 1, 2 }, "{ $and : [ { X : { $gte : 0 } }, { X : { $lt : NumberLong(9223372036854775807) } } ] }")] @@ -1591,7 +1591,7 @@ public void Lt_UInt64_typed(ulong value, int[] expectedIds, string expectedFilte ids.Should().Equal(expectedIds); } - [SkippableTheory] + [Theory] [InlineData(0UL, new[] { 1 }, "{ $and : [ { X : { $gte : 0 } }, { X : { $lte : NumberLong(0) } } ] }")] [InlineData(1UL, new[] { 1, 2 }, "{ $and : [ { X : { $gte : 0 } }, { X : { $lte : NumberLong(1) } } ] }")] [InlineData(0x7fffffffffffffffUL, new[] { 1, 2, 3 }, "{ $and : [ { X : { $gte : 0 } }, { X : { $lte : NumberLong(9223372036854775807) } } ] }")] @@ -1612,7 +1612,7 @@ public void Lte_UInt64(ulong value, int[] expectedIds, string expectedFilter) ids.Should().Equal(expectedIds); } - [SkippableTheory] + [Theory] [InlineData(0UL, new[] { 1 }, "{ $and : [ { X : { $gte : 0 } }, { X : { $lte : NumberLong(0) } } ] }")] [InlineData(1UL, new[] { 1, 2 }, "{ $and : [ { X : { $gte : 0 } }, { X : { $lte : NumberLong(1) } } ] }")] [InlineData(0x7fffffffffffffffUL, new[] { 1, 2, 3 }, "{ $and : [ { X : { $gte : 0 } }, { X : { $lte : NumberLong(9223372036854775807) } } ] }")] diff --git a/tests/MongoDB.Driver.Tests/FindFluentTests.cs b/tests/MongoDB.Driver.Tests/FindFluentTests.cs index b9ca37b891c..af63aa98b85 100644 --- a/tests/MongoDB.Driver.Tests/FindFluentTests.cs +++ b/tests/MongoDB.Driver.Tests/FindFluentTests.cs @@ -20,10 +20,12 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Linq; using Moq; using Xunit; +using MongoDB.Driver.Core.TestHelpers.XunitExtensions; +using MongoDB.Driver.Core.Misc; namespace MongoDB.Driver.Tests { @@ -303,10 +305,17 @@ public void ToCursor_should_call_collection_Find_with_expected_arguments( } } - [Fact] - public void ToString_should_return_the_correct_string() + [Theory] + [ParameterAttributeData] + public void ToString_should_return_the_correct_string( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { - var subject = CreateSubject(); + if (linqProvider == LinqProvider.V3) + { + RequireServer.Check().Supports(Feature.FindProjectionExpressions); + } + + var subject = CreateSubject(linqProvider: linqProvider); subject.Filter = new BsonDocument("Age", 20); subject.Options.Collation = new Collation("en_US"); subject.Options.Comment = "awesome"; @@ -333,8 +342,12 @@ public void ToString_should_return_the_correct_string() var str = find.ToString(); + var expectedProjection = linqProvider == LinqProvider.V2 ? + "{ \"FirstName\" : 1, \"LastName\" : 1, \"_id\" : 0 }" : + "{ \"_v\" : { \"$concat\" : [\"$FirstName\", \" \", \"$LastName\"] }, \"_id\" : 0 }"; + str.Should().Be( - "find({ \"Age\" : 20 }, { \"FirstName\" : 1, \"LastName\" : 1, \"_id\" : 0 })" + + "find({ \"Age\" : 20 }, " + expectedProjection + ")" + ".collation({ \"locale\" : \"en_US\" })" + ".sort({ \"LastName\" : 1, \"FirstName\" : -1 })" + ".skip(2)" + @@ -356,9 +369,9 @@ private IClientSessionHandle CreateSession(bool usingSession) return usingSession ? Mock.Of() : null; } - private IFindFluent CreateSubject(IClientSessionHandle session = null, FilterDefinition filter = null, FindOptions options = null) + private IFindFluent CreateSubject(IClientSessionHandle session = null, FilterDefinition filter = null, FindOptions options = null, LinqProvider linqProvider = LinqProvider.V3) { - var clientSettings = new MongoClientSettings { LinqProvider = LinqProvider.V2 }; + var clientSettings = new MongoClientSettings { LinqProvider = linqProvider }; var mockClient = new Mock(); mockClient.SetupGet(c => c.Settings).Returns(clientSettings); diff --git a/tests/MongoDB.Driver.Tests/IAggregateFluentExtensionsTests.cs b/tests/MongoDB.Driver.Tests/IAggregateFluentExtensionsTests.cs index a589fa79f55..55d01b4b666 100644 --- a/tests/MongoDB.Driver.Tests/IAggregateFluentExtensionsTests.cs +++ b/tests/MongoDB.Driver.Tests/IAggregateFluentExtensionsTests.cs @@ -17,8 +17,10 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; +using MongoDB.Driver.Linq; +using MongoDB.Driver.Tests.Linq.Linq3ImplementationTests; using Moq; using System; using System.Collections.Generic; @@ -29,7 +31,7 @@ namespace MongoDB.Driver.Tests { - public class IAggregateFluentExtensionsTests + public class IAggregateFluentExtensionsTests : Linq3IntegrationTest { // public methods #if WINDOWS @@ -165,15 +167,29 @@ public void Group_should_generate_the_correct_group_when_a_result_type_is_not_sp AssertLast(subject, expectedGroup); } - [Fact] - public void Group_should_generate_the_correct_document_using_expressions() + [Theory] + [ParameterAttributeData] + public void Group_should_generate_the_correct_document_using_expressions( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { - var subject = CreateSubject() + var collection = GetCollection(linqProvider: linqProvider); + var subject = collection.Aggregate() .Group(x => x.Age, g => new { Name = g.Select(x => x.FirstName + " " + x.LastName).First() }); - var expectedGroup = BsonDocument.Parse("{$group: {_id: '$Age', Name: {'$first': { '$concat': ['$FirstName', ' ', '$LastName']}}}}"); + var stages = Translate(collection, subject); + var expectedStages = linqProvider == LinqProvider.V2 ? + new[] + { + "{ $group : { _id : '$Age', Name : { $first : { $concat : ['$FirstName', ' ', '$LastName'] } } } }" + } + : + new[] + { + "{ $group : { _id : '$Age', __agg0 : { $first : { $concat : ['$FirstName', ' ', '$LastName'] } } } }", + "{ $project : { Name : '$__agg0', _id : 0 } }" + }; - AssertLast(subject, expectedGroup); + AssertStages(stages, expectedStages); } [Fact] @@ -202,7 +218,7 @@ public void Lookup_should_generate_the_correct_group_when_using_lambdas() AssertLast(subject, expectedLookup); } - [SkippableFact] + [Fact] public void Lookup_expressive_should_generate_the_correct_lookup_when_using_BsonDocument() { RequireServer.Check(); @@ -218,7 +234,7 @@ public void Lookup_expressive_should_generate_the_correct_lookup_when_using_Bson AssertLast(subject, expectedLookup); } - [SkippableFact] + [Fact] public void Lookup_expressive_should_generate_the_correct_lookup_when_using_lambdas() { RequireServer.Check(); diff --git a/tests/MongoDB.Driver.Tests/IFindFluentExtensionsTests.cs b/tests/MongoDB.Driver.Tests/IFindFluentExtensionsTests.cs index 8c0b6b2b1bc..afdbd877a87 100644 --- a/tests/MongoDB.Driver.Tests/IFindFluentExtensionsTests.cs +++ b/tests/MongoDB.Driver.Tests/IFindFluentExtensionsTests.cs @@ -20,13 +20,17 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.Serialization; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.Driver.Core.Misc; +using MongoDB.Driver.Core.TestHelpers.XunitExtensions; +using MongoDB.Driver.Linq; +using MongoDB.Driver.Tests.Linq.Linq3ImplementationTests; +using MongoDB.TestHelpers.XunitExtensions; using Moq; using Xunit; namespace MongoDB.Driver.Tests { - public class IFindFluentExtensionsTests + public class IFindFluentExtensionsTests : Linq3IntegrationTest { // public methods [Theory] @@ -228,15 +232,27 @@ public void Project_should_generate_the_correct_fields_when_a_string_is_used() AssertProjection(subject, expectedProjection); } - [Fact] - public void Project_should_generate_the_correct_fields_and_assign_the_correct_result_serializer() + [Theory] + [ParameterAttributeData] + public void Project_should_generate_the_correct_fields_and_assign_the_correct_result_serializer( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { - var subject = CreateSubject() + if (linqProvider == LinqProvider.V3) + { + RequireServer.Check().Supports(Feature.FindProjectionExpressions); + } + + var subject = CreateSubject(linqProvider) .Project(x => x.FirstName + " " + x.LastName); - var expectedProjection = BsonDocument.Parse("{FirstName: 1, LastName: 1, _id: 0}"); + var expectedProjection = linqProvider == LinqProvider.V2 ? + BsonDocument.Parse("{ FirstName : 1, LastName : 1, _id : 0}") : + BsonDocument.Parse("{ _v : { $concat : ['$FirstName', ' ', '$LastName'] }, _id : 0 }"); - AssertProjection(subject, expectedProjection); + AssertProjection(subject, expectedProjection, linqProvider); + + var results = subject.ToList(); + results.Should().Equal("John Doe"); } [Theory] @@ -435,9 +451,9 @@ public void SortByDescending_ThenByDescending_should_generate_the_correct_sort() AssertSort(subject, expectedSort); } - private static void AssertProjection(IFindFluent subject, BsonDocument expectedProjection) + private static void AssertProjection(IFindFluent subject, BsonDocument expectedProjection, LinqProvider linqProvider = LinqProvider.V3) { - Assert.Equal(expectedProjection, subject.Options.Projection.Render(BsonSerializer.SerializerRegistry.GetSerializer(), BsonSerializer.SerializerRegistry).Document); + Assert.Equal(expectedProjection, subject.Options.Projection.Render(BsonSerializer.SerializerRegistry.GetSerializer(), BsonSerializer.SerializerRegistry, linqProvider).Document); } private static void AssertSort(IFindFluent subject, BsonDocument expectedSort) @@ -445,13 +461,21 @@ private static void AssertSort(IFindFluent subject, BsonDocument Assert.Equal(expectedSort, subject.Options.Sort.Render(BsonSerializer.SerializerRegistry.GetSerializer(), BsonSerializer.SerializerRegistry)); } - private IFindFluent CreateSubject() + private IMongoCollection CreateCollection(LinqProvider linqProvider = LinqProvider.V3) + { + var collection = GetCollection(linqProvider: linqProvider); + + CreateCollection( + collection, + new Person { FirstName = "John", LastName = "Doe", Age = 21 }); + + return collection; + } + + private IFindFluent CreateSubject(LinqProvider linqProvider = LinqProvider.V3) { - var settings = new MongoCollectionSettings(); - var mockCollection = new Mock>(); - mockCollection.SetupGet(c => c.Settings).Returns(settings); - var options = new FindOptions(); - return new FindFluent(session: null, collection: mockCollection.Object, filter: new BsonDocument(), options: options); + var collection = CreateCollection(linqProvider); + return collection.Find("{}"); } public class Person diff --git a/tests/MongoDB.Driver.Tests/IMongoClientExtensionsTests.cs b/tests/MongoDB.Driver.Tests/IMongoClientExtensionsTests.cs index e3a103c5ec7..4cd59675a0f 100644 --- a/tests/MongoDB.Driver.Tests/IMongoClientExtensionsTests.cs +++ b/tests/MongoDB.Driver.Tests/IMongoClientExtensionsTests.cs @@ -15,7 +15,7 @@ using System.Threading; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Moq; using Xunit; diff --git a/tests/MongoDB.Driver.Tests/IMongoCollectionExtensionsTests.cs b/tests/MongoDB.Driver.Tests/IMongoCollectionExtensionsTests.cs index 77279086626..b09cf4bcb0a 100644 --- a/tests/MongoDB.Driver.Tests/IMongoCollectionExtensionsTests.cs +++ b/tests/MongoDB.Driver.Tests/IMongoCollectionExtensionsTests.cs @@ -18,7 +18,7 @@ using System.Threading; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Linq; using MongoDB.Driver.Tests.Linq.Linq2ImplementationTests; using MongoDB.Driver.Tests.Linq.Linq3ImplementationTests; @@ -59,9 +59,9 @@ public void Aggregate_should_return_expected_result( [ParameterAttributeData] public void AsQueryable_should_return_expected_result( [Values(false, true)] bool withSession, - [Values(2, 3)] int linqVersion) + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { - var collection = CreateMockCollection(linqVersion).Object; + var collection = CreateMockCollection(linqProvider).Object; var session = withSession ? Mock.Of() : null; var options = new AggregateOptions(); @@ -75,7 +75,7 @@ public void AsQueryable_should_return_expected_result( result = collection.AsQueryable(options); } - if (linqVersion == 2) + if (linqProvider == LinqProvider.V2) { var queryable = result.Should().BeOfType>().Subject; var provider = queryable.Provider.Should().BeOfType>().Subject; @@ -83,8 +83,7 @@ public void AsQueryable_should_return_expected_result( provider._options().Should().BeSameAs(options); provider._session().Should().BeSameAs(session); } - - if (linqVersion == 3) + else { var queryable = result.Should().BeOfType>().Subject; var provider = queryable.Provider.Should().BeOfType>().Subject; @@ -1234,15 +1233,9 @@ public void Watch_should_call_collection_with_expected_arguments( } } - private Mock> CreateMockCollection(int linqVersion = 2) + private Mock> CreateMockCollection(LinqProvider linqProvider = LinqProvider.V2) { var mockClient = new Mock(); - var linqProvider = linqVersion switch - { - 2 => LinqProvider.V2, - 3 => LinqProvider.V3, - _ => throw new ArgumentException($"Invalid linqVersion: {linqVersion}.", nameof(linqVersion)) - }; var clientSettings = new MongoClientSettings { LinqProvider = linqProvider }; mockClient.SetupGet(c => c.Settings).Returns(clientSettings); diff --git a/tests/MongoDB.Driver.Tests/IMongoDatabaseExtensionsTests.cs b/tests/MongoDB.Driver.Tests/IMongoDatabaseExtensionsTests.cs index 7158c95f3da..ac41119a061 100644 --- a/tests/MongoDB.Driver.Tests/IMongoDatabaseExtensionsTests.cs +++ b/tests/MongoDB.Driver.Tests/IMongoDatabaseExtensionsTests.cs @@ -15,7 +15,7 @@ using System.Threading; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Moq; using Xunit; diff --git a/tests/MongoDB.Driver.Tests/Jira/CSharp2564Tests.cs b/tests/MongoDB.Driver.Tests/Jira/CSharp2564Tests.cs index 071c74bfa84..c3147928d60 100644 --- a/tests/MongoDB.Driver.Tests/Jira/CSharp2564Tests.cs +++ b/tests/MongoDB.Driver.Tests/Jira/CSharp2564Tests.cs @@ -30,7 +30,7 @@ namespace MongoDB.Driver.Tests.Jira { public class CSharp2564Tests { - [SkippableFact] + [Fact] public async Task Misbehaved_async_method_should_not_deadlock_server_selection() { RequireServer.Check().ClusterType(ClusterType.ReplicaSet); diff --git a/tests/MongoDB.Driver.Tests/Jira/CSharp3188Tests.cs b/tests/MongoDB.Driver.Tests/Jira/CSharp3188Tests.cs index eace8ca6627..2027d15dab2 100644 --- a/tests/MongoDB.Driver.Tests/Jira/CSharp3188Tests.cs +++ b/tests/MongoDB.Driver.Tests/Jira/CSharp3188Tests.cs @@ -18,7 +18,7 @@ using System.Net.Sockets; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; @@ -28,7 +28,7 @@ namespace MongoDB.Driver.Tests.Jira { public class CSharp3188Tests { - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Connection_timeout_should_throw_expected_exception([Values(false, true)] bool async) { diff --git a/tests/MongoDB.Driver.Tests/Jira/CSharp3915Tests.cs b/tests/MongoDB.Driver.Tests/Jira/CSharp3915Tests.cs index 035d53ae041..f614237004a 100644 --- a/tests/MongoDB.Driver.Tests/Jira/CSharp3915Tests.cs +++ b/tests/MongoDB.Driver.Tests/Jira/CSharp3915Tests.cs @@ -28,7 +28,7 @@ namespace MongoDB.Driver.Tests.Jira public class CSharp3915Tests : Linq3IntegrationTest { // this example is from: https://www.mongodb.com/docs/v5.2/reference/operator/aggregation/densify - [SkippableTheory] + [Theory] [InlineData(false)] [InlineData(true)] public void Densify_time_series_data_example_using_aggregate_should_work(bool usingExpressions) @@ -76,7 +76,7 @@ public void Densify_time_series_data_example_using_aggregate_should_work(bool us } // this example is from: https://www.mongodb.com/docs/v5.2/reference/operator/aggregation/densify - [SkippableFact] + [Fact] public void Densify_time_series_data_example_using_linq_should_work() { RequireServer.Check().Supports(Feature.DensifyStage); @@ -112,7 +112,7 @@ public void Densify_time_series_data_example_using_linq_should_work() } // this example is from: https://www.mongodb.com/docs/v5.2/reference/operator/aggregation/densify - [SkippableTheory] + [Theory] [InlineData(false)] [InlineData(true)] public void Densify_the_full_range_of_values_example_using_aggregate_should_work(bool usingExpressions) @@ -164,7 +164,7 @@ public void Densify_the_full_range_of_values_example_using_aggregate_should_work } // this example is from: https://www.mongodb.com/docs/v5.2/reference/operator/aggregation/densify - [SkippableFact] + [Fact] public void Densify_the_full_range_of_values_example_using_linq_should_work() { RequireServer.Check().Supports(Feature.DensifyStage); @@ -203,7 +203,7 @@ public void Densify_the_full_range_of_values_example_using_linq_should_work() } // this example is from: https://www.mongodb.com/docs/v5.2/reference/operator/aggregation/densify - [SkippableTheory] + [Theory] [InlineData(false)] [InlineData(true)] public void Densify_values_within_each_partition_example_using_aggregate_should_work(bool usingExpressions) @@ -247,7 +247,7 @@ public void Densify_values_within_each_partition_example_using_aggregate_should_ } // this example is from: https://www.mongodb.com/docs/v5.2/reference/operator/aggregation/densify - [SkippableFact] + [Fact] public void Densify_values_within_each_partition_example_using_linq_should_work() { RequireServer.Check().Supports(Feature.DensifyStage); diff --git a/tests/MongoDB.Driver.Tests/Jira/CSharp4172Tests.cs b/tests/MongoDB.Driver.Tests/Jira/CSharp4172Tests.cs index c0a3934ce97..d783b9d56fa 100644 --- a/tests/MongoDB.Driver.Tests/Jira/CSharp4172Tests.cs +++ b/tests/MongoDB.Driver.Tests/Jira/CSharp4172Tests.cs @@ -22,6 +22,7 @@ using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Linq; using MongoDB.Driver.Tests.Linq.Linq3ImplementationTests; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.Tests.Jira @@ -29,9 +30,9 @@ namespace MongoDB.Driver.Tests.Jira public class CSharp4172Tests : Linq3IntegrationTest { [Theory] - [InlineData(LinqProvider.V2)] - [InlineData(LinqProvider.V3)] - public void Find_uses_the_expected_serializer(LinqProvider linqProvider) + [ParameterAttributeData] + public void Find_uses_the_expected_serializer( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { var collection = GetCollection(null, null, linqProvider); @@ -43,9 +44,9 @@ public void Find_uses_the_expected_serializer(LinqProvider linqProvider) } [Theory] - [InlineData(LinqProvider.V2)] - [InlineData(LinqProvider.V3)] - public void Aggregate_uses_the_expected_serializer(LinqProvider linqProvider) + [ParameterAttributeData] + public void Aggregate_uses_the_expected_serializer( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { var collection = GetCollection(null, null, linqProvider); diff --git a/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenTestFactory.cs b/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenTestFactory.cs index dd46600f40f..322e77dac89 100644 --- a/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenTestFactory.cs +++ b/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenTestFactory.cs @@ -19,6 +19,7 @@ using MongoDB.Bson.TestHelpers.JsonDrivenTests; using MongoDB.Driver.Core; using Xunit; +using Xunit.Sdk; namespace MongoDB.Driver.Tests.JsonDrivenTests { diff --git a/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenWaitForEventTest.cs b/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenWaitForEventTest.cs index b014d82f097..71a69daa52e 100644 --- a/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenWaitForEventTest.cs +++ b/tests/MongoDB.Driver.Tests/JsonDrivenTests/JsonDrivenWaitForEventTest.cs @@ -89,7 +89,8 @@ @event is ServerDescriptionChangedEvent serverDescriptionChangedEvent && private void Wait() { var eventCondition = MapEventNameToCondition(_event); - Func, bool> eventsConditionWithFilterByCount = (events) => events.Count(eventCondition) >= _count; + Func, bool> eventsConditionWithFilterByCount = (events) => + events.Where(eventCondition).Take(_count).Count() == _count; _eventCapturer.WaitForOrThrowIfTimeout( eventsConditionWithFilterByCount, diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/AggregateQueryableExecutionModelTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/AggregateQueryableExecutionModelTests.cs index 71641b87c47..7dad1c6e754 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/AggregateQueryableExecutionModelTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/AggregateQueryableExecutionModelTests.cs @@ -16,7 +16,7 @@ using System.Threading; using MongoDB.Bson; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Linq; using Moq; using Xunit; diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/IntegrationTestBase.cs b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/IntegrationTestBase.cs index 865ddcded67..3e3177f7ac6 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/IntegrationTestBase.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/IntegrationTestBase.cs @@ -41,7 +41,7 @@ protected virtual void FillCustomDocuments(List customDocuments) private bool OneTimeSetup(Type type) { - var client = DriverTestConfiguration.Client; + var client = DriverTestConfiguration.Linq2Client; var db = client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName); __collection = db.GetCollection(DriverTestConfiguration.CollectionNamespace.CollectionName); __otherCollection = db.GetCollection(DriverTestConfiguration.CollectionNamespace.CollectionName + "_other"); diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryProviderImplTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryProviderImplTests.cs index abb9802d549..4bccd8615e1 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryProviderImplTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryProviderImplTests.cs @@ -18,7 +18,7 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Linq; using MongoDB.Driver.Linq.Linq2Implementation; using Moq; diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableEnumComparedToEnumTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableEnumComparedToEnumTests.cs index f36111284df..c74ad9e8413 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableEnumComparedToEnumTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableEnumComparedToEnumTests.cs @@ -27,7 +27,7 @@ public class MongoQueryableEnumComparedToEnumTests static MongoQueryableEnumComparedToEnumTests() { - __client = DriverTestConfiguration.Client; + __client = DriverTestConfiguration.Linq2Client; __database = __client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName); __collection = __database.GetCollection(DriverTestConfiguration.CollectionNamespace.CollectionName); } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableEnumComparedToEnumWithStringRepresentationTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableEnumComparedToEnumWithStringRepresentationTests.cs index 5ea1f2db8b6..626335e0775 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableEnumComparedToEnumWithStringRepresentationTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableEnumComparedToEnumWithStringRepresentationTests.cs @@ -29,7 +29,7 @@ public class MongoQueryableEnumComparedToEnumWithStringRepresentationTests static MongoQueryableEnumComparedToEnumWithStringRepresentationTests() { - __client = DriverTestConfiguration.Client; + __client = DriverTestConfiguration.Linq2Client; __database = __client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName); __collection = __database.GetCollection(DriverTestConfiguration.CollectionNamespace.CollectionName); } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableIntArrayComparedToEnumerableIntTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableIntArrayComparedToEnumerableIntTests.cs index 5b2e161998c..1e6980b8649 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableIntArrayComparedToEnumerableIntTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableIntArrayComparedToEnumerableIntTests.cs @@ -28,7 +28,7 @@ public class MongoQueryableIntArrayComparedToEnumerableIntTests static MongoQueryableIntArrayComparedToEnumerableIntTests() { - __client = DriverTestConfiguration.Client; + __client = DriverTestConfiguration.Linq2Client; __database = __client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName); __collection = __database.GetCollection(DriverTestConfiguration.CollectionNamespace.CollectionName); } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableIntComparedToDoubleTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableIntComparedToDoubleTests.cs index 8ebb4192c6d..e4c933f87d9 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableIntComparedToDoubleTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableIntComparedToDoubleTests.cs @@ -27,7 +27,7 @@ public class MongoQueryableIntComparedToDoubleTests static MongoQueryableIntComparedToDoubleTests() { - __client = DriverTestConfiguration.Client; + __client = DriverTestConfiguration.Linq2Client; __database = __client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName); __collection = __database.GetCollection(DriverTestConfiguration.CollectionNamespace.CollectionName); } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableIntComparedToDoubleWithStringRepresentationTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableIntComparedToDoubleWithStringRepresentationTests.cs index 39461546ed0..f1f94f3e012 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableIntComparedToDoubleWithStringRepresentationTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableIntComparedToDoubleWithStringRepresentationTests.cs @@ -29,7 +29,7 @@ public class MongoQueryableIntComparedToDoubleWithStringRepresentationTests static MongoQueryableIntComparedToDoubleWithStringRepresentationTests() { - __client = DriverTestConfiguration.Client; + __client = DriverTestConfiguration.Linq2Client; __database = __client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName); __collection = __database.GetCollection(DriverTestConfiguration.CollectionNamespace.CollectionName); } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableIntComparedToNullableIntTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableIntComparedToNullableIntTests.cs index e976feb0698..1fbab9a6d2c 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableIntComparedToNullableIntTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableIntComparedToNullableIntTests.cs @@ -27,7 +27,7 @@ public class MongoQueryableIntComparedToNullableIntTests static MongoQueryableIntComparedToNullableIntTests() { - __client = DriverTestConfiguration.Client; + __client = DriverTestConfiguration.Linq2Client; __database = __client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName); __collection = __database.GetCollection(DriverTestConfiguration.CollectionNamespace.CollectionName); } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableIntComparedToNullableIntWithStringRepresentationTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableIntComparedToNullableIntWithStringRepresentationTests.cs index 66d5cf01c76..b61c8c5d13b 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableIntComparedToNullableIntWithStringRepresentationTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableIntComparedToNullableIntWithStringRepresentationTests.cs @@ -29,7 +29,7 @@ public class MongoQueryableIntComparedToNullableIntWithStringRepresentationTests static MongoQueryableIntComparedToNullableIntWithStringRepresentationTests() { - __client = DriverTestConfiguration.Client; + __client = DriverTestConfiguration.Linq2Client; __database = __client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName); __collection = __database.GetCollection(DriverTestConfiguration.CollectionNamespace.CollectionName); } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableNullableEnumComparedToNullableEnumTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableNullableEnumComparedToNullableEnumTests.cs index 00a40405fbe..177b89cdca3 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableNullableEnumComparedToNullableEnumTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableNullableEnumComparedToNullableEnumTests.cs @@ -27,7 +27,7 @@ public class MongoQueryableNullableEnumComparedToNullableEnumTests static MongoQueryableNullableEnumComparedToNullableEnumTests() { - __client = DriverTestConfiguration.Client; + __client = DriverTestConfiguration.Linq2Client; __database = __client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName); __collection = __database.GetCollection(DriverTestConfiguration.CollectionNamespace.CollectionName); } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableNullableEnumComparedToNullableEnumWithStringRepresentationTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableNullableEnumComparedToNullableEnumWithStringRepresentationTests.cs index 5055c06b1be..371643de6a5 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableNullableEnumComparedToNullableEnumWithStringRepresentationTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableNullableEnumComparedToNullableEnumWithStringRepresentationTests.cs @@ -29,7 +29,7 @@ public class MongoQueryableNullableEnumComparedToNullableEnumWithStringRepresent static MongoQueryableNullableEnumComparedToNullableEnumWithStringRepresentationTests() { - __client = DriverTestConfiguration.Client; + __client = DriverTestConfiguration.Linq2Client; __database = __client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName); __collection = __database.GetCollection(DriverTestConfiguration.CollectionNamespace.CollectionName); } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableTests.cs index ddc09f3d070..a293d2a62ac 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableTests.cs @@ -179,7 +179,7 @@ public async Task CountAsync_with_no_matches() result.Should().Be(0); } - [SkippableFact] + [Fact] public void Distinct_document_followed_by_where() { RequireServer.Check(); @@ -193,7 +193,7 @@ public void Distinct_document_followed_by_where() "{ $match: { '_id.A': 'Awesome' } }"); } - [SkippableFact] + [Fact] public void Distinct_document_preceded_by_select_where() { RequireServer.Check(); @@ -209,7 +209,7 @@ public void Distinct_document_preceded_by_select_where() "{ $group: { '_id': '$$ROOT' } }"); } - [SkippableFact] + [Fact] public void Distinct_document_preceded_by_where_select() { RequireServer.Check(); @@ -224,7 +224,7 @@ public void Distinct_document_preceded_by_where_select() "{ $group: { '_id': { 'A': '$A', 'B': '$B' } } }"); } - [SkippableFact] + [Fact] public void Distinct_field_preceded_by_where_select() { RequireServer.Check(); @@ -239,7 +239,7 @@ public void Distinct_field_preceded_by_where_select() "{ $group: { '_id': '$A' } }"); } - [SkippableFact] + [Fact] public void Distinct_field_preceded_by_select_where() { RequireServer.Check(); @@ -473,7 +473,7 @@ public void GroupBy_with_resultSelector_anonymous_type_method() "{ $group: { _id: '$A', FirstB: { $first: '$B'} } }"); } - [SkippableFact] + [Fact] public void GroupJoin_method() { RequireServer.Check(); @@ -489,7 +489,7 @@ public void GroupJoin_method() "{ $lookup: { from: 'testcollection_other', localField: '_id', foreignField: '_id', as: 'o' } }"); } - [SkippableFact] + [Fact] public void GroupJoinForeignField_method() { RequireServer.Check(); @@ -505,7 +505,7 @@ public void GroupJoinForeignField_method() "{ $lookup: { from: 'testcollection_other', localField: '_id', foreignField: 'CEF', as: 'o' } }"); } - [SkippableFact] + [Fact] public void GroupJoin_syntax() { RequireServer.Check(); @@ -519,7 +519,7 @@ join o in CreateOtherQuery() on p.Id equals o.Id into joined "{ $project: { A: '$A', SumCEF: { $sum: '$joined.CEF' }, _id: 0 } }"); } - [SkippableFact] + [Fact] public void GroupJoin_syntax_with_a_transparent_identifier() { RequireServer.Check(); @@ -535,7 +535,7 @@ orderby p.B "{ $project: { A: '$A', Joined: '$joined', _id: 0 } }"); } - [SkippableFact] + [Fact] public void GroupJoin_syntax_with_select_many() { RequireServer.Check(); @@ -551,7 +551,7 @@ from subo in joined "{ $project: { A: '$A', CEF: '$joined.CEF', _id: 0 } }"); } - [SkippableFact] + [Fact] public void GroupJoin_syntax_with_select_many_and_DefaultIfEmpty() { RequireServer.Check(); @@ -568,7 +568,7 @@ from subo in joined.DefaultIfEmpty() "{ $project: { A: '$A', CEF: { $ifNull: ['$joined.CEF', null] }, _id: 0 } }"); } - [SkippableFact] + [Fact] public void Join_method() { RequireServer.Check(); @@ -585,7 +585,7 @@ public void Join_method() "{ $unwind: '$o' }"); } - [SkippableFact] + [Fact] public void JoinForeignField_method() { RequireServer.Check(); @@ -602,7 +602,7 @@ public void JoinForeignField_method() "{ $unwind: '$o' }"); } - [SkippableFact] + [Fact] public void Join_syntax() { RequireServer.Check(); @@ -617,7 +617,7 @@ join o in CreateOtherQuery() on p.Id equals o.Id "{ $project: { A: '$A', CEF: '$o.CEF', _id: 0 } }"); } - [SkippableFact] + [Fact] public void Join_syntax_with_a_transparent_identifier() { RequireServer.Check(); @@ -866,7 +866,7 @@ public void OrderBy_ThenBy_ThenByDescending_with_redundant_fields_in_different_d act.ShouldThrow(); } - [SkippableFact] + [Fact] public void Sample() { RequireServer.Check(); @@ -877,7 +877,7 @@ public void Sample() "{ $sample: { size: 100 } }"); } - [SkippableFact] + [Fact] public void Sample_after_another_function() { RequireServer.Check(); @@ -934,7 +934,7 @@ public void Select_method_computed_scalar_followed_by_where() "{ $match: { __fld0: 'Awesome Balloon' } }"); } - [SkippableFact] + [Fact] public void Select_method_with_predicated_any() { RequireServer.Check(); @@ -1056,7 +1056,7 @@ public void Select_syntax_array() "{ $project: { M: '$M', _id: 0 } }"); } - [SkippableFact] + [Fact] public void Select_method_array_index() { RequireServer.Check(); @@ -1067,7 +1067,7 @@ public void Select_method_array_index() "{ $project: { __fld0: { $arrayElemAt: ['$M', 0] }, _id: 0 } }"); } - [SkippableFact] + [Fact] public void Select_syntax_array_index() { RequireServer.Check(); @@ -1079,7 +1079,7 @@ public void Select_syntax_array_index() "{ $project: { __fld0: { $arrayElemAt: ['$M', 0] }, _id: 0 } }"); } - [SkippableFact] + [Fact] public void Select_method_embedded_pipeline() { RequireServer.Check(); @@ -1090,7 +1090,7 @@ public void Select_method_embedded_pipeline() "{ $project: { __fld0: { $arrayElemAt: ['$M', 0] }, _id: 0 } }"); } - [SkippableFact] + [Fact] public void Select_method_computed_array() { RequireServer.Check(); @@ -1102,7 +1102,7 @@ public void Select_method_computed_array() "{ $project: { __fld0: { $map: { input: '$M', as: 'i', in: { $add: ['$$i', 1] } } }, _id: 0 } }"); } - [SkippableFact] + [Fact] public void Select_syntax_computed_array() { RequireServer.Check(); @@ -1546,7 +1546,7 @@ public void Skip() "{ $skip: 10 }"); } - [SkippableFact] + [Fact] public void StandardDeviationPopulation() { RequireServer.Check(); @@ -1556,7 +1556,7 @@ public void StandardDeviationPopulation() result.Should().Be(50); } - [SkippableFact] + [Fact] public void StandardDeviationPopulation_with_selector() { RequireServer.Check(); @@ -1566,7 +1566,7 @@ public void StandardDeviationPopulation_with_selector() result.Should().Be(50); } - [SkippableFact] + [Fact] public async Task StandardDeviationPopulationAsync() { RequireServer.Check(); @@ -1576,7 +1576,7 @@ public async Task StandardDeviationPopulationAsync() result.Should().Be(50); } - [SkippableFact] + [Fact] public async Task StandardDeviationPopulationAsync_with_selector() { RequireServer.Check(); @@ -1586,7 +1586,7 @@ public async Task StandardDeviationPopulationAsync_with_selector() result.Should().Be(50); } - [SkippableFact] + [Fact] public void StandardDeviationSample() { RequireServer.Check(); @@ -1596,7 +1596,7 @@ public void StandardDeviationSample() result.Should().BeApproximately(70.7106781186548, .0001); } - [SkippableFact] + [Fact] public void StandardDeviationSample_with_selector() { RequireServer.Check(); @@ -1606,7 +1606,7 @@ public void StandardDeviationSample_with_selector() result.Should().BeApproximately(70.7106781186548, .0001); } - [SkippableFact] + [Fact] public async Task StandardDeviationSampleAsync() { RequireServer.Check(); @@ -1616,7 +1616,7 @@ public async Task StandardDeviationSampleAsync() result.Should().BeApproximately(70.7106781186548, .0001); } - [SkippableFact] + [Fact] public async Task StandardDeviationSampleAsync_with_selector() { RequireServer.Check(); @@ -1719,7 +1719,7 @@ public void Where_method_with_predicated_any() "{ $match : { 'G' : { '$elemMatch' : { 'D' : \"Don't\" } } } }"); } - [SkippableFact] + [Fact] public void AsQueryable_in_transaction() { RequireServer.Check().ClusterTypes(ClusterType.ReplicaSet, ClusterType.Sharded).Supports(Feature.Transactions); @@ -1728,7 +1728,7 @@ public void AsQueryable_in_transaction() RequireServer.Check().Supports(Feature.ShardedTransactions); } - using (var session = DriverTestConfiguration.Client.StartSession()) + using (var session = DriverTestConfiguration.Linq2Client.StartSession()) { session.StartTransaction(); try diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableWithDotNotationTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableWithDotNotationTests.cs index 0b05f7d7c11..3a4085cd199 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableWithDotNotationTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/MongoQueryableWithDotNotationTests.cs @@ -17,6 +17,7 @@ using System.Collections.Generic; using System.Linq; using FluentAssertions; +using MongoDB.Driver.Linq; using MongoDB.Driver.TestHelpers; using Xunit; @@ -98,6 +99,7 @@ public void Where_with_Contains_should_render_correctly() private DisposableMongoClient CreateDisposableClient() { var mongoClientSettings = MongoClientSettings.FromConnectionString("mongodb://hostnotneeded"); + mongoClientSettings.LinqProvider = LinqProvider.V2; return DriverTestConfiguration.CreateDisposableClient(mongoClientSettings); } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/Translators/AggregateGroupTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/Translators/AggregateGroupTranslatorTests.cs index af2cdbb7fce..03ff1fb006e 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/Translators/AggregateGroupTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/Translators/AggregateGroupTranslatorTests.cs @@ -299,7 +299,7 @@ public void Should_translate_push_with_ToList() result.Value.Result.Should().Equal(111); } - [SkippableFact] + [Fact] public void Should_translate_stdDevPop_with_embedded_projector() { RequireServer.Check(); @@ -311,7 +311,7 @@ public void Should_translate_stdDevPop_with_embedded_projector() result.Value.Result.Should().Be(50); } - [SkippableFact] + [Fact] public void Should_translate_stdDevPop_with_selected_projector() { RequireServer.Check(); @@ -323,7 +323,7 @@ public void Should_translate_stdDevPop_with_selected_projector() result.Value.Result.Should().Be(50); } - [SkippableFact] + [Fact] public void Should_translate_stdDevSamp_with_embedded_projector() { RequireServer.Check(); @@ -335,7 +335,7 @@ public void Should_translate_stdDevSamp_with_embedded_projector() result.Value.Result.Should().BeApproximately(70.7106781156545, .0001); } - [SkippableFact] + [Fact] public void Should_translate_stdDevSamp_with_selected_projector() { RequireServer.Check(); diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/Translators/AggregateProjectTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/Translators/AggregateProjectTranslatorTests.cs index d284572788c..bd31927a996 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/Translators/AggregateProjectTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/Translators/AggregateProjectTranslatorTests.cs @@ -57,7 +57,7 @@ public void Should_translate_using_non_anonymous_type_with_parameterized_constru result.Value.Field.Should().Be("Balloon"); } - [SkippableFact] + [Fact] public void Should_translate_abs() { RequireServer.Check(); @@ -88,7 +88,7 @@ public void Should_translate_add_flattened() result.Value.Result.Should().Be(43); } - [SkippableFact] + [Fact] public void Should_translate_allElementsTrue() { RequireServer.Check(); @@ -100,7 +100,7 @@ public void Should_translate_allElementsTrue() result.Value.Result.Should().BeTrue(); } - [SkippableFact] + [Fact] public void Should_translate_anyElementTrue() { RequireServer.Check(); @@ -112,7 +112,7 @@ public void Should_translate_anyElementTrue() result.Value.Result.Should().BeTrue(); } - [SkippableFact] + [Fact] public void Should_translate_anyElementTrue_with_predicate() { RequireServer.Check(); @@ -124,7 +124,7 @@ public void Should_translate_anyElementTrue_with_predicate() result.Value.Result.Should().BeTrue(); } - [SkippableFact] + [Fact] public void Should_translate_anyElementTrue_using_Contains() { RequireServer.Check(); @@ -136,7 +136,7 @@ public void Should_translate_anyElementTrue_using_Contains() result.Value.Result.Should().BeTrue(); } - [SkippableFact] + [Fact] public void Should_translate_anyElementTrue_using_Contains_on_a_local_collection() { RequireServer.Check(); @@ -169,7 +169,7 @@ public void Should_translate_and_flattened() result.Value.Result.Should().BeFalse(); } - [SkippableFact] + [Fact] public void Should_translate_arrayElemAt_using_a_constant_ElementAt() { RequireServer.Check(); @@ -181,7 +181,7 @@ public void Should_translate_arrayElemAt_using_a_constant_ElementAt() result.Value.Result.Should().Be(4); } - [SkippableFact] + [Fact] public void Should_translate_arrayElemAt_using_a_constant_indexer() { RequireServer.Check(); @@ -193,7 +193,7 @@ public void Should_translate_arrayElemAt_using_a_constant_indexer() result.Value.Result.Should().Be(4); } - [SkippableFact] + [Fact] public void Should_translate_arrayElemAt_using_a_constant_get_Item() { RequireServer.Check(); @@ -205,7 +205,7 @@ public void Should_translate_arrayElemAt_using_a_constant_get_Item() result.Value.Result.Should().Be(20); } - [SkippableFact] + [Fact] public void Should_translate_arrayElemAt_using_a_variable_ElementAt() { RequireServer.Check(); @@ -217,7 +217,7 @@ public void Should_translate_arrayElemAt_using_a_variable_ElementAt() result.Value.Result.Should().Be(4); } - [SkippableFact] + [Fact] public void Should_translate_arrayElemAt_using_a_variable_indexer() { RequireServer.Check(); @@ -229,7 +229,7 @@ public void Should_translate_arrayElemAt_using_a_variable_indexer() result.Value.Result.Should().Be(4); } - [SkippableFact] + [Fact] public void Should_translate_arrayElemAt_using_a_variable_get_Item() { RequireServer.Check(); @@ -241,7 +241,7 @@ public void Should_translate_arrayElemAt_using_a_variable_get_Item() result.Value.Result.Should().Be(20); } - [SkippableFact] + [Fact] public void Should_translate_arrayElemAt_using_a_constant_ElementAt_followed_by_a_field() { RequireServer.Check(); @@ -253,7 +253,7 @@ public void Should_translate_arrayElemAt_using_a_constant_ElementAt_followed_by_ result.Value.Result.Should().Be("Dolphin"); } - [SkippableFact] + [Fact] public void Should_translate_arrayElemAt_using_a_variable_ElementAt_followed_by_a_field() { RequireServer.Check(); @@ -265,7 +265,7 @@ public void Should_translate_arrayElemAt_using_a_variable_ElementAt_followed_by_ result.Value.Result.Should().Be("Dolphin"); } - [SkippableFact] + [Fact] public void Should_translate_arrayElemAt_using_First() { RequireServer.Check(); @@ -277,7 +277,7 @@ public void Should_translate_arrayElemAt_using_First() result.Value.Result.Should().Be(2); } - [SkippableFact] + [Fact] public void Should_translate_arrayElemAt_using_First_followed_by_a_field() { RequireServer.Check(); @@ -289,7 +289,7 @@ public void Should_translate_arrayElemAt_using_First_followed_by_a_field() result.Value.Result.Should().Be("Don't"); } - [SkippableFact] + [Fact] public void Should_translate_arrayElemAt_using_Last() { RequireServer.Check(); @@ -301,7 +301,7 @@ public void Should_translate_arrayElemAt_using_Last() result.Value.Result.Should().Be(5); } - [SkippableFact] + [Fact] public void Should_translate_arrayElemAt_using_Last_followed_by_a_field() { RequireServer.Check(); @@ -313,7 +313,7 @@ public void Should_translate_arrayElemAt_using_Last_followed_by_a_field() result.Value.Result.Should().Be("Dolphin"); } - [SkippableFact] + [Fact] public void Should_translate_avg() { RequireServer.Check(); @@ -325,7 +325,7 @@ public void Should_translate_avg() result.Value.Result.Should().BeApproximately(3.66666667, .0001); } - [SkippableFact] + [Fact] public void Should_translate_avg_with_selector() { RequireServer.Check(); @@ -337,7 +337,7 @@ public void Should_translate_avg_with_selector() result.Value.Result.Should().Be(44); } - [SkippableFact] + [Fact] public void Should_translate_boolToString() { RequireServer.Check().Supports(Feature.AggregateToString); @@ -349,7 +349,7 @@ public void Should_translate_boolToString() result.Value.Result.Should().Be("true"); } - [SkippableFact] + [Fact] public void Should_translate_ceil() { RequireServer.Check(); @@ -381,7 +381,7 @@ public void Should_translate_compare() result.Value.Result.Should().Be(0); } - [SkippableFact] + [Fact] public void Should_translate_concat() { RequireServer.Check(); @@ -393,7 +393,7 @@ public void Should_translate_concat() result.Value.Result.Should().Be("AwesomeBalloon"); } - [SkippableFact] + [Fact] public void Should_translate_concat_flattened() { RequireServer.Check(); @@ -405,7 +405,7 @@ public void Should_translate_concat_flattened() result.Value.Result.Should().Be("Awesome Balloon"); } - [SkippableFact] + [Fact] public void Should_translate_concatArrays() { RequireServer.Check(); @@ -427,7 +427,7 @@ public void Should_translate_condition() result.Value.Result.Should().Be("b"); } - [SkippableFact] + [Fact] public void Should_translate_dateTimeToString() { RequireServer.Check().Supports(Feature.AggregateToString); @@ -439,7 +439,7 @@ public void Should_translate_dateTimeToString() result.Value.Result.Should().Be("2012-12-01T13:14:15.016Z"); } - [SkippableFact] + [Fact] public void Should_translate_dateToString() { RequireServer.Check(); @@ -481,7 +481,7 @@ public void Should_translate_day_of_year() result.Value.Result.Should().Be(336); } - [SkippableFact] + [Fact] public void Should_translate_decimalToString() { RequireServer.Check().Supports(Feature.AggregateToString); @@ -513,7 +513,7 @@ public void Should_translate_divide_3_numbers() result.Value.Result.Should().BeApproximately(0.04, .01); } - [SkippableFact] + [Fact] public void Should_translate_doubleToString() { RequireServer.Check().Supports(Feature.AggregateToString); @@ -545,7 +545,7 @@ public void Should_translate_equals_as_a_method_call() result.Value.Result.Should().BeFalse(); } - [SkippableFact] + [Fact] public void Should_translate_exp() { RequireServer.Check(); @@ -557,7 +557,7 @@ public void Should_translate_exp() result.Value.Result.Should().BeApproximately(59874.1417151978, .0001); } - [SkippableFact] + [Fact] public void Should_translate_floor() { RequireServer.Check(); @@ -599,7 +599,7 @@ public void Should_translate_hour() result.Value.Result.Should().Be(13); } - [SkippableFact] + [Fact] public void Should_translate_indexOfBytes() { RequireServer.Check(); @@ -629,7 +629,7 @@ public void Should_translate_indexOfBytes() result.Value.Result.Should().Be(-1); } - [SkippableFact] + [Fact] public void Should_translate_indexOfCP() { RequireServer.Check(); @@ -659,7 +659,7 @@ public void Should_translate_indexOfCP() result.Value.Result.Should().Be(-1); } - [SkippableFact] + [Fact] public void Should_translate_intToString() { RequireServer.Check().Supports(Feature.AggregateToString); @@ -691,7 +691,7 @@ public void Should_translate_less_than_or_equal() result.Value.Result.Should().BeFalse(); } - [SkippableFact] + [Fact] public void Should_translate_literal_when_a_constant_strings_begins_with_a_dollar() { RequireServer.Check(); @@ -703,7 +703,7 @@ public void Should_translate_literal_when_a_constant_strings_begins_with_a_dolla result.Value.Result.Should().BeFalse(); } - [SkippableFact] + [Fact] public void Should_translate_ln() { RequireServer.Check(); @@ -715,7 +715,7 @@ public void Should_translate_ln() result.Value.Result.Should().BeApproximately(2.39789527279837, .0001); } - [SkippableFact] + [Fact] public void Should_translate_log() { RequireServer.Check(); @@ -727,7 +727,7 @@ public void Should_translate_log() result.Value.Result.Should().Be(1); } - [SkippableFact] + [Fact] public void Should_translate_log10() { RequireServer.Check(); @@ -739,7 +739,7 @@ public void Should_translate_log10() result.Value.Result.Should().BeApproximately(1.0413928515823, .0001); } - [SkippableFact] + [Fact] public void Should_translate_longToString() { RequireServer.Check().Supports(Feature.AggregateToString); @@ -751,7 +751,7 @@ public void Should_translate_longToString() result.Value.Result.Should().Be("9"); } - [SkippableFact] + [Fact] public void Should_translate_map_with_document() { RequireServer.Check(); @@ -763,7 +763,7 @@ public void Should_translate_map_with_document() result.Value.Result.Should().Equal("Don't0", "Dolphin0"); } - [SkippableFact] + [Fact] public void Should_translate_map_with_value() { RequireServer.Check(); @@ -775,7 +775,7 @@ public void Should_translate_map_with_value() result.Value.Result.Should().Equal("it0", "icky0"); } - [SkippableFact] + [Fact] public void Should_translate_max() { RequireServer.Check(); @@ -787,7 +787,7 @@ public void Should_translate_max() result.Value.Result.Should().Be(5); } - [SkippableFact] + [Fact] public void Should_translate_max_with_selector() { RequireServer.Check(); @@ -799,7 +799,7 @@ public void Should_translate_max_with_selector() result.Value.Result.Should().Be(55); } - [SkippableFact] + [Fact] public void Should_translate_millisecond() { RequireServer.Check(); @@ -811,7 +811,7 @@ public void Should_translate_millisecond() result.Value.Result.Should().Be(16); } - [SkippableFact] + [Fact] public void Should_translate_min() { RequireServer.Check(); @@ -823,7 +823,7 @@ public void Should_translate_min() result.Value.Result.Should().Be(2); } - [SkippableFact] + [Fact] public void Should_translate_min_with_selector() { RequireServer.Check(); @@ -885,7 +885,7 @@ public void Should_translate_multiply_flattened() result.Value.Result.Should().Be(2420); } - [SkippableFact] + [Fact] public void Should_translate_new_DateTime() { RequireServer.Check(); @@ -933,7 +933,7 @@ public void Should_translate_not_equals() result.Value.Result.Should().BeTrue(); } - [SkippableFact] + [Fact] public void Should_translate_objectIdToString() { RequireServer.Check().Supports(Feature.AggregateToString); @@ -965,7 +965,7 @@ public void Should_translate_or_flattened() result.Value.Result.Should().BeFalse(); } - [SkippableFact] + [Fact] public void Should_translate_DateTime_parse() { RequireServer.Check(); @@ -977,7 +977,7 @@ public void Should_translate_DateTime_parse() result.Value.Result.Should().Be(new DateTime(2017, 2, 8, 12, 10, 40, 787, DateTimeKind.Utc)); } - [SkippableFact] + [Fact] public void Should_translate_pow() { RequireServer.Check(); @@ -989,7 +989,7 @@ public void Should_translate_pow() result.Value.Result.Should().Be(161051); } - [SkippableFact] + [Fact] public void Should_translate_range() { RequireServer.Check(); @@ -1001,7 +1001,7 @@ public void Should_translate_range() result.Value.Result.Should().BeEquivalentTo(11, 12, 13); } - [SkippableFact] + [Fact] public void Should_translate_reduce() { RequireServer.Check(); @@ -1025,7 +1025,7 @@ public void Should_translate_reduce() typeResult.Value.Result.y.Should().Be(5); } - [SkippableFact] + [Fact] public void Should_translate_reverse() { RequireServer.Check(); @@ -1047,7 +1047,7 @@ public void Should_translate_second() result.Value.Result.Should().Be(15); } - [SkippableFact] + [Fact] public void Should_translate_size_greater_than_zero_from_any() { RequireServer.Check(); @@ -1059,7 +1059,7 @@ public void Should_translate_size_greater_than_zero_from_any() result.Value.Result.Should().BeTrue(); } - [SkippableFact] + [Fact] public void Should_translate_size_from_an_array() { RequireServer.Check(); @@ -1071,7 +1071,7 @@ public void Should_translate_size_from_an_array() result.Value.Result.Should().Be(3); } - [SkippableFact] + [Fact] public void Should_translate_size_from_Count_extension_method() { RequireServer.Check(); @@ -1083,7 +1083,7 @@ public void Should_translate_size_from_Count_extension_method() result.Value.Result.Should().Be(3); } - [SkippableFact] + [Fact] public void Should_translate_size_from_LongCount_extension_method() { RequireServer.Check(); @@ -1095,7 +1095,7 @@ public void Should_translate_size_from_LongCount_extension_method() result.Value.Result.Should().Be(3); } - [SkippableFact] + [Fact] public void Should_translate_size_from_Count_property_on_Generic_ICollection() { RequireServer.Check(); @@ -1107,7 +1107,7 @@ public void Should_translate_size_from_Count_property_on_Generic_ICollection() result.Value.Result.Should().Be(3); } - [SkippableFact] + [Fact] public void Should_translate_set_difference() { RequireServer.Check(); @@ -1119,7 +1119,7 @@ public void Should_translate_set_difference() result.Value.Result.Should().Equal("icky"); } - [SkippableFact] + [Fact] public void Should_translate_set_difference_reversed() { RequireServer.Check(); @@ -1131,7 +1131,7 @@ public void Should_translate_set_difference_reversed() result.Value.Result.Should().Equal("not in here"); } - [SkippableFact] + [Fact] public void Should_translate_set_equals() { RequireServer.Check(); @@ -1143,7 +1143,7 @@ public void Should_translate_set_equals() result.Value.Result.Should().BeTrue(); } - [SkippableFact] + [Fact] public void Should_translate_set_equals_reversed() { RequireServer.Check(); @@ -1156,7 +1156,7 @@ public void Should_translate_set_equals_reversed() result.Value.Result.Should().BeTrue(); } - [SkippableFact] + [Fact] public void Should_translate_set_intersection() { RequireServer.Check(); @@ -1168,7 +1168,7 @@ public void Should_translate_set_intersection() result.Value.Result.Should().Equal("it"); } - [SkippableFact] + [Fact] public void Should_translate_set_intersection_reversed() { RequireServer.Check(); @@ -1180,7 +1180,7 @@ public void Should_translate_set_intersection_reversed() result.Value.Result.Should().Equal("it"); } - [SkippableFact] + [Fact] public void Should_translate_set_is_subset() { RequireServer.Check(); @@ -1192,7 +1192,7 @@ public void Should_translate_set_is_subset() result.Value.Result.Should().BeTrue(); } - [SkippableFact] + [Fact] public void Should_translate_set_is_subset_reversed() { RequireServer.Check(); @@ -1205,7 +1205,7 @@ public void Should_translate_set_is_subset_reversed() result.Value.Result.Should().BeTrue(); } - [SkippableFact] + [Fact] public void Should_translate_set_union() { RequireServer.Check(); @@ -1217,7 +1217,7 @@ public void Should_translate_set_union() result.Value.Result.Should().BeEquivalentTo("it", "icky", "not in here"); } - [SkippableFact] + [Fact] public void Should_translate_set_union_reversed() { RequireServer.Check(); @@ -1229,7 +1229,7 @@ public void Should_translate_set_union_reversed() result.Value.Result.Should().BeEquivalentTo("it", "icky", "not in here"); } - [SkippableFact] + [Fact] public void Should_translate_sqrt() { RequireServer.Check(); @@ -1241,7 +1241,7 @@ public void Should_translate_sqrt() result.Value.Result.Should().BeApproximately(3.31662479, .0001); } - [SkippableFact] + [Fact] public void Should_translate_stringToString() { RequireServer.Check().Supports(Feature.AggregateToString); @@ -1253,7 +1253,7 @@ public void Should_translate_stringToString() result.Value.Result.Should().Be("Awesome"); } - [SkippableFact] + [Fact] public void Should_translate_trunc() { RequireServer.Check(); @@ -1265,7 +1265,7 @@ public void Should_translate_trunc() result.Value.Result.Should().Be(1); } - [SkippableFact] + [Fact] public void Should_translate_where_to_filter() { RequireServer.Check(); @@ -1278,7 +1278,7 @@ public void Should_translate_where_to_filter() result.Value.Result.Single().D.Should().Be("Don't"); } - [SkippableFact] + [Fact] public void Should_translate_where_then_select_to_filter_then_map() { RequireServer.Check(); @@ -1291,7 +1291,7 @@ public void Should_translate_where_then_select_to_filter_then_map() result.Value.Result.Single().Should().Be("Don't"); } - [SkippableFact] + [Fact] public void Should_translate_select_then_where_to_map_then_filter() { RequireServer.Check(); @@ -1304,7 +1304,7 @@ public void Should_translate_select_then_where_to_map_then_filter() result.Value.Result.Single().Should().Be("Don't"); } - [SkippableFact] + [Fact] public void Should_translate_select_with_an_anonymous_type_then_where_to_map_then_filter() { RequireServer.Check(); @@ -1318,7 +1318,7 @@ public void Should_translate_select_with_an_anonymous_type_then_where_to_map_the result.Value.Result.Single().F.Should().Be(33); } - [SkippableFact] + [Fact] public void Should_translate_strLenBytes() { RequireServer.Check(); @@ -1330,7 +1330,7 @@ public void Should_translate_strLenBytes() result.Value.Result.Should().Be(7); } - [SkippableFact] + [Fact] public void Should_translate_strLenCP() { RequireServer.Check(); @@ -1342,7 +1342,7 @@ public void Should_translate_strLenCP() result.Value.Result.Should().Be(7); } - [SkippableFact] + [Fact] public void Should_translate_split() { RequireServer.Check(); @@ -1368,7 +1368,7 @@ public void Should_translate_split() result4.Value.Result.Should().BeEquivalentTo("Aw", "ome"); } - [SkippableFact] + [Fact] public void Should_translate_stdDevPop() { RequireServer.Check(); @@ -1380,7 +1380,7 @@ public void Should_translate_stdDevPop() result.Value.Result.Should().BeApproximately(1.247219128924647, .0001); } - [SkippableFact] + [Fact] public void Should_translate_stdDevPop_with_selector() { RequireServer.Check(); @@ -1392,7 +1392,7 @@ public void Should_translate_stdDevPop_with_selector() result.Value.Result.Should().Be(11); } - [SkippableFact] + [Fact] public void Should_translate_stdDevSamp() { RequireServer.Check(); @@ -1404,7 +1404,7 @@ public void Should_translate_stdDevSamp() result.Value.Result.Should().BeApproximately(1.5275252316519468, .0001); } - [SkippableFact] + [Fact] public void Should_translate_stdDevSamp_with_selector() { RequireServer.Check(); @@ -1478,7 +1478,7 @@ public void Should_translate_substr() result.Value.Result.Should().Be("loon"); } - [SkippableFact] + [Fact] public void Should_translate_substrCP() { RequireServer.Check(); @@ -1510,7 +1510,7 @@ public void Should_translate_subtract_3_numbers() result.Value.Result.Should().Be(-23); } - [SkippableFact] + [Fact] public void Should_translate_slice_with_2_arguments() { RequireServer.Check(); @@ -1522,7 +1522,7 @@ public void Should_translate_slice_with_2_arguments() result.Value.Result.Should().BeEquivalentTo(2, 4); } - [SkippableFact] + [Fact] public void Should_translate_slice_with_3_arguments() { RequireServer.Check(); @@ -1534,7 +1534,7 @@ public void Should_translate_slice_with_3_arguments() result.Value.Result.Should().BeEquivalentTo(4, 5); } - [SkippableFact] + [Fact] public void Should_translate_sum() { RequireServer.Check(); @@ -1546,7 +1546,7 @@ public void Should_translate_sum() result.Value.Result.Should().Be(11); } - [SkippableFact] + [Fact] public void Should_translate_sum_with_selector() { RequireServer.Check(); @@ -1608,7 +1608,7 @@ public void Should_translate_year() result.Value.Result.Should().Be(2012); } - [SkippableFact] + [Fact] public void Should_translate_zip_with_operation() { RequireServer.Check(); @@ -1620,7 +1620,7 @@ public void Should_translate_zip_with_operation() result.Value.Result.Should().BeEquivalentTo(12L, 24L, 35L); } - [SkippableFact] + [Fact] public void Should_translate_zip_with_anonymous_type() { RequireServer.Check(); @@ -1657,7 +1657,7 @@ public void Should_translate_a_derived_class_projection() result.Value.DerivedProperty.Should().Be("Balloon"); } - [SkippableFact] + [Fact] public void Should_translate_array_projection_complex() { RequireServer.Check(); diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/Translators/LegacyPredicateTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/Translators/LegacyPredicateTranslatorTests.cs index ff8666b722d..87e82f269ec 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/Translators/LegacyPredicateTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/Translators/LegacyPredicateTranslatorTests.cs @@ -51,7 +51,7 @@ public LegacyPredicateTranslatorTests() public bool OneTimeSetup() { - __database = DriverTestConfiguration.Client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName); + __database = DriverTestConfiguration.Linq2Client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName); __collection = __database.GetCollection(DriverTestConfiguration.CollectionNamespace.CollectionName); // documents inserted deliberately out of order to test sorting diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/Translators/PredicateTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/Translators/PredicateTranslatorTests.cs index ceede91b1cf..21656133d03 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/Translators/PredicateTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/Translators/PredicateTranslatorTests.cs @@ -477,7 +477,7 @@ public void Any_with_a_predicate_on_scalars_legacy() "{ M : { $elemMatch : { $gt : 2, $lt : 6 } } }"); } - [SkippableFact] + [Fact] public void Any_with_a_predicate_on_scalars() { RequireServer.Check(); @@ -545,7 +545,7 @@ public void AsQueryable() "{ 'G' : { '$elemMatch' : { 'D' : \"Don't\" } } }"); } - [SkippableFact] + [Fact] public void BitsAllClear_with_bitwise_operators() { RequireServer.Check(); @@ -556,7 +556,7 @@ public void BitsAllClear_with_bitwise_operators() "{'C.E.F': { $bitsAllClear: 20 } }"); } - [SkippableFact] + [Fact] public void BitsAllSet_with_bitwise_operators() { RequireServer.Check(); @@ -567,7 +567,7 @@ public void BitsAllSet_with_bitwise_operators() "{'C.E.F': { $bitsAllSet: 7 } }"); } - [SkippableFact] + [Fact] public void BitsAllSet_with_HasFlag() { RequireServer.Check(); @@ -578,7 +578,7 @@ public void BitsAllSet_with_HasFlag() "{Q: { $bitsAllSet: 1 } }"); } - [SkippableFact] + [Fact] public void BitsAnyClear_with_bitwise_operators() { RequireServer.Check(); @@ -589,7 +589,7 @@ public void BitsAnyClear_with_bitwise_operators() "{'C.E.F': { $bitsAnyClear: 7 } }"); } - [SkippableFact] + [Fact] public void BitsAnySet_with_bitwise_operators() { RequireServer.Check(); diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/Translators/PredicateTranslatorValidationTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/Translators/PredicateTranslatorValidationTests.cs index 4fe66f5f1ab..936d204b645 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/Translators/PredicateTranslatorValidationTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTests/Translators/PredicateTranslatorValidationTests.cs @@ -31,7 +31,7 @@ public class PredicateTranslatorValidationTests public void Setup() { - var client = DriverTestConfiguration.Client; + var client = DriverTestConfiguration.Linq2Client; var database = client.GetDatabase("test"); _collection = database.GetCollection("testObject"); database.DropCollection("testObject"); diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTestsOnLinq3/MongoQueryableTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTestsOnLinq3/MongoQueryableTests.cs index 054d5df1405..9f411b0c323 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTestsOnLinq3/MongoQueryableTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTestsOnLinq3/MongoQueryableTests.cs @@ -189,7 +189,7 @@ public async Task CountAsync_with_no_matches() result.Should().Be(0); } - [SkippableFact] + [Fact] public void Distinct_document_followed_by_where() { RequireServer.Check(); @@ -204,7 +204,7 @@ public void Distinct_document_followed_by_where() "{ $match: { A: 'Awesome' } }"); } - [SkippableFact] + [Fact] public void Distinct_document_preceded_by_select_where() { RequireServer.Check(); @@ -221,7 +221,7 @@ public void Distinct_document_preceded_by_select_where() "{ $replaceRoot : { newRoot : '$_id' } }"); } - [SkippableFact] + [Fact] public void Distinct_document_preceded_by_where_select() { RequireServer.Check(); @@ -238,7 +238,7 @@ public void Distinct_document_preceded_by_where_select() "{ $replaceRoot : { newRoot : '$_id' } }"); } - [SkippableFact] + [Fact] public void Distinct_field_preceded_by_where_select() { RequireServer.Check(); @@ -255,7 +255,7 @@ public void Distinct_field_preceded_by_where_select() "{ $replaceRoot : { newRoot : '$_id' } }"); } - [SkippableFact] + [Fact] public void Distinct_field_preceded_by_select_where() { RequireServer.Check(); @@ -549,7 +549,7 @@ public void GroupBy_with_resultSelector_anonymous_type_method2() "{ $project : { Key : '$_id', FirstB : '$__agg0', _id : 0 } }"); } - [SkippableFact] + [Fact] public void GroupJoin_method() { RequireServer.Check(); @@ -567,7 +567,7 @@ public void GroupJoin_method() "{ $project : { p : '$_outer', o : '$_inner', _id : 0 } }"); } - [SkippableFact] + [Fact] public void GroupJoinForeignField_method() { RequireServer.Check(); @@ -585,7 +585,7 @@ public void GroupJoinForeignField_method() "{ $project : { p : '$_outer', o : '$_inner', _id : 0 } }"); } - [SkippableFact] + [Fact] public void GroupJoin_syntax() { RequireServer.Check(); @@ -600,7 +600,7 @@ join o in CreateOtherQuery() on p.Id equals o.Id into joined "{ $project : { A : '$_outer.A', SumCEF : { $sum : '$_inner.CEF' }, _id : 0 } }"); } - [SkippableFact] + [Fact] public void GroupJoin_syntax_with_a_transparent_identifier() { RequireServer.Check(); @@ -618,7 +618,7 @@ orderby p.B "{ $project : { A : '$p.A', Joined : '$joined', _id : 0 } }"); } - [SkippableFact] + [Fact] public void GroupJoin_syntax_with_select_many() { RequireServer.Check(); @@ -637,7 +637,7 @@ from subo in joined "{ $unwind : '$_v' }"); } - [SkippableFact] + [Fact] public void GroupJoin_syntax_with_select_many_and_DefaultIfEmpty() { RequireServer.Check(); @@ -656,7 +656,7 @@ join o in otherCollection on p.Id equals o.Id into joined "{ $unwind : '$_v' }"); } - [SkippableFact] + [Fact] public void Join_method() { RequireServer.Check(); @@ -675,7 +675,7 @@ public void Join_method() "{ $project : { p : '$_outer', o : '$_inner', _id : 0 } }"); } - [SkippableFact] + [Fact] public void JoinForeignField_method() { RequireServer.Check(); @@ -694,7 +694,7 @@ public void JoinForeignField_method() "{ $project : { p : '$_outer', o : '$_inner', _id : 0 } }"); } - [SkippableFact] + [Fact] public void Join_syntax() { RequireServer.Check(); @@ -710,7 +710,7 @@ join o in CreateOtherQuery() on p.Id equals o.Id "{ $project : { A : '$_outer.A', CEF : '$_inner.CEF', _id : 0 } }"); } - [SkippableFact] + [Fact] public void Join_syntax_with_a_transparent_identifier() { RequireServer.Check(); @@ -961,7 +961,7 @@ public void OrderBy_ThenBy_ThenByDescending_with_redundant_fields_in_different_d act.ShouldThrow(); } - [SkippableFact] + [Fact] public void Sample() { RequireServer.Check(); @@ -972,7 +972,7 @@ public void Sample() "{ $sample: { size: 100 } }"); } - [SkippableFact] + [Fact] public void Sample_after_another_function() { RequireServer.Check(); @@ -1032,7 +1032,7 @@ public void Select_method_computed_scalar_followed_by_where() "{ $match : { _v : 'Awesome Balloon' } }"); } - [SkippableFact] + [Fact] public void Select_method_with_predicated_any() { RequireServer.Check(); @@ -1154,7 +1154,7 @@ public void Select_syntax_array() "{ $project : { _v : '$M', _id : 0 } }"); } - [SkippableFact] + [Fact] public void Select_method_array_index() { RequireServer.Check(); @@ -1165,7 +1165,7 @@ public void Select_method_array_index() "{ $project : { _v : { $arrayElemAt : ['$M', 0] }, _id : 0 } }"); } - [SkippableFact] + [Fact] public void Select_syntax_array_index() { RequireServer.Check(); @@ -1177,7 +1177,7 @@ public void Select_syntax_array_index() "{ $project : { _v : { $arrayElemAt : ['$M', 0] }, _id : 0 } }"); } - [SkippableFact] + [Fact] public void Select_method_embedded_pipeline() { RequireServer.Check(); @@ -1188,7 +1188,7 @@ public void Select_method_embedded_pipeline() "{ $project : { _v : { $arrayElemAt : ['$M', 0] }, _id : 0 } }"); } - [SkippableFact] + [Fact] public void Select_method_computed_array() { RequireServer.Check(); @@ -1200,7 +1200,7 @@ public void Select_method_computed_array() "{ $project : { _v : { $map : { input : '$M', as : 'i', in : { $add : ['$$i', 1] } } }, _id : 0 } }"); } - [SkippableFact] + [Fact] public void Select_syntax_computed_array() { RequireServer.Check(); @@ -1537,8 +1537,8 @@ group f by f.D into g 4, "{ $project : { _v : '$G', _id : 0 } }", "{ $unwind : '$_v' }", - "{ $group : { _id : '$_v.D', _elements : { $push : '$_v' } } }", - "{ $project : { Key : '$_id', SumF : { $sum : '$_elements.E.F' }, _id : 0 } }"); + "{ $group : { _id : '$_v.D', __agg0 : { $sum : '$_v.E.F' } } }", + "{ $project : { Key : '$_id', SumF : '$__agg0', _id : 0 } }"); } [Fact] @@ -1567,8 +1567,8 @@ group s by s.D into g "{ $unwind : '$_v' }", "{ $project : { '_v' : '$_v.S', '_id' : 0 } }", "{ $unwind : '$_v' }", - "{ $group : { _id : '$_v.D', _elements : { $push : '$_v' } } }", - "{ $project : { Key : '$_id', SumF : { $sum : '$_elements.E.F' }, _id : 0 } }"); + "{ $group : { _id : '$_v.D', __agg0 : { $sum : '$_v.E.F' } } }", + "{ $project : { Key : '$_id', SumF : '$__agg0', _id : 0 } }"); } [Fact] @@ -1645,7 +1645,7 @@ public void Skip() "{ $skip: 10 }"); } - [SkippableFact] + [Fact] public void StandardDeviationPopulation() { RequireServer.Check(); @@ -1655,7 +1655,7 @@ public void StandardDeviationPopulation() result.Should().Be(50); } - [SkippableFact] + [Fact] public void StandardDeviationPopulation_with_selector() { RequireServer.Check(); @@ -1665,7 +1665,7 @@ public void StandardDeviationPopulation_with_selector() result.Should().Be(50); } - [SkippableFact] + [Fact] public async Task StandardDeviationPopulationAsync() { RequireServer.Check(); @@ -1675,7 +1675,7 @@ public async Task StandardDeviationPopulationAsync() result.Should().Be(50); } - [SkippableFact] + [Fact] public async Task StandardDeviationPopulationAsync_with_selector() { RequireServer.Check(); @@ -1685,7 +1685,7 @@ public async Task StandardDeviationPopulationAsync_with_selector() result.Should().Be(50); } - [SkippableFact] + [Fact] public void StandardDeviationSample() { RequireServer.Check(); @@ -1695,7 +1695,7 @@ public void StandardDeviationSample() result.Should().BeApproximately(70.7106781186548, .0001); } - [SkippableFact] + [Fact] public void StandardDeviationSample_with_selector() { RequireServer.Check(); @@ -1705,7 +1705,7 @@ public void StandardDeviationSample_with_selector() result.Should().BeApproximately(70.7106781186548, .0001); } - [SkippableFact] + [Fact] public async Task StandardDeviationSampleAsync() { RequireServer.Check(); @@ -1715,7 +1715,7 @@ public async Task StandardDeviationSampleAsync() result.Should().BeApproximately(70.7106781186548, .0001); } - [SkippableFact] + [Fact] public async Task StandardDeviationSampleAsync_with_selector() { RequireServer.Check(); @@ -1818,7 +1818,7 @@ public void Where_method_with_predicated_any() "{ $match : { 'G' : { '$elemMatch' : { 'D' : \"Don't\" } } } }"); } - [SkippableFact] + [Fact] public void AsQueryable_in_transaction() { RequireServer.Check().ClusterTypes(ClusterType.ReplicaSet, ClusterType.Sharded).Supports(Feature.Transactions); @@ -1827,7 +1827,7 @@ public void AsQueryable_in_transaction() RequireServer.Check().Supports(Feature.ShardedTransactions); } - using (var session = DriverTestConfiguration.Client.StartSession()) + using (var session = DriverTestConfiguration.Linq3Client.StartSession()) { session.StartTransaction(); try diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTestsOnLinq3/Translators/AggregateGroupTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTestsOnLinq3/Translators/AggregateGroupTranslatorTests.cs index 1d2734c74fa..891be65801b 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTestsOnLinq3/Translators/AggregateGroupTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTestsOnLinq3/Translators/AggregateGroupTranslatorTests.cs @@ -387,7 +387,7 @@ public void Should_translate_push_with_ToList() result.Value.Result.Should().Equal(111); } - [SkippableFact] + [Fact] public void Should_translate_stdDevPop_with_embedded_projector() { RequireServer.Check(); @@ -402,7 +402,7 @@ public void Should_translate_stdDevPop_with_embedded_projector() result.Value.Result.Should().Be(50); } - [SkippableFact] + [Fact] public void Should_translate_stdDevPop_with_selected_projector() { RequireServer.Check(); @@ -417,7 +417,7 @@ public void Should_translate_stdDevPop_with_selected_projector() result.Value.Result.Should().Be(50); } - [SkippableFact] + [Fact] public void Should_translate_stdDevSamp_with_embedded_projector() { RequireServer.Check(); @@ -432,7 +432,7 @@ public void Should_translate_stdDevSamp_with_embedded_projector() result.Value.Result.Should().BeApproximately(70.7106781156545, .0001); } - [SkippableFact] + [Fact] public void Should_translate_stdDevSamp_with_selected_projector() { RequireServer.Check(); diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTestsOnLinq3/Translators/AggregateProjectTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTestsOnLinq3/Translators/AggregateProjectTranslatorTests.cs index 5a3e43ddd72..197cd04eac2 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTestsOnLinq3/Translators/AggregateProjectTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTestsOnLinq3/Translators/AggregateProjectTranslatorTests.cs @@ -58,7 +58,7 @@ public void Should_translate_using_non_anonymous_type_with_parameterized_constru result.Value.Field.Should().Be("Balloon"); } - [SkippableFact] + [Fact] public void Should_translate_abs() { RequireServer.Check(); @@ -89,7 +89,7 @@ public void Should_translate_add_flattened() result.Value.Result.Should().Be(43); } - [SkippableFact] + [Fact] public void Should_translate_allElementsTrue() { RequireServer.Check(); @@ -101,7 +101,7 @@ public void Should_translate_allElementsTrue() result.Value.Result.Should().BeTrue(); } - [SkippableFact] + [Fact] public void Should_translate_anyElementTrue() { RequireServer.Check(); @@ -113,7 +113,7 @@ public void Should_translate_anyElementTrue() result.Value.Result.Should().BeTrue(); } - [SkippableFact] + [Fact] public void Should_translate_anyElementTrue_with_predicate() { RequireServer.Check(); @@ -125,7 +125,7 @@ public void Should_translate_anyElementTrue_with_predicate() result.Value.Result.Should().BeTrue(); } - [SkippableFact] + [Fact] public void Should_translate_anyElementTrue_using_Contains() { RequireServer.Check(); @@ -137,7 +137,7 @@ public void Should_translate_anyElementTrue_using_Contains() result.Value.Result.Should().BeTrue(); } - [SkippableFact] + [Fact] public void Should_translate_anyElementTrue_using_Contains_on_a_local_collection() { RequireServer.Check(); @@ -170,7 +170,7 @@ public void Should_translate_and_flattened() result.Value.Result.Should().BeFalse(); } - [SkippableFact] + [Fact] public void Should_translate_arrayElemAt_using_a_constant_ElementAt() { RequireServer.Check(); @@ -182,7 +182,7 @@ public void Should_translate_arrayElemAt_using_a_constant_ElementAt() result.Value.Result.Should().Be(4); } - [SkippableFact] + [Fact] public void Should_translate_arrayElemAt_using_a_constant_indexer() { RequireServer.Check(); @@ -194,7 +194,7 @@ public void Should_translate_arrayElemAt_using_a_constant_indexer() result.Value.Result.Should().Be(4); } - [SkippableFact] + [Fact] public void Should_translate_arrayElemAt_using_a_constant_get_Item() { RequireServer.Check(); @@ -206,7 +206,7 @@ public void Should_translate_arrayElemAt_using_a_constant_get_Item() result.Value.Result.Should().Be(20); } - [SkippableFact] + [Fact] public void Should_translate_arrayElemAt_using_a_variable_ElementAt() { RequireServer.Check(); @@ -218,7 +218,7 @@ public void Should_translate_arrayElemAt_using_a_variable_ElementAt() result.Value.Result.Should().Be(4); } - [SkippableFact] + [Fact] public void Should_translate_arrayElemAt_using_a_variable_indexer() { RequireServer.Check(); @@ -230,7 +230,7 @@ public void Should_translate_arrayElemAt_using_a_variable_indexer() result.Value.Result.Should().Be(4); } - [SkippableFact] + [Fact] public void Should_translate_arrayElemAt_using_a_variable_get_Item() { RequireServer.Check(); @@ -242,7 +242,7 @@ public void Should_translate_arrayElemAt_using_a_variable_get_Item() result.Value.Result.Should().Be(20); } - [SkippableFact] + [Fact] public void Should_translate_arrayElemAt_using_a_constant_ElementAt_followed_by_a_field() { RequireServer.Check(); @@ -254,7 +254,7 @@ public void Should_translate_arrayElemAt_using_a_constant_ElementAt_followed_by_ result.Value.Result.Should().Be("Dolphin"); } - [SkippableFact] + [Fact] public void Should_translate_arrayElemAt_using_a_variable_ElementAt_followed_by_a_field() { RequireServer.Check(); @@ -266,7 +266,7 @@ public void Should_translate_arrayElemAt_using_a_variable_ElementAt_followed_by_ result.Value.Result.Should().Be("Dolphin"); } - [SkippableFact] + [Fact] public void Should_translate_arrayElemAt_using_First() { RequireServer.Check(); @@ -278,7 +278,7 @@ public void Should_translate_arrayElemAt_using_First() result.Value.Result.Should().Be(2); } - [SkippableFact] + [Fact] public void Should_translate_arrayElemAt_using_First_followed_by_a_field() { RequireServer.Check(); @@ -290,7 +290,7 @@ public void Should_translate_arrayElemAt_using_First_followed_by_a_field() result.Value.Result.Should().Be("Don't"); } - [SkippableFact] + [Fact] public void Should_translate_arrayElemAt_using_Last() { RequireServer.Check(); @@ -302,7 +302,7 @@ public void Should_translate_arrayElemAt_using_Last() result.Value.Result.Should().Be(5); } - [SkippableFact] + [Fact] public void Should_translate_arrayElemAt_using_Last_followed_by_a_field() { RequireServer.Check(); @@ -314,7 +314,7 @@ public void Should_translate_arrayElemAt_using_Last_followed_by_a_field() result.Value.Result.Should().Be("Dolphin"); } - [SkippableFact] + [Fact] public void Should_translate_avg() { RequireServer.Check(); @@ -326,7 +326,7 @@ public void Should_translate_avg() result.Value.Result.Should().BeApproximately(3.66666667, .0001); } - [SkippableFact] + [Fact] public void Should_translate_avg_with_selector() { RequireServer.Check(); @@ -338,7 +338,7 @@ public void Should_translate_avg_with_selector() result.Value.Result.Should().Be(44); } - [SkippableFact] + [Fact] public void Should_translate_boolToString() { RequireServer.Check().Supports(Feature.AggregateToString); @@ -350,7 +350,7 @@ public void Should_translate_boolToString() result.Value.Result.Should().Be("true"); } - [SkippableFact] + [Fact] public void Should_translate_ceil() { RequireServer.Check(); @@ -382,7 +382,7 @@ public void Should_translate_compare() result.Value.Result.Should().Be(0); } - [SkippableFact] + [Fact] public void Should_translate_concat() { RequireServer.Check(); @@ -394,7 +394,7 @@ public void Should_translate_concat() result.Value.Result.Should().Be("AwesomeBalloon"); } - [SkippableFact] + [Fact] public void Should_translate_concat_flattened() { RequireServer.Check(); @@ -406,7 +406,7 @@ public void Should_translate_concat_flattened() result.Value.Result.Should().Be("Awesome Balloon"); } - [SkippableFact] + [Fact] public void Should_translate_concatArrays() { RequireServer.Check(); @@ -428,7 +428,7 @@ public void Should_translate_condition() result.Value.Result.Should().Be("b"); } - [SkippableFact] + [Fact] public void Should_translate_dateTimeToString() { RequireServer.Check().Supports(Feature.AggregateToString); @@ -440,7 +440,7 @@ public void Should_translate_dateTimeToString() result.Value.Result.Should().Be("2012-12-01T13:14:15.016Z"); } - [SkippableFact] + [Fact] public void Should_translate_dateToString() { RequireServer.Check(); @@ -482,7 +482,7 @@ public void Should_translate_day_of_year() result.Value.Result.Should().Be(336); } - [SkippableFact] + [Fact] public void Should_translate_decimalToString() { RequireServer.Check().Supports(Feature.AggregateToString); @@ -514,7 +514,7 @@ public void Should_translate_divide_3_numbers() result.Value.Result.Should().BeApproximately(0.04, .01); } - [SkippableFact] + [Fact] public void Should_translate_doubleToString() { RequireServer.Check().Supports(Feature.AggregateToString); @@ -546,7 +546,7 @@ public void Should_translate_equals_as_a_method_call() result.Value.Result.Should().BeFalse(); } - [SkippableFact] + [Fact] public void Should_translate_exp() { RequireServer.Check(); @@ -558,7 +558,7 @@ public void Should_translate_exp() result.Value.Result.Should().BeApproximately(59874.1417151978, .0001); } - [SkippableFact] + [Fact] public void Should_translate_floor() { RequireServer.Check(); @@ -600,7 +600,7 @@ public void Should_translate_hour() result.Value.Result.Should().Be(13); } - [SkippableFact] + [Fact] public void Should_translate_indexOfBytes() { RequireServer.Check(); @@ -618,7 +618,7 @@ public void Should_translate_indexOfBytes() result.Value.Result.Should().Be(-1); } - [SkippableFact] + [Fact] public void Should_translate_indexOfCP() { RequireServer.Check(); @@ -648,7 +648,7 @@ public void Should_translate_indexOfCP() result.Value.Result.Should().Be(-1); } - [SkippableFact] + [Fact] public void Should_translate_intToString() { RequireServer.Check().Supports(Feature.AggregateToString); @@ -680,7 +680,7 @@ public void Should_translate_less_than_or_equal() result.Value.Result.Should().BeFalse(); } - [SkippableFact] + [Fact] public void Should_translate_literal_when_a_constant_strings_begins_with_a_dollar() { RequireServer.Check(); @@ -692,7 +692,7 @@ public void Should_translate_literal_when_a_constant_strings_begins_with_a_dolla result.Value.Result.Should().BeFalse(); } - [SkippableFact] + [Fact] public void Should_translate_ln() { RequireServer.Check(); @@ -704,7 +704,7 @@ public void Should_translate_ln() result.Value.Result.Should().BeApproximately(2.39789527279837, .0001); } - [SkippableFact] + [Fact] public void Should_translate_log() { RequireServer.Check(); @@ -716,7 +716,7 @@ public void Should_translate_log() result.Value.Result.Should().Be(1); } - [SkippableFact] + [Fact] public void Should_translate_log10() { RequireServer.Check(); @@ -728,7 +728,7 @@ public void Should_translate_log10() result.Value.Result.Should().BeApproximately(1.0413928515823, .0001); } - [SkippableFact] + [Fact] public void Should_translate_longToString() { RequireServer.Check().Supports(Feature.AggregateToString); @@ -740,7 +740,7 @@ public void Should_translate_longToString() result.Value.Result.Should().Be("9"); } - [SkippableFact] + [Fact] public void Should_translate_map_with_document() { RequireServer.Check(); @@ -752,7 +752,7 @@ public void Should_translate_map_with_document() result.Value.Result.Should().Equal("Don't0", "Dolphin0"); } - [SkippableFact] + [Fact] public void Should_translate_map_with_value() { RequireServer.Check(); @@ -764,7 +764,7 @@ public void Should_translate_map_with_value() result.Value.Result.Should().Equal("it0", "icky0"); } - [SkippableFact] + [Fact] public void Should_translate_max() { RequireServer.Check(); @@ -776,7 +776,7 @@ public void Should_translate_max() result.Value.Result.Should().Be(5); } - [SkippableFact] + [Fact] public void Should_translate_max_with_selector() { RequireServer.Check(); @@ -788,7 +788,7 @@ public void Should_translate_max_with_selector() result.Value.Result.Should().Be(55); } - [SkippableFact] + [Fact] public void Should_translate_millisecond() { RequireServer.Check(); @@ -800,7 +800,7 @@ public void Should_translate_millisecond() result.Value.Result.Should().Be(16); } - [SkippableFact] + [Fact] public void Should_translate_min() { RequireServer.Check(); @@ -812,7 +812,7 @@ public void Should_translate_min() result.Value.Result.Should().Be(2); } - [SkippableFact] + [Fact] public void Should_translate_min_with_selector() { RequireServer.Check(); @@ -874,7 +874,7 @@ public void Should_translate_multiply_flattened() result.Value.Result.Should().Be(2420); } - [SkippableFact] + [Fact] public void Should_translate_new_DateTime() { RequireServer.Check(); @@ -922,7 +922,7 @@ public void Should_translate_not_equals() result.Value.Result.Should().BeTrue(); } - [SkippableFact] + [Fact] public void Should_translate_objectIdToString() { RequireServer.Check().Supports(Feature.AggregateToString); @@ -954,7 +954,7 @@ public void Should_translate_or_flattened() result.Value.Result.Should().BeFalse(); } - [SkippableFact] + [Fact] public void Should_translate_DateTime_parse() { RequireServer.Check(); @@ -966,7 +966,7 @@ public void Should_translate_DateTime_parse() result.Value.Result.Should().Be(new DateTime(2017, 2, 8, 12, 10, 40, 787, DateTimeKind.Utc)); } - [SkippableFact] + [Fact] public void Should_translate_pow() { RequireServer.Check(); @@ -978,7 +978,7 @@ public void Should_translate_pow() result.Value.Result.Should().Be(161051); } - [SkippableFact] + [Fact] public void Should_translate_range() { RequireServer.Check(); @@ -990,7 +990,7 @@ public void Should_translate_range() result.Value.Result.Should().BeEquivalentTo(11, 12, 13); } - [SkippableFact] + [Fact] public void Should_translate_reduce() { RequireServer.Check(); @@ -1111,7 +1111,7 @@ public void Should_translate_reduce() typeResult.Value.Result.y.Should().Be(5); } - [SkippableFact] + [Fact] public void Should_translate_reverse() { RequireServer.Check(); @@ -1133,7 +1133,7 @@ public void Should_translate_second() result.Value.Result.Should().Be(15); } - [SkippableFact] + [Fact] public void Should_translate_size_greater_than_zero_from_any() { RequireServer.Check(); @@ -1145,7 +1145,7 @@ public void Should_translate_size_greater_than_zero_from_any() result.Value.Result.Should().BeTrue(); } - [SkippableFact] + [Fact] public void Should_translate_size_from_an_array() { RequireServer.Check(); @@ -1157,7 +1157,7 @@ public void Should_translate_size_from_an_array() result.Value.Result.Should().Be(3); } - [SkippableFact] + [Fact] public void Should_translate_size_from_Count_extension_method() { RequireServer.Check(); @@ -1169,7 +1169,7 @@ public void Should_translate_size_from_Count_extension_method() result.Value.Result.Should().Be(3); } - [SkippableFact] + [Fact] public void Should_translate_size_from_LongCount_extension_method() { RequireServer.Check(); @@ -1181,7 +1181,7 @@ public void Should_translate_size_from_LongCount_extension_method() result.Value.Result.Should().Be(3); } - [SkippableFact] + [Fact] public void Should_translate_size_from_Count_property_on_Generic_ICollection() { RequireServer.Check(); @@ -1193,7 +1193,7 @@ public void Should_translate_size_from_Count_property_on_Generic_ICollection() result.Value.Result.Should().Be(3); } - [SkippableFact] + [Fact] public void Should_translate_set_difference() { RequireServer.Check(); @@ -1205,7 +1205,7 @@ public void Should_translate_set_difference() result.Value.Result.Should().Equal("icky"); } - [SkippableFact] + [Fact] public void Should_translate_set_difference_reversed() { RequireServer.Check(); @@ -1217,7 +1217,7 @@ public void Should_translate_set_difference_reversed() result.Value.Result.Should().Equal("not in here"); } - [SkippableFact] + [Fact] public void Should_translate_set_equals() { RequireServer.Check(); @@ -1229,7 +1229,7 @@ public void Should_translate_set_equals() result.Value.Result.Should().BeTrue(); } - [SkippableFact] + [Fact] public void Should_translate_set_equals_reversed() { RequireServer.Check(); @@ -1242,7 +1242,7 @@ public void Should_translate_set_equals_reversed() result.Value.Result.Should().BeTrue(); } - [SkippableFact] + [Fact] public void Should_translate_set_intersection() { RequireServer.Check(); @@ -1254,7 +1254,7 @@ public void Should_translate_set_intersection() result.Value.Result.Should().Equal("it"); } - [SkippableFact] + [Fact] public void Should_translate_set_intersection_reversed() { RequireServer.Check(); @@ -1266,7 +1266,7 @@ public void Should_translate_set_intersection_reversed() result.Value.Result.Should().Equal("it"); } - [SkippableFact] + [Fact] public void Should_translate_set_is_subset() { RequireServer.Check(); @@ -1278,7 +1278,7 @@ public void Should_translate_set_is_subset() result.Value.Result.Should().BeTrue(); } - [SkippableFact] + [Fact] public void Should_translate_set_is_subset_reversed() { RequireServer.Check(); @@ -1291,7 +1291,7 @@ public void Should_translate_set_is_subset_reversed() result.Value.Result.Should().BeTrue(); } - [SkippableFact] + [Fact] public void Should_translate_set_union() { RequireServer.Check(); @@ -1303,7 +1303,7 @@ public void Should_translate_set_union() result.Value.Result.Should().BeEquivalentTo("it", "icky", "not in here"); } - [SkippableFact] + [Fact] public void Should_translate_set_union_reversed() { RequireServer.Check(); @@ -1315,7 +1315,7 @@ public void Should_translate_set_union_reversed() result.Value.Result.Should().BeEquivalentTo("it", "icky", "not in here"); } - [SkippableFact] + [Fact] public void Should_translate_sqrt() { RequireServer.Check(); @@ -1327,7 +1327,7 @@ public void Should_translate_sqrt() result.Value.Result.Should().BeApproximately(3.31662479, .0001); } - [SkippableFact] + [Fact] public void Should_translate_stringToString() { RequireServer.Check().Supports(Feature.AggregateToString); @@ -1339,7 +1339,7 @@ public void Should_translate_stringToString() result.Value.Result.Should().Be("Awesome"); } - [SkippableFact] + [Fact] public void Should_translate_trunc() { RequireServer.Check(); @@ -1351,7 +1351,7 @@ public void Should_translate_trunc() result.Value.Result.Should().Be(1); } - [SkippableFact] + [Fact] public void Should_translate_where_to_filter() { RequireServer.Check(); @@ -1364,7 +1364,7 @@ public void Should_translate_where_to_filter() result.Value.Result.Single().D.Should().Be("Don't"); } - [SkippableFact] + [Fact] public void Should_translate_where_then_select_to_filter_then_map() { RequireServer.Check(); @@ -1377,7 +1377,7 @@ public void Should_translate_where_then_select_to_filter_then_map() result.Value.Result.Single().Should().Be("Don't"); } - [SkippableFact] + [Fact] public void Should_translate_select_then_where_to_map_then_filter() { RequireServer.Check(); @@ -1390,7 +1390,7 @@ public void Should_translate_select_then_where_to_map_then_filter() result.Value.Result.Single().Should().Be("Don't"); } - [SkippableFact] + [Fact] public void Should_translate_select_with_an_anonymous_type_then_where_to_map_then_filter() { RequireServer.Check(); @@ -1404,7 +1404,7 @@ public void Should_translate_select_with_an_anonymous_type_then_where_to_map_the result.Value.Result.Single().F.Should().Be(33); } - [SkippableFact] + [Fact] public void Should_translate_strLenBytes() { RequireServer.Check(); @@ -1416,7 +1416,7 @@ public void Should_translate_strLenBytes() result.Value.Result.Should().Be(7); } - [SkippableFact] + [Fact] public void Should_translate_strLenCP() { RequireServer.Check(); @@ -1428,7 +1428,7 @@ public void Should_translate_strLenCP() result.Value.Result.Should().Be(7); } - [SkippableFact] + [Fact] public void Should_translate_split() { RequireServer.Check(); @@ -1454,7 +1454,7 @@ public void Should_translate_split() result4.Value.Result.Should().BeEquivalentTo("Aw", "ome"); } - [SkippableFact] + [Fact] public void Should_translate_stdDevPop() { RequireServer.Check(); @@ -1466,7 +1466,7 @@ public void Should_translate_stdDevPop() result.Value.Result.Should().BeApproximately(1.247219128924647, .0001); } - [SkippableFact] + [Fact] public void Should_translate_stdDevPop_with_selector() { RequireServer.Check(); @@ -1478,7 +1478,7 @@ public void Should_translate_stdDevPop_with_selector() result.Value.Result.Should().Be(11); } - [SkippableFact] + [Fact] public void Should_translate_stdDevSamp() { RequireServer.Check(); @@ -1490,7 +1490,7 @@ public void Should_translate_stdDevSamp() result.Value.Result.Should().BeApproximately(1.5275252316519468, .0001); } - [SkippableFact] + [Fact] public void Should_translate_stdDevSamp_with_selector() { RequireServer.Check(); @@ -1562,7 +1562,7 @@ public void Should_translate_substr() result.Value.Result.Should().Be("loon"); } - [SkippableFact] + [Fact] public void Should_translate_substrBytes() { RequireServer.Check(); @@ -1594,7 +1594,7 @@ public void Should_translate_subtract_3_numbers() result.Value.Result.Should().Be(-23); } - [SkippableFact] + [Fact] public void Should_translate_slice_with_2_arguments() { RequireServer.Check(); @@ -1606,7 +1606,7 @@ public void Should_translate_slice_with_2_arguments() result.Value.Result.Should().BeEquivalentTo(2, 4); } - [SkippableFact] + [Fact] public void Should_translate_slice_with_3_arguments() { RequireServer.Check(); @@ -1618,7 +1618,7 @@ public void Should_translate_slice_with_3_arguments() result.Value.Result.Should().BeEquivalentTo(4, 5); } - [SkippableFact] + [Fact] public void Should_translate_sum() { RequireServer.Check(); @@ -1630,7 +1630,7 @@ public void Should_translate_sum() result.Value.Result.Should().Be(11); } - [SkippableFact] + [Fact] public void Should_translate_sum_with_selector() { RequireServer.Check(); @@ -1692,7 +1692,7 @@ public void Should_translate_year() result.Value.Result.Should().Be(2012); } - [SkippableFact] + [Fact] public void Should_translate_zip_with_operation() { RequireServer.Check(); @@ -1704,7 +1704,7 @@ public void Should_translate_zip_with_operation() result.Value.Result.Should().BeEquivalentTo(12L, 24L, 35L); } - [SkippableFact] + [Fact] public void Should_translate_zip_with_anonymous_type() { RequireServer.Check(); @@ -1741,7 +1741,7 @@ public void Should_translate_a_derived_class_projection() result.Value.DerivedProperty.Should().Be("Balloon"); } - [SkippableFact] + [Fact] public void Should_translate_array_projection_complex() { RequireServer.Check(); diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTestsOnLinq3/Translators/LegacyPredicateTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTestsOnLinq3/Translators/LegacyPredicateTranslatorTests.cs index 0300c0239ff..b38475a8610 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTestsOnLinq3/Translators/LegacyPredicateTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTestsOnLinq3/Translators/LegacyPredicateTranslatorTests.cs @@ -53,7 +53,7 @@ public LegacyPredicateTranslatorTests() public bool OneTimeSetup() { - __database = DriverTestConfiguration.Client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName); + __database = DriverTestConfiguration.Linq3Client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName); __collection = __database.GetCollection(DriverTestConfiguration.CollectionNamespace.CollectionName); // documents inserted deliberately out of order to test sorting diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTestsOnLinq3/Translators/PredicateTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTestsOnLinq3/Translators/PredicateTranslatorTests.cs index e77c83ccb84..9df49eb2bd7 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTestsOnLinq3/Translators/PredicateTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq2ImplementationTestsOnLinq3/Translators/PredicateTranslatorTests.cs @@ -479,7 +479,7 @@ public void Any_with_a_predicate_on_scalars_legacy() "{ M : { $elemMatch : { $gt : 2, $lt : 6 } } }"); } - [SkippableFact] + [Fact] public void Any_with_a_predicate_on_scalars() { RequireServer.Check(); @@ -551,7 +551,7 @@ public void AsQueryable() exception.Should().BeOfType(); } - [SkippableFact] + [Fact] public void BitsAllClear_with_bitwise_operators() { RequireServer.Check(); @@ -562,7 +562,7 @@ public void BitsAllClear_with_bitwise_operators() "{'C.E.F': { $bitsAllClear: 20 } }"); } - [SkippableFact] + [Fact] public void BitsAllSet_with_bitwise_operators() { RequireServer.Check(); @@ -573,7 +573,7 @@ public void BitsAllSet_with_bitwise_operators() "{'C.E.F': { $bitsAllSet: 7 } }"); } - [SkippableFact] + [Fact] public void BitsAllSet_with_HasFlag() { RequireServer.Check(); @@ -584,7 +584,7 @@ public void BitsAllSet_with_HasFlag() "{Q: { $bitsAllSet: 1 } }"); } - [SkippableFact] + [Fact] public void BitsAnyClear_with_bitwise_operators() { RequireServer.Check(); @@ -595,7 +595,7 @@ public void BitsAnyClear_with_bitwise_operators() "{'C.E.F': { $bitsAnyClear: 7 } }"); } - [SkippableFact] + [Fact] public void BitsAnySet_with_bitwise_operators() { RequireServer.Check(); diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Ast/Expressions/AstExpressionTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Ast/Expressions/AstExpressionTests.cs new file mode 100644 index 00000000000..496ce2d3c31 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Ast/Expressions/AstExpressionTests.cs @@ -0,0 +1,146 @@ +/* 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.Driver.Linq.Linq3Implementation.Ast.Expressions; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Ast.Expressions +{ + public class AstExpressionTests + { + [Theory] + [InlineData("bool", (int)AstUnaryOperator.ToBool)] + [InlineData("date", (int)AstUnaryOperator.ToDate)] + [InlineData("decimal", (int)AstUnaryOperator.ToDecimal)] + [InlineData("double", (int)AstUnaryOperator.ToDouble)] + [InlineData("int", (int)AstUnaryOperator.ToInt)] + [InlineData("long", (int)AstUnaryOperator.ToLong)] + [InlineData("objectId", (int)AstUnaryOperator.ToObjectId)] + [InlineData("string", (int)AstUnaryOperator.ToString)] + public void Convert_with_to_constant_should_return_short_form_when_possible(string toValue, int expectedOperator) + { + var input = AstExpression.Constant(BsonNull.Value); + var to = AstExpression.Constant(toValue); + + var result = AstExpression.Convert(input, to, onError: null, onNull: null); + + var unaryExpression = result.Should().BeOfType().Subject; + unaryExpression.Operator.Should().Be((AstUnaryOperator)expectedOperator); + unaryExpression.Arg.Should().BeSameAs(input); + } + + [Theory] + [InlineData("xyz")] + public void Convert_with_to_constant_should_return_long_form_when_necessary(string toValue) + { + var input = AstExpression.Constant(BsonNull.Value); + var to = AstExpression.Constant(toValue); + + var result = AstExpression.Convert(input, to, onError: null, onNull: null); + + var convertExpression = result.Should().BeOfType().Subject; + convertExpression.Input.Should().BeSameAs(input); + convertExpression.To.Should().BeSameAs(to); + convertExpression.OnError.Should().BeNull(); + convertExpression.OnNull.Should().BeNull(); + } + + [Theory] + [InlineData("bool")] + [InlineData("date")] + [InlineData("decimal")] + [InlineData("double")] + [InlineData("int")] + [InlineData("long")] + [InlineData("objectId")] + [InlineData("string")] + public void Convert_with_to_expression_should_return_long_form(string toValue) + { + var input = AstExpression.Constant(BsonNull.Value); + var to = AstExpression.FieldPath("$To"); + + var result = AstExpression.Convert(input, to, onError: null, onNull: null); + + var convertExpression = result.Should().BeOfType().Subject; + convertExpression.Input.Should().BeSameAs(input); + convertExpression.To.Should().BeSameAs(to); + convertExpression.OnError.Should().BeNull(); + convertExpression.OnNull.Should().BeNull(); + } + + [Theory] + [InlineData("bool")] + [InlineData("date")] + [InlineData("decimal")] + [InlineData("double")] + [InlineData("int")] + [InlineData("long")] + [InlineData("objectId")] + [InlineData("string")] + public void Convert_with_on_error_should_return_long_form(string toValue) + { + var input = AstExpression.Constant(BsonNull.Value); + var to = AstExpression.Constant(toValue); + var onError = AstExpression.Constant(BsonNull.Value); + + var result = AstExpression.Convert(input, to, onError, onNull: null); + + var convertExpression = result.Should().BeOfType().Subject; + convertExpression.Input.Should().BeSameAs(input); + convertExpression.To.Should().BeSameAs(to); + convertExpression.OnError.Should().BeSameAs(onError); + convertExpression.OnNull.Should().BeNull(); + } + + [Theory] + [InlineData("bool")] + [InlineData("date")] + [InlineData("decimal")] + [InlineData("double")] + [InlineData("int")] + [InlineData("long")] + [InlineData("objectId")] + [InlineData("string")] + public void Convert_with_on_null_should_return_long_form(string toValue) + { + var input = AstExpression.Constant(BsonNull.Value); + var to = AstExpression.Constant(toValue); + var onNull = AstExpression.Constant(BsonNull.Value); + + var result = AstExpression.Convert(input, to, onError: null, onNull); + + var convertExpression = result.Should().BeOfType().Subject; + convertExpression.Input.Should().BeSameAs(input); + convertExpression.To.Should().BeSameAs(to); + convertExpression.OnError.Should().BeNull(); + convertExpression.OnNull.Should().BeSameAs(onNull); + } + + [Fact] + public void Unary_should_return_expected_result() + { + var @operator = AstUnaryOperator.Abs; + var arg = AstExpression.Constant(-1); + + var result = AstExpression.Unary(@operator, arg); + + var unaryExpression = result.Should().BeOfType().Subject; + unaryExpression.Operator.Should().Be(@operator); + unaryExpression.Arg.Should().BeSameAs(arg); + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp1326Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp1326Tests.cs new file mode 100644 index 00000000000..1856763d439 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp1326Tests.cs @@ -0,0 +1,87 @@ +/* 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.Collections.Generic; +using System.Linq; +using FluentAssertions; +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira +{ + public class CSharp1326Tests : Linq3IntegrationTest + { + [Fact] + public void Projection_of_ArrayOfDocuments_dictionary_keys_and_values_should_work() + { + var collection = CreateCollection(); + var parentIds = new int[] { 1, 2, 3 }; + var childrenFilter = + Builders.Filter.In(c => c.ParentId, parentIds) & + Builders.Filter.Eq(c => c.Gender, Gender.Male); + + var aggregate = collection + .Aggregate() + .Match(childrenFilter) + .Group(c => c.ParentId, g => new KeyValuePair>(g.Key, new List(g))); + + var stages = Translate(collection, aggregate); + AssertStages( + stages, + "{ $match : { ParentId : { $in : [1, 2, 3] }, Gender : 'Male' } }", + "{ $group : { _id : '$ParentId', _elements : { $push : '$$ROOT' } } }", + "{ $project : { Key : '$_id', Value : '$_elements', _id : 0 } }"); + + var results = aggregate.ToList().OrderBy(x => x.Key).ToList(); + results[0].Key.Should().Be(1); + results[0].Value.Select(x => x.Id).Should().BeEquivalentTo(1, 2); + results[1].Key.Should().Be(2); + results[1].Value.Select(x => x.Id).Should().BeEquivalentTo(4); + } + + private IMongoCollection CreateCollection() + { + var collection = GetCollection("Children"); + + CreateCollection( + collection, + new Child { Id = 1, ParentId = 1, Gender = Gender.Male }, + new Child { Id = 2, ParentId = 1, Gender = Gender.Male }, + new Child { Id = 3, ParentId = 1, Gender = Gender.Female }, + new Child { Id = 4, ParentId = 2, Gender = Gender.Male }, + new Child { Id = 5, ParentId = 4, Gender = Gender.Male }); + + return collection; + } + + public class Parent + { + public int Id { get; set; } + } + + public class Child + { + public int Id { get; set; } + + public int ParentId { get; set; } + + [BsonRepresentation(BsonType.String)] + public Gender Gender { get; set; } + } + + public enum Gender { Male, Female }; + } +} diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp2003Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp2003Tests.cs index f3f11f397fc..852ff7543da 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp2003Tests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp2003Tests.cs @@ -29,7 +29,7 @@ public void Find_BitsAllClear_should_work() var mask = E.E2 | E.E4; var find = collection.Find(x => (x.E & mask) == 0); - var filter = TranslateFilter(collection, find); + var filter = Translate(collection, find.Filter); filter.Should().Be("{ E : { $bitsAllClear : 6 } }"); var results = find.ToList().OrderBy(x => x.Id).ToList(); @@ -43,7 +43,7 @@ public void Find_BitsAllSet_should_work() var mask = E.E2 | E.E4; var find = collection.Find(x => (x.E & mask) == mask); - var filter = TranslateFilter(collection, find); + var filter = Translate(collection, find.Filter); filter.Should().Be("{ E : { $bitsAllSet : 6 } }"); var results = find.ToList().OrderBy(x => x.Id).ToList(); @@ -57,7 +57,7 @@ public void Find_BitsAnyClear_should_work() var mask = E.E2 | E.E4; var find = collection.Find(x => (x.E & mask) != mask); - var filter = TranslateFilter(collection, find); + var filter = Translate(collection, find.Filter); filter.Should().Be("{ E : { $bitsAnyClear : 6 } }"); var results = find.ToList().OrderBy(x => x.Id).ToList(); @@ -71,7 +71,7 @@ public void Find_BitsAnySet_should_work() var mask = E.E2 | E.E4; var find = collection.Find(x => (x.E & mask) != 0); - var filter = TranslateFilter(collection, find); + var filter = Translate(collection, find.Filter); filter.Should().Be("{ E : { $bitsAnySet : 6 } }"); var results = find.ToList().OrderBy(x => x.Id).ToList(); diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp2113Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp2113Tests.cs new file mode 100644 index 00000000000..fbd12010793 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp2113Tests.cs @@ -0,0 +1,151 @@ +/* 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.Linq; +using FluentAssertions; +using MongoDB.Bson.TestHelpers; +using MongoDB.Driver.Linq; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira +{ + [Collection(RegisterObjectSerializerFixture.CollectionName)] + public class CSharp2113Tests : Linq3IntegrationTest + { + [Fact] + public void Query1_should_work() + { + var collection = CreateDocumentCollection(); + + var queryable = collection + .AsQueryable() + .Where(x => x.X == 1); + + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $match : { X : 1 } }"); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(1); + } + + [Fact] + public void Query2_should_work() + { + var collection = CreateDocumentCollection(); + + var queryable = collection + .AsQueryable() + .Where(x => x.Inner.Y == 1); + + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $match : { 'Inner.Y' : 1 } }"); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(1); + } + + [Fact] + public void Query3_should_work() + { + var collection = CreateIDocumentCollection(); + + var queryable = collection + .AsQueryable() + .Where(x => x.X == 1); + + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $match : { X : 1 } }"); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(1); + } + + [Fact] + public void Query4_should_work() + { + var collection = CreateIDocumentCollection(); + + var queryable = collection + .AsQueryable() + .Where(x => x.Inner.Y == 1); + + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $match : { 'Inner.Y' : 1 } }"); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(1); + } + + private IMongoCollection CreateDocumentCollection() + { + var collection = GetCollection(); + + CreateCollection( + collection, + new Document { Id = 1, X = 1, Inner = new Inner { Y = 1 } }, + new Document { Id = 2, X = 2, Inner = new Inner { Y = 2 } }); + + return collection; + } + + private IMongoCollection CreateIDocumentCollection() + { + var collection = GetCollection(); + + CreateCollection( + collection, + new DocumentWithInterface { Id = 1, X = 1, Inner = new InnerWithInterface { Y = 1 } }, + new DocumentWithInterface { Id = 2, X = 2, Inner = new InnerWithInterface { Y = 2 } }); + + return collection; + } + + private class Document + { + public int Id { get; set; } + public int X { get; set; } + public Inner Inner { get; set; } + } + + private class Inner + { + public int Y { get; set; } + } + + private interface IDocument + { + int Id { get; set; } + int X { get; set; } + IInner Inner { get; set; } + } + + private interface IInner + { + int Y { get; set; } + } + + private class DocumentWithInterface : IDocument + { + public int Id { get; set; } + public int X { get; set; } + public IInner Inner { get; set; } + } + + public class InnerWithInterface : IInner + { + public int Y { get; set; } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp2134Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp2134Tests.cs new file mode 100644 index 00000000000..c1d6b4c426f --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp2134Tests.cs @@ -0,0 +1,72 @@ +/* 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.Collections.Generic; +using System.Linq; +using FluentAssertions; +using MongoDB.Driver.Linq.Linq3Implementation; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira +{ + public class CSharp2134Tests : Linq3IntegrationTest + { + [Theory] + [InlineData(false, true)] + [InlineData(true, false)] + public void Projection_of_ArrayOfDocuments_dictionary_keys_and_values_should_work(bool includeNullId, bool expectedResult) + { + var collection = CreateCollection(includeNullId); + + var queryable = collection.AsQueryable(); + var result = queryable.All(x => x.Id != null); + + var provider = (MongoQueryProvider)queryable.Provider; + var stages = provider.GetMostRecentPipelineStages(); + AssertStages( + stages, + "{ $match : { _id : null } }", + "{ $limit : 1 }", + "{ $project : { _id : 0, _v : null } }"); + + result.Should().Be(expectedResult); + } + + private IMongoCollection CreateCollection(bool includeNullId) + { + var collection = GetCollection("C"); + + var documents = new List + { + new C { Id = 1 }, + new C { Id = 2 }, + new C { Id = 3 } + }; + if (includeNullId) + { + documents.Add(new C { Id = null }); + } + + CreateCollection(collection, documents); + + return collection; + } + + private class C + { + public int? Id { get; set; } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp2195Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp2195Tests.cs new file mode 100644 index 00000000000..b855f0494ca --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp2195Tests.cs @@ -0,0 +1,73 @@ +/* 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.Linq; +using FluentAssertions; +using MongoDB.Bson; +using MongoDB.Bson.Serialization; +using MongoDB.Driver.Linq; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira +{ + public class CSharp2195Tests : Linq3IntegrationTest + { + [Fact] + public void Filter_should_work() + { + var collection = CreateCollection(); + var builder = Builders.Filter; + var filter = builder.Eq(x => x["life"], 42); + + var serializerRegistry = BsonSerializer.SerializerRegistry; + var documentSerializer = serializerRegistry.GetSerializer(); + var renderedFilter = filter.Render(documentSerializer, serializerRegistry); + renderedFilter.Should().Be("{ life : 42 }"); + + var results = collection.FindSync(filter).ToList(); + results.Select(x => x["_id"].AsInt32).Should().Equal(2); + } + + [Fact] + public void Where_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .Where(x => x["life"] == 42); + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $match : { life : 42 } }"); + + var results = queryable.ToList(); + results.Select(x => x["_id"].AsInt32).Should().Equal(2); + } + + private IMongoCollection CreateCollection() + { + var collection = GetCollection(); + + CreateCollection( + collection, + new BsonDocument { { "_id", 1 }, { "life", 41 } }, + new BsonDocument { { "_id", 2 }, { "life", 42 } }); + + return GetCollection(); + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp2422Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp2422Tests.cs index 55d2953a783..0b0830f21a3 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp2422Tests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp2422Tests.cs @@ -16,7 +16,7 @@ using System; using System.Linq; using System.Linq.Expressions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp2471Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp2471Tests.cs new file mode 100644 index 00000000000..955db82f8b8 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp2471Tests.cs @@ -0,0 +1,125 @@ +/* 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 System.Linq.Expressions; +using FluentAssertions; +using MongoDB.Driver.Core.Misc; +using MongoDB.Driver.Core.TestHelpers.XunitExtensions; +using MongoDB.Driver.Linq; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira +{ + public class CSharp2471Tests : Linq3IntegrationTest + { + [Theory] + [InlineData("$acos", 1.0, 0.0)] +#if NETCOREAPP3_1_OR_GREATER + [InlineData("$acosh", 1.0, 0.0)] +#endif + [InlineData("$asin", 0.0, 0.0)] +#if NETCOREAPP3_1_OR_GREATER + [InlineData("$asinh", 0.0, 0.0)] +#endif + [InlineData("$atan", 0.0, 0.0)] +#if NETCOREAPP3_1_OR_GREATER + [InlineData("$atanh", 0.0, 0.0)] +#endif + [InlineData("$cos", 0.0, 1.0)] + [InlineData("$cosh", 0.0, 1.0)] + [InlineData("$degreesToRadians", 0.0, 0.0)] + [InlineData("$radiansToDegrees", 0.0, 0.0)] + [InlineData("$sin", 0.0, 0.0)] + [InlineData("$sinh", 0.0, 0.0)] + [InlineData("$tan", 0.0, 0.0)] + [InlineData("$tanh", 0.0, 0.0)] + public void Trig_method_should_work(string trigOperator, double x, double expectedResult) + { + RequireServer.Check().Supports(Feature.TrigOperators); + var collection = CreateCollection(x); + + Expression> projection = trigOperator switch + { + "$acos" => x => Math.Acos(x.X), +#if NETCOREAPP3_1_OR_GREATER + "$acosh" => x => Math.Acosh(x.X), +#endif + "$asin" => x => Math.Asin(x.X), +#if NETCOREAPP3_1_OR_GREATER + "$asinh" => x => Math.Asinh(x.X), +#endif + "$atan" => x => Math.Atan(x.X), +#if NETCOREAPP3_1_OR_GREATER + "$atanh" => x => Math.Atanh(x.X), +#endif + "$cos" => x => Math.Cos(x.X), + "$cosh" => x => Math.Cosh(x.X), + "$degreesToRadians" => x => MongoDBMath.DegreesToRadians(x.X), + "$radiansToDegrees" => x => MongoDBMath.RadiansToDegrees(x.X), + "$sin" => x => Math.Sin(x.X), + "$sinh" => x => Math.Sinh(x.X), + "$tan" => x => Math.Tan(x.X), + "$tanh" => x => Math.Tanh(x.X), + _ => throw new Exception($"Invalid trig operator: {trigOperator}.") + }; + + var queryable = collection + .AsQueryable() + .Select(projection); + + var stages = Translate(collection, queryable); + AssertStages(stages, $"{{ $project : {{ _v : {{ {trigOperator} : '$X' }}, _id : 0 }} }}"); + + var result = queryable.Single(); + result.Should().Be(expectedResult); + } + + [Fact] + public void Atan2_should_work() + { + RequireServer.Check().Supports(Feature.TrigOperators); + var collection = CreateCollection(0.0); + + var queryable = collection + .AsQueryable() + .Select(x => Math.Atan2(x.X, 0.0)); + + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $project : { _v : { $atan2 : ['$X', 0.0] }, _id : 0 } }"); + + var result = queryable.Single(); + result.Should().Be(0.0); + } + + private IMongoCollection CreateCollection(double x) + { + var collection = GetCollection("C"); + + CreateCollection( + collection, + new C { Id = 1, X = x }); + + return collection; + } + + private class C + { + public int Id { get; set; } + public double X { get; set; } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp2472Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp2472Tests.cs new file mode 100644 index 00000000000..469b9f301aa --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp2472Tests.cs @@ -0,0 +1,92 @@ +/* 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.Driver.Core.Misc; +using MongoDB.Driver.Core.TestHelpers.XunitExtensions; +using MongoDB.Driver.Linq; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira +{ + public class CSharp2472Tests : Linq3IntegrationTest + { + [Fact] + public void Numeric_casts_should_work() + { + RequireServer.Check().Supports(Feature.ToConversionOperators); + var collection = CreateCollection(); + var equipmentId = 1; + var startDate = new DateTime(2022, 01, 01, 0, 0, 0, DateTimeKind.Utc); + var endDate = new DateTime(2022, 01, 02, 0, 0, 0, DateTimeKind.Utc); + + var queryable = collection + .AsQueryable() + .Where( + q => q.equipment_id == equipmentId + && q.timestamp >= startDate + && q.timestamp <= endDate + ) + .GroupBy(g => g.timestamp) + .Select(p => new MyDTO + { + timestamp = p.Key, + sqrt_calc = (decimal)Math.Sqrt( + p.Sum(x => (double)x.my_decimal_value) + ) + }) + .OrderBy(q => q.timestamp); + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $match : { equipment_id : 1, timestamp : { $gte : ISODate('2022-01-01T00:00:00Z'), $lte : ISODate('2022-01-02T00:00:00Z') } } }", + "{ $group : { _id : '$timestamp', __agg0 : { $sum : { $toDouble : '$my_decimal_value' } } } }", + "{ $project : { timestamp : '$_id', sqrt_calc : { $toDecimal : { $sqrt : '$__agg0' } }, _id : 0 } }", + "{ $sort : { timestamp : 1 } }"); + + var result = queryable.Single(); + result.timestamp.Should().Be(new DateTime(2022, 01, 01, 12, 0, 0, DateTimeKind.Utc)); + result.sqrt_calc.Should().Be(2M); + } + + private IMongoCollection CreateCollection() + { + var collection = GetCollection("C"); + + CreateCollection( + collection, + new C { Id = 1, equipment_id = 1, timestamp = new DateTime(2022, 01, 01, 12, 0, 0, DateTimeKind.Utc), my_decimal_value = 4M }); + + return collection; + } + + private class C + { + public int Id { get; set; } + public int equipment_id { get; set; } + public DateTime timestamp { get; set; } + public decimal my_decimal_value { get; set; } + } + + private class MyDTO + { + public DateTime timestamp { get; set; } + public decimal sqrt_calc { get; set; } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp2727Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp2727Tests.cs new file mode 100644 index 00000000000..a86863f8a36 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp2727Tests.cs @@ -0,0 +1,120 @@ +/* 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; +using MongoDB.Driver.Core.Misc; +using MongoDB.Driver.Core.TestHelpers.XunitExtensions; +using MongoDB.Driver.Linq; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira +{ + public class CSharp2727Tests : Linq3IntegrationTest + { + [Fact] + public void Find_with_predicate_on_Body_should_work() + { + RequireServer.Check().Supports(Feature.AggregateToString); + var collection = CreateCollection(); + var filter = new ExpressionFilterDefinition(x => new[] { "Test1", "Test2" }.Contains((string)x.Body["name"])); + + var serializerRegistry = BsonSerializer.SerializerRegistry; + var documentSerializer = serializerRegistry.GetSerializer(); + var renderedFilter = filter.Render(documentSerializer, serializerRegistry, LinqProvider.V3); + + renderedFilter.Should().Be("{ $expr : { $in : [{ $toString : '$Body.name' }, ['Test1', 'Test2']] } }"); + + var cursor = collection.Find(filter); + + var results = cursor.ToList(); + results.Select(x => x.Id).Should().Equal(1, 2); + } + + [Fact] + public void Find_with_predicate_on_Caption_should_work() + { + var collection = CreateCollection(); + var filter = new ExpressionFilterDefinition(x => new[] { "Test1", "Test2" }.Contains(x.Caption)); + + var serializerRegistry = BsonSerializer.SerializerRegistry; + var documentSerializer = serializerRegistry.GetSerializer(); + var renderedFilter = filter.Render(documentSerializer, serializerRegistry, LinqProvider.V3); + + renderedFilter.Should().Be("{ Caption : { $in : ['Test1', 'Test2'] } }"); + + var cursor = collection.FindSync(filter); + + var results = cursor.ToList(); + results.Select(x => x.Id).Should().Equal(1, 2); + } + + [Fact] + public void Where_with_predicate_on_Body_should_work() + { + RequireServer.Check().Supports(Feature.AggregateToString); + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .Where(x => new[] { "Test1", "Test2" }.Contains((string)x.Body["name"])); + + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $match : { $expr : { $in : [{ $toString : '$Body.name' }, ['Test1', 'Test2']] } } }"); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(1, 2); + } + + [Fact] + public void Where_with_predicate_on_Caption_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .Where(x => new[] { "Test1", "Test2" }.Contains(x.Caption)); + + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $match : { Caption : { $in : ['Test1', 'Test2'] } } }"); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(1, 2); + } + + private IMongoCollection CreateCollection() + { + var collection = GetCollection("C"); + + CreateCollection( + collection, + new Entity { Id = 1, Body = BsonDocument.Parse("{ name : 'Test1' }"), Caption = "Test1" }, + new Entity { Id = 2, Body = BsonDocument.Parse("{ name : 'Test2' }"), Caption = "Test2" }, + new Entity { Id = 3, Body = BsonDocument.Parse("{ name : 'Test3' }"), Caption = "Test3" }); + + return collection; + } + + private class Entity + { + public int Id { get; set; } + public BsonDocument Body { get; set; } + public string Caption { get; set; } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp3136Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp3136Tests.cs new file mode 100644 index 00000000000..bb392e08c06 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp3136Tests.cs @@ -0,0 +1,258 @@ +/* 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.Driver.Core.Misc; +using MongoDB.Driver.Core.TestHelpers.XunitExtensions; +using MongoDB.Driver.Linq; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira +{ + public class CSharp3136Tests : Linq3IntegrationTest + { + + [Fact] + public void DateTime_ToString_with_no_arguments_should_work() + { + RequireServer.Check().Supports(Feature.ToConversionOperators); + + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .OrderBy(x => x.Id) + .Select(x => x.D.ToString()); + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $sort : { _id : 1 } }", + "{ $project : { _v : { $toString : '$D' }, _id : 0 } }"); + + var results = queryable.ToList(); + results.Should().Equal("2021-01-02T03:04:05.123Z", "2021-01-02T03:04:05.123Z"); + } + + [Fact] + public void DateTime_ToString_with_format_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .OrderBy(x => x.Id) + .Select(x => x.D.ToString("%H:%M:%S")); + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $sort : { _id : 1 } }", + "{ $project : { _v : { $dateToString : { date : '$D', format : '%H:%M:%S' } }, _id : 0 } }"); + + var results = queryable.ToList(); + results.Should().Equal("03:04:05", "03:04:05"); + } + + [Theory] + [InlineData(null, null, "{ $project : { _v : { $dateToString : { date : '$D' } }, _id : 0 } }", new[] { "2021-01-02T03:04:05.123Z", "2021-01-02T03:04:05.123Z" })] + [InlineData("%H:%M:%S", null, "{ $project : { _v : { $dateToString : { date : '$D', format : '%H:%M:%S' } }, _id : 0 } }", new[] { "03:04:05", "03:04:05" })] + [InlineData(null, "-04:00", "{ $project : { _v : { $dateToString : { date : '$D', timezone : '-04:00' } }, _id : 0 } }", new[] { "2021-01-01T23:04:05.123Z", "2021-01-01T23:04:05.123Z" })] + [InlineData("%H:%M:%S", "-04:00", "{ $project : { _v : { $dateToString : { date : '$D', format : '%H:%M:%S', timezone : '-04:00' } }, _id : 0 } }", new[] { "23:04:05", "23:04:05" })] + public void DateTime_ToString_with_format_and_timezone_constants_should_work(string format, string timezone, string expectedProjectStage, string[] expectedResults) + { + if (format == null) + { + RequireServer.Check().VersionGreaterThanOrEqualTo("4.0"); + } + + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .OrderBy(x => x.Id) + .Select(x => x.D.ToString(format, timezone)); + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $sort : { _id : 1 } }", + expectedProjectStage); + + var results = queryable.ToList(); + results.Should().Equal(expectedResults); + } + + [Theory] + [InlineData(false, false, "{ $project : { _v : { $dateToString : { date : '$D' } }, _id : 0 } }", new[] { "2021-01-02T03:04:05.123Z", "2021-01-02T03:04:05.123Z" })] + [InlineData(true, false, "{ $project : { _v : { $dateToString : { date : '$D', format : '$Format' } }, _id : 0 } }", new[] { "03:04:05", "03:04:05" })] + [InlineData(false, true, "{ $project : { _v : { $dateToString : { date : '$D', timezone : '$Timezone' } }, _id : 0 } }", new[] { "2021-01-01T23:04:05.123Z", "2021-01-01T23:04:05.123Z" })] + [InlineData(true, true, "{ $project : { _v : { $dateToString : { date : '$D', format : '$Format', timezone : '$Timezone' } }, _id : 0 } }", new[] { "23:04:05", "23:04:05" })] + public void DateTime_ToString_with_format_and_timezone_expressions_should_work(bool withFormat, bool withTimezone, string expectedProjectStage, string[] expectedResults) + { + RequireServer.Check().VersionGreaterThanOrEqualTo("4.0"); + + var collection = CreateCollection(); + + var orderby = collection + .AsQueryable() + .OrderBy(x => x.Id); + + string @null = null; // null typed as string to match the desired overload + var queryable = (withFormat, withTimezone) switch + { + (false, false) => orderby.Select(x => x.D.ToString(@null, @null)), + (true, false) => orderby.Select(x => x.D.ToString(x.Format, @null)), + (false, true) => orderby.Select(x => x.D.ToString(@null, x.Timezone)), + (true, true) => orderby.Select(x => x.D.ToString(x.Format, x.Timezone)) + }; + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $sort : { _id : 1 } }", + expectedProjectStage); + + var results = queryable.ToList(); + results.Should().Equal(expectedResults); + } + + [Fact] + public void NullableDateTime_ToString_with_no_arguments_should_work() + { + RequireServer.Check().Supports(Feature.ToConversionOperators); + + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .OrderBy(x => x.Id) + .Select(x => x.N.ToString()); + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $sort : { _id : 1 } }", + "{ $project : { _v : { $toString : '$N' }, _id : 0 } }"); + + var results = queryable.ToList(); + results.Should().Equal("2021-01-02T03:04:05.123Z", null); + } + + [Theory] + [InlineData(null, null, null, "{ $project : { _v : { $dateToString : { date : '$N' } }, _id : 0 } }", new[] { "2021-01-02T03:04:05.123Z", null })] + [InlineData(null, null, "xx", "{ $project : { _v : { $dateToString : { date : '$N', onNull : 'xx' } }, _id : 0 } }", new[] { "2021-01-02T03:04:05.123Z", "xx" })] + [InlineData("%H:%M:%S", null, null, "{ $project : { _v : { $dateToString : { date : '$N', format : '%H:%M:%S' } }, _id : 0 } }", new[] { "03:04:05", null })] + [InlineData("%H:%M:%S", null, "xx", "{ $project : { _v : { $dateToString : { date : '$N', format : '%H:%M:%S', onNull : 'xx' } }, _id : 0 } }", new[] { "03:04:05", "xx" })] + [InlineData(null, "-04:00", null, "{ $project : { _v : { $dateToString : { date : '$N', timezone : '-04:00' } }, _id : 0 } }", new[] { "2021-01-01T23:04:05.123Z", null })] + [InlineData(null, "-04:00", "xx", "{ $project : { _v : { $dateToString : { date : '$N', timezone : '-04:00', onNull : 'xx' } }, _id : 0 } }", new[] { "2021-01-01T23:04:05.123Z", "xx" })] + [InlineData("%H:%M:%S", "-04:00", null, "{ $project : { _v : { $dateToString : { date : '$N', format : '%H:%M:%S', timezone : '-04:00' } }, _id : 0 } }", new[] { "23:04:05", null })] + [InlineData("%H:%M:%S", "-04:00", "xx", "{ $project : { _v : { $dateToString : { date : '$N', format : '%H:%M:%S', timezone : '-04:00', onNull : 'xx' } }, _id : 0 } }", new[] { "23:04:05", "xx" })] + public void NullableDateTime_ToString_with_format_and_timezone_and_onNull_constants_should_work(string format, string timezone, string onNull, string expectedProjectStage, string[] expectedResults) + { + if (format == null || onNull != null) + { + RequireServer.Check().VersionGreaterThanOrEqualTo("4.0"); + } + + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .OrderBy(x => x.Id) + .Select(x => x.N.ToString(format, timezone, onNull)); + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $sort : { _id : 1 } }", + expectedProjectStage); + + var results = queryable.ToList(); + results.Should().Equal(expectedResults); + } + + [Theory] + [InlineData(false, false, false, "{ $project : { _v : { $dateToString : { date : '$N' } }, _id : 0 } }", new[] { "2021-01-02T03:04:05.123Z", null })] + [InlineData(false, false, true, "{ $project : { _v : { $dateToString : { date : '$N', onNull : '$OnNull' } }, _id : 0 } }", new[] { "2021-01-02T03:04:05.123Z", "missing" })] + [InlineData(false, true, false, "{ $project : { _v : { $dateToString : { date : '$N', timezone : '$Timezone' } }, _id : 0 } }", new[] { "2021-01-01T23:04:05.123Z", null })] + [InlineData(false, true, true, "{ $project : { _v : { $dateToString : { date : '$N', timezone : '$Timezone', onNull : '$OnNull' } }, _id : 0 } }", new[] { "2021-01-01T23:04:05.123Z", "missing" })] + [InlineData(true, false, false, "{ $project : { _v : { $dateToString : { date : '$N', format : '$Format' } }, _id : 0 } }", new[] { "03:04:05", null })] + [InlineData(true, false, true, "{ $project : { _v : { $dateToString : { date : '$N', format : '$Format', onNull : '$OnNull' } }, _id : 0 } }", new[] { "03:04:05", "missing" })] + [InlineData(true, true, false, "{ $project : { _v : { $dateToString : { date : '$N', format : '$Format', timezone : '$Timezone' } }, _id : 0 } }", new[] { "23:04:05", null })] + [InlineData(true, true, true, "{ $project : { _v : { $dateToString : { date : '$N', format : '$Format', timezone : '$Timezone', onNull : '$OnNull' } }, _id : 0 } }", new[] { "23:04:05", "missing" })] + public void NullableDateTime_ToString_with_format_and_timezone_and_onNull_expressions_should_work(bool withFormat, bool withTimezone, bool withOnNull, string expectedProjectStage, string[] expectedResults) + { + RequireServer.Check().VersionGreaterThanOrEqualTo("4.0"); + + var collection = CreateCollection(); + + var orderby = collection + .AsQueryable() + .OrderBy(x => x.Id); + + string @null = null; // null typed as string to match the desired overload + var queryable = (withFormat, withTimezone, withOnNull) switch + { + (false, false, false) => orderby.Select(x => x.N.ToString(@null, @null, @null)), + (false, false, true) => orderby.Select(x => x.N.ToString(@null, @null, x.OnNull)), + (false, true, false) => orderby.Select(x => x.N.ToString(@null, x.Timezone, @null)), + (false, true, true) => orderby.Select(x => x.N.ToString(@null, x.Timezone, x.OnNull)), + (true, false, false) => orderby.Select(x => x.N.ToString(x.Format, @null, @null)), + (true, false, true) => orderby.Select(x => x.N.ToString(x.Format, @null, x.OnNull)), + (true, true, false) => orderby.Select(x => x.N.ToString(x.Format, x.Timezone, @null)), + (true, true, true) => orderby.Select(x => x.N.ToString(x.Format, x.Timezone, x.OnNull)) + }; + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $sort : { _id : 1 } }", + expectedProjectStage); + + var results = queryable.ToList(); + results.Should().Equal(expectedResults); + } + + private IMongoCollection CreateCollection() + { + var collection = GetCollection(); + + CreateCollection( + collection, + new C { Id = 1, D = new DateTime(2021, 1, 2, 3, 4, 5, 123, DateTimeKind.Utc), N = new DateTime(2021, 1, 2, 3, 4, 5, 123, DateTimeKind.Utc), Format = "%H:%M:%S", Timezone = "-04:00", OnNull = "missing" }, + new C { Id = 2, D = new DateTime(2021, 1, 2, 3, 4, 5, 123, DateTimeKind.Utc), N = null, Format = "%H:%M:%S", Timezone = "-04:00", OnNull = "missing" }); + + return collection; + } + + private class C + { + public int Id { get; set; } + public DateTime D { get; set; } + public DateTime? N { get; set; } + public string Format { get; set; } + public string Timezone { get; set; } + public string OnNull { get; set; } + } + + private class ProductTypeSearchResult + { + public bool IsExternalUrl { get; set; } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp3144Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp3144Tests.cs new file mode 100644 index 00000000000..c32a145477d --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp3144Tests.cs @@ -0,0 +1,110 @@ +/* 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.Collections.Generic; +using System.Linq; +using FluentAssertions; +using MongoDB.Driver.Linq; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira +{ + public class CSharp3144Tests : Linq3IntegrationTest + { + [Fact] + public void Where_with_Contains_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .Where(x => x.Items.Select(e => e.GoodId).Contains(2)); + + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $match : { 'Items.GoodId' : 2 } }"); + + var results = queryable.ToList(); + results.Select(r => r.Id).Should().Equal(1); + } + + [Fact] + public void Suggested_workaround_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .Where(x => x.Items.Select(e => e.GoodId).Any(e => e == 2)); + + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $match : { 'Items.GoodId' : 2 } }"); + + var results = queryable.ToList(); + results.Select(r => r.Id).Should().Equal(1); + } + + private IMongoCollection CreateCollection() + { + var collection = GetCollection(); + + CreateCollection( + collection, + new Order + { + Id = 1, + Items = new List + { + new OrderItem { GoodId = 1, Amount = 1 }, + new OrderItem { GoodId = 2, Amount = 10 }, + new OrderItem { GoodId = 3, Amount = 20 }, + new OrderItem { GoodId = 4, Amount = 30 }, + new OrderItem { GoodId = 5, Amount = 40 } + } + }, + new Order + { + Id = 2, + Items = new List + { + new OrderItem { GoodId = 6, Amount = 1 }, + new OrderItem { GoodId = 7, Amount = 10 }, + new OrderItem { GoodId = 8, Amount = 20 }, + new OrderItem { GoodId = 9, Amount = 30 }, + new OrderItem { GoodId = 10, Amount = 40 } + } + }); + + return collection; + } + + class Order + { + public virtual int Id { get; set; } + public virtual List Items { get; set; } + + public Order() + { + Items = new List(); + } + } + + class OrderItem + { + public virtual int Id { get; set; } + public virtual int GoodId { get; set; } + public virtual int Amount { get; set; } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp3197Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp3197Tests.cs new file mode 100644 index 00000000000..536519e919d --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp3197Tests.cs @@ -0,0 +1,62 @@ +/* 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.Linq; +using FluentAssertions; +using MongoDB.Driver.Linq; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira +{ + public class CSharp3197Tests : Linq3IntegrationTest + { + [Fact] + public void Select_select_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .Select(i => new { A = i.Age }) + .Select(i => new { B = i.A }); + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $project : { A : '$Age', _id : 0 } }", + "{ $project : { B : '$A', _id : 0 } }"); + + var result = queryable.Single(); + result.Should().Be(new { B = 42 }); + } + + private IMongoCollection CreateCollection() + { + var collection = GetCollection("C"); + + CreateCollection( + collection, + new Person { Id = 1, Age = 42 }); + + return collection; + } + + private class Person + { + public int Id { get; set; } + public int Age { get; set; } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp3234Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp3234Tests.cs new file mode 100644 index 00000000000..d828257a911 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp3234Tests.cs @@ -0,0 +1,97 @@ +/* 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.Driver.Linq; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira +{ + public class CSharp3234Tests : Linq3IntegrationTest + { + [Fact] + public void Contains_should_work() + { + var collection = CreateCollection(); + var selectedIds = new[] { 1, 2, 3 }; + + var queryable = collection + .AsQueryable() + .Where(x => selectedIds.Contains(x.Id)); + + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $match : { _id : { $in : [1, 2, 3] } } }"); + + var results = queryable.ToList(); + results.OrderBy(x => x.Id).Select(x => x.Id).Should().Equal(1, 2, 3); + } + + [Fact] + public void Contains_equals_false_should_work() + { + var collection = CreateCollection(); + var selectedIds = new[] { 1, 2, 3 }; + + var queryable = collection + .AsQueryable() + .Where(x => selectedIds.Contains(x.Id) == false); + + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $match : { _id : { $nin : [1, 2, 3] } } }"); + + var results = queryable.ToList(); + results.OrderBy(x => x.Id).Select(x => x.Id).Should().Equal(4, 5); + } + + [Fact] + public void Contains_equals_true_should_work() + { + var collection = CreateCollection(); + var selectedIds = new[] { 1, 2, 3 }; + + var queryable = collection + .AsQueryable() + .Where(x => selectedIds.Contains(x.Id) == true); + + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $match : { _id : { $in : [1, 2, 3] } } }"); + + var results = queryable.ToList(); + results.OrderBy(x => x.Id).Select(x => x.Id).Should().Equal(1, 2, 3); + } + + private IMongoCollection CreateCollection() + { + var collection = GetCollection("C"); + + CreateCollection( + collection, + new C { Id = 1 }, + new C { Id = 2 }, + new C { Id = 3 }, + new C { Id = 4 }, + new C { Id = 5 }); + + return collection; + } + + private class C + { + public int Id { get; set; } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp3236Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp3236Tests.cs new file mode 100644 index 00000000000..9aa2309dd04 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp3236Tests.cs @@ -0,0 +1,82 @@ +/* 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.Collections.Generic; +using System.Linq; +using FluentAssertions; +using MongoDB.Driver.Linq; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira +{ + public class CSharp3236Tests : Linq3IntegrationTest + { + [Fact] + public void Select_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .Select(p => new + { + Id = p.Id, + Comments = p.Comments.Where(c => c.Text.Contains("test")) + }); + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $project : { Id : '$_id', Comments : { $filter : { input : '$Comments', as : 'c', cond : { $gte : [{ $indexOfCP : ['$$c.Text', 'test'] }, 0] } } }, _id : 0 } }"); + + var result = queryable.Single(); + result.Id.Should().Be(1); + result.Comments.Select(c => c.Id).Should().Equal(1, 3); + } + + private IMongoCollection CreateCollection() + { + var collection = GetCollection("C"); + + CreateCollection( + collection, + new Post + { + Id = 1, + Comments = new List + { + new Comment { Id = 1, Text = "this is a test comment" }, + new Comment { Id = 2, Text = "this is not" }, + new Comment { Id = 3, Text = "and this is another test comment" } + } + }); + + return collection; + } + + private class Post + { + public int Id { get; set; } + public List Comments { get; set; } + + } + + public class Comment + { + public int Id { get; set; } + public string Text { get; set; } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp3924Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp3924Tests.cs new file mode 100644 index 00000000000..3c8b31f7280 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp3924Tests.cs @@ -0,0 +1,152 @@ +/* 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 FluentAssertions; +using MongoDB.Driver.Linq; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira +{ + public class CSharp3924Tests : Linq3IntegrationTest + { + [Fact] + public void Projection_with_call_to_Tuple1_constructor_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .Select(x => new Tuple(x.X)); + + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $project : { Item1 : '$X', _id : 0 } }"); + + var result = queryable.Single(); + result.Should().Be(new Tuple(1)); + } + + [Fact] + public void Projection_with_call_to_Tuple2_constructor_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .Select(x => new Tuple(x.X, x.Y)); + + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $project : { Item1 : '$X', Item2 : '$Y', _id : 0 } }"); + + var result = queryable.Single(); + result.Should().Be(new Tuple(1, 11)); + } + + [Fact] + public void Projection_with_call_to_Tuple3_constructor_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .Select(x => new Tuple(x.X, x.Y, x.Z)); + + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $project : { Item1 : '$X', Item2 : '$Y', Item3 : '$Z', _id : 0 } }"); + + var result = queryable.Single(); + result.Should().Be(new Tuple(1, 11, 111)); + } + + [Fact] + public void Where_with_Tuple1_item_comparisons_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .Select(x => new Tuple(x.X)) + .Where(x => x.Item1 == 1); + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $project : { Item1 : '$X', _id : 0 } }", + "{ $match : { Item1 : 1 } }"); + + var result = queryable.Single(); + result.Should().Be(new Tuple(1)); + } + + [Fact] + public void Where_with_Tuple2_item_comparisons_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .Select(x => new Tuple(x.X, x.Y)) + .Where(x => x.Item1 == 1 && x.Item2 == 11); + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $project : { Item1 : '$X', Item2 : '$Y', _id : 0 } }", + "{ $match : { Item1 : 1, Item2 : 11 } }"); + + var result = queryable.Single(); + result.Should().Be(new Tuple(1, 11)); + } + + [Fact] + public void Where_with_Tuple3_item_comparisons_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .Select(x => new Tuple(x.X, x.Y, x.Z)) + .Where(x => x.Item1 == 1 && x.Item2 == 11 && x.Item3 == 111); + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $project : { Item1 : '$X', Item2 : '$Y', Item3: '$Z', _id : 0 } }", + "{ $match : { Item1 : 1, Item2 : 11, Item3 : 111 } }"); + + var result = queryable.Single(); + result.Should().Be(new Tuple(1, 11, 111)); + } + + private IMongoCollection CreateCollection() + { + var collection = GetCollection("C"); + + CreateCollection( + collection, + new C { Id = 1, X = 1, Y = 11, Z = 111 }); + + return collection; + } + + private class C + { + public int Id { get; set; } + public int X { get; set; } + public int Y { get; set; } + public int Z { get; set; } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp3933Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp3933Tests.cs index aa151624d0c..f2c9e42a784 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp3933Tests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp3933Tests.cs @@ -15,12 +15,10 @@ using System; using System.Linq; -using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Bson.TestHelpers.XunitExtensions; using MongoDB.Driver.Linq; -using MongoDB.Driver.Linq.Linq3Implementation.Serializers; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira @@ -126,56 +124,27 @@ public void PipelineDefinitionBuilder_Group_with_projection_to_TOutput_should_wo Linq3TestHelpers.AssertStages(stages, expectedStages); } - [Fact] - public void PipelineDefinitionBuilder_Group_with_expressions_should_work_with_LINQ2() - { - var emptyPipeline = (PipelineDefinition)new EmptyPipelineDefinition(); - - var pipeline = emptyPipeline.Group(x => 1, x => new { Count = x.Count() }); - - var stages = Linq3TestHelpers.Render(pipeline, BsonDocumentSerializer.Instance, LinqProvider.V2); - var expectedStages = new[] - { - "{ $group : { _id : 1, Count : { $sum : 1 } } }" - }; - Linq3TestHelpers.AssertStages(stages, expectedStages); - } - - [Fact] - public void PipelineDefinitionBuilder_Group_with_expressions_should_throw_with_LINQ3() + [Theory] + [ParameterAttributeData] + public void PipelineDefinitionBuilder_Group_with_expressions_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { var emptyPipeline = (PipelineDefinition)new EmptyPipelineDefinition(); var pipeline = emptyPipeline.Group(x => 1, x => new { Count = x.Count() }); - var exception = Record.Exception(() => Linq3TestHelpers.Render(pipeline, BsonDocumentSerializer.Instance, LinqProvider.V3)); - exception.Should().BeOfType(); - } - - [Fact] - public void PipelineDefinitionBuilder_GroupForLinq3_with_expressions_should_throw_with_LINQ2() - { - var emptyPipeline = (PipelineDefinition)new EmptyPipelineDefinition(); - - var pipeline = emptyPipeline.GroupForLinq3(x => 1, x => new { Count = x.Count() }); - - var exception = Record.Exception(() => Linq3TestHelpers.Render(pipeline, BsonDocumentSerializer.Instance, LinqProvider.V2)); - exception.Should().BeOfType(); - } - - [Fact] - public void PipelineDefinitionBuilder_GroupForLinq3_with_expressions_should_work_with_LINQ3() - { - var emptyPipeline = (PipelineDefinition)new EmptyPipelineDefinition(); - - var pipeline = emptyPipeline.GroupForLinq3(x => 1, x => new { Count = x.Count() }); - - var stages = Linq3TestHelpers.Render(pipeline, BsonDocumentSerializer.Instance, LinqProvider.V3); - var expectedStages = new[] - { - "{ $group : { _id : 1, __agg0 : { $sum : 1 } } }", - "{ $project : { Count : '$__agg0', _id : 0 } }" - }; + var stages = Linq3TestHelpers.Render(pipeline, BsonDocumentSerializer.Instance, linqProvider); + var expectedStages = linqProvider == LinqProvider.V2 ? + new[] + { + "{ $group : { _id : 1, Count : { $sum : 1 } } }" + } + : + new[] + { + "{ $group : { _id : 1, __agg0 : { $sum : 1 } } }", + "{ $project : { Count : '$__agg0', _id : 0 } }" + }; Linq3TestHelpers.AssertStages(stages, expectedStages); } @@ -186,12 +155,12 @@ public void PipelineStageDefinitionBuilder_Group_with_projection_to_implied_Bson { var stageDefinition = PipelineStageDefinitionBuilder.Group("{ _id : 1, Count : { $sum : 1 } }"); - var stage = Linq3TestHelpers.Render(stageDefinition, BsonDocumentSerializer.Instance, linqProvider); + var stages = Linq3TestHelpers.Render(stageDefinition, BsonDocumentSerializer.Instance, linqProvider); var expectedStages = new[] { "{ $group : { _id : 1, Count : { $sum : 1 } } }" }; - Linq3TestHelpers.AssertStages(new[] { stage }, expectedStages); + Linq3TestHelpers.AssertStages(stages, expectedStages); } [Theory] @@ -201,64 +170,39 @@ public void PipelineStageDefinitionBuilder_Group_with_projection_to_TOutput_shou { var stageDefinition = PipelineStageDefinitionBuilder.Group("{ _id : 1, Count : { $sum : 1 } }"); - var stage = Linq3TestHelpers.Render(stageDefinition, BsonDocumentSerializer.Instance, linqProvider); - var expectedStages = new[] - { - "{ $group : { _id : 1, Count : { $sum : 1 } } }" - }; - Linq3TestHelpers.AssertStages(new[] { stage }, expectedStages); - } - - [Fact] - public void PipelineStageDefinitionBuilder_Group_with_expressions_should_work_with_LINQ2() - { - var stageDefinition = PipelineStageDefinitionBuilder.Group((BsonDocument x) => 1, x => new { Count = x.Count() }); - - var stage = Linq3TestHelpers.Render(stageDefinition, BsonDocumentSerializer.Instance, LinqProvider.V2); + var stages = Linq3TestHelpers.Render(stageDefinition, BsonDocumentSerializer.Instance, linqProvider); var expectedStages = new[] { "{ $group : { _id : 1, Count : { $sum : 1 } } }" }; - Linq3TestHelpers.AssertStages(new[] { stage }, expectedStages); + Linq3TestHelpers.AssertStages(stages, expectedStages); } - [Fact] - public void PipelineStageDefinitionBuilder_Group_with_expressions_should_throw_with_LINQ3() + [Theory] + [ParameterAttributeData] + public void PipelineStageDefinitionBuilder_Group_with_expressions_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { var stageDefinition = PipelineStageDefinitionBuilder.Group((BsonDocument x) => 1, x => new { Count = x.Count() }); - var exception = Record.Exception(() => Linq3TestHelpers.Render(stageDefinition, BsonDocumentSerializer.Instance, LinqProvider.V3)); - exception.Should().BeOfType(); - } - - [Fact] - public void PipelineStageDefinitionBuilder_GroupForLinq3_with_expressions_should_throw_with_LINQ2() - { - var (groupStageDefinition, projectStageDefinition) = PipelineStageDefinitionBuilder.GroupForLinq3((BsonDocument x) => 1, x => new { Count = x.Count() }); - - var exception = Record.Exception(() => Linq3TestHelpers.Render(groupStageDefinition, BsonDocumentSerializer.Instance, LinqProvider.V2)); - exception.Should().BeOfType(); - } - - [Fact] - public void PipelineStageDefinitionBuilderGroupForLinq3_with_expressions_should_work_with_LINQ3() - { - var (groupStageDefinition, projectStageDefinition) = PipelineStageDefinitionBuilder.GroupForLinq3((BsonDocument x) => 1, x => new { Count = x.Count() }); - - var groupStage = Linq3TestHelpers.Render(groupStageDefinition, BsonDocumentSerializer.Instance, LinqProvider.V3); - var groupingSerializer = new IGroupingSerializer(new Int32Serializer(), BsonDocumentSerializer.Instance); - var projectStage = Linq3TestHelpers.Render(projectStageDefinition, groupingSerializer, LinqProvider.V3); - var expectedStages = new[] - { - "{ $group : { _id : 1, __agg0 : { $sum : 1 } } }", - "{ $project : { Count : '$__agg0', _id : 0 } }" - }; - Linq3TestHelpers.AssertStages(new[] { groupStage, projectStage }, expectedStages); + var stages = Linq3TestHelpers.Render(stageDefinition, BsonDocumentSerializer.Instance, linqProvider); + var expectedStages = linqProvider == LinqProvider.V2 ? + new[] + { + "{ $group : { _id : 1, Count : { $sum : 1 } } }" + } + : + new[] + { + "{ $group : { _id : 1, __agg0 : { $sum : 1 } } }", + "{ $project : { Count : '$__agg0', _id : 0 } }" + }; + Linq3TestHelpers.AssertStages(stages, expectedStages); } private IMongoCollection GetCollection(LinqProvider linqProvider) { - var client = linqProvider == LinqProvider.V2 ? DriverTestConfiguration.Client : DriverTestConfiguration.Linq3Client; + var client = DriverTestConfiguration.GetLinqClient(linqProvider); var database = client.GetDatabase("test"); return database.GetCollection("test"); } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp3965Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp3965Tests.cs new file mode 100644 index 00000000000..afe2a0091d4 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp3965Tests.cs @@ -0,0 +1,461 @@ +/* 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.Linq; +using FluentAssertions; +using MongoDB.Driver.Linq; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira +{ + public class CSharp3965Tests : Linq3IntegrationTest + { + [Fact] + public void OrderBy_with_expression_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .OrderBy(x => x.Y + 1); + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $project : { _id : 0, _document : '$$ROOT', _key1 : { $add : ['$Y', 1] } } }", + "{ $sort : { _key1 : 1 } }", + "{ $replaceRoot : { newRoot : '$_document' } }"); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(1, 2); + } + + [Fact] + public void OrderBy_with_expression_and_ThenBy_with_expression_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .OrderBy(x => x.X + 1) + .ThenBy(x => x.Y + 1); + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $project : { _id : 0, _document : '$$ROOT', _key1 : { $add : ['$X', 1] }, _key2 : { $add : ['$Y', 1] } } }", + "{ $sort : { _key1 : 1, _key2 : 1 } }", + "{ $replaceRoot : { newRoot : '$_document' } }"); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(1, 2); + } + + [Fact] + public void OrderBy_with_expression_and_ThenBy_with_field_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .OrderBy(x => x.X + 1) + .ThenBy(x => x.Y); + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $project : { _id : 0, _document : '$$ROOT', _key1 : { $add : ['$X', 1] } } }", + "{ $sort : { _key1 : 1, '_document.Y' : 1 } }", + "{ $replaceRoot : { newRoot : '$_document' } }"); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(1, 2); + } + + [Fact] + public void OrderBy_with_expression_and_ThenByDescending_with_expression_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .OrderBy(x => x.X + 1) + .ThenByDescending(x => x.Y + 1); + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $project : { _id : 0, _document : '$$ROOT', _key1 : { $add : ['$X', 1] }, _key2 : { $add : ['$Y', 1] } } }", + "{ $sort : { _key1 : 1, _key2 : -1 } }", + "{ $replaceRoot : { newRoot : '$_document' } }"); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(2, 1); + } + + [Fact] + public void OrderBy_with_expression_and_ThenByDescending_with_field_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .OrderBy(x => x.X + 1) + .ThenByDescending(x => x.Y); + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $project : { _id : 0, _document : '$$ROOT', _key1 : { $add : ['$X', 1] } } }", + "{ $sort : { _key1 : 1, '_document.Y' : -1 } }", + "{ $replaceRoot : { newRoot : '$_document' } }"); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(2, 1); + } + + [Fact] + public void OrderBy_with_field_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .OrderBy(x => x.Y); + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $sort : { Y : 1 } }"); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(1, 2); + } + + [Fact] + public void OrderBy_with_field_and_ThenBy_with_expression_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .OrderBy(x => x.X) + .ThenBy(x => x.Y + 1); + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $project : { _id : 0, _document : '$$ROOT', _key1 : { $add : ['$Y', 1] } } }", + "{ $sort : { '_document.X' : 1, _key1 : 1 } }", + "{ $replaceRoot : { newRoot : '$_document' } }"); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(1, 2); + } + + [Fact] + public void OrderBy_with_field_and_ThenBy_with_field_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .OrderBy(x => x.X) + .ThenBy(x => x.Y); + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $sort : { X : 1, Y : 1 } }"); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(1, 2); + } + + [Fact] + public void OrderBy_with_field_and_ThenByDescending_with_expression_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .OrderBy(x => x.X) + .ThenByDescending(x => x.Y + 1); + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $project : { _id : 0, _document : '$$ROOT', _key1 : { $add : ['$Y', 1] } } }", + "{ $sort : { '_document.X' : 1, _key1 : -1 } }", + "{ $replaceRoot : { newRoot : '$_document' } }"); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(2, 1); + } + + [Fact] + public void OrderBy_with_field_and_ThenByDescending_with_field_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .OrderBy(x => x.X) + .ThenByDescending(x => x.Y); + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $sort : { X : 1, Y : -1 } }"); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(2, 1); + } + + [Fact] + public void OrderByDescending_with_expression_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .OrderByDescending(x => x.Y + 1); + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $project : { _id : 0, _document : '$$ROOT', _key1 : { $add : ['$Y', 1] } } }", + "{ $sort : { _key1 : -1 } }", + "{ $replaceRoot : { newRoot : '$_document' } }"); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(2, 1); + } + + [Fact] + public void OrderByDescending_with_expression_and_ThenBy_with_expression_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .OrderByDescending(x => x.X + 1) + .ThenBy(x => x.Y + 1); + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $project : { _id : 0, _document : '$$ROOT', _key1 : { $add : ['$X', 1] }, _key2 : { $add : ['$Y', 1] } } }", + "{ $sort : { _key1 : -1, _key2 : 1 } }", + "{ $replaceRoot : { newRoot : '$_document' } }"); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(1, 2); + } + + [Fact] + public void OrderByDescending_with_expression_and_ThenBy_with_field_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .OrderByDescending(x => x.X + 1) + .ThenBy(x => x.Y); + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $project : { _id : 0, _document : '$$ROOT', _key1 : { $add : ['$X', 1] } } }", + "{ $sort : { _key1 : -1, '_document.Y' : 1 } }", + "{ $replaceRoot : { newRoot : '$_document' } }"); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(1, 2); + } + + [Fact] + public void OrderByDescending_with_expression_and_ThenByDescending_with_expression_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .OrderByDescending(x => x.X + 1) + .ThenByDescending(x => x.Y + 1); + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $project : { _id : 0, _document : '$$ROOT', _key1 : { $add : ['$X', 1] }, _key2 : { $add : ['$Y', 1] } } }", + "{ $sort : { _key1 : -1, _key2 : -1 } }", + "{ $replaceRoot : { newRoot : '$_document' } }"); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(2, 1); + } + + [Fact] + public void OrderByDescending_with_expression_and_ThenByDescending_with_field_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .OrderByDescending(x => x.X + 1) + .ThenByDescending(x => x.Y); + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $project : { _id : 0, _document : '$$ROOT', _key1 : { $add : ['$X', 1] } } }", + "{ $sort : { _key1 : -1, '_document.Y' : -1 } }", + "{ $replaceRoot : { newRoot : '$_document' } }"); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(2, 1); + } + + [Fact] + public void OrderByDescending_with_field_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .OrderByDescending(x => x.Y); + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $sort : { Y : -1 } }"); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(2, 1); + } + + [Fact] + public void OrderByDescending_with_field_and_ThenBy_with_expression_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .OrderByDescending(x => x.X) + .ThenBy(x => x.Y + 1); + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $project : { _id : 0, _document : '$$ROOT', _key1 : { $add : ['$Y', 1] } } }", + "{ $sort : { '_document.X' : -1, _key1 : 1 } }", + "{ $replaceRoot : { newRoot : '$_document' } }"); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(1, 2); + } + + [Fact] + public void OrderByDescending_with_field_and_ThenBy_with_field_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .OrderByDescending(x => x.X) + .ThenBy(x => x.Y); + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $sort : { X : -1, Y : 1 } }"); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(1, 2); + } + + [Fact] + public void OrderByDescending_with_field_and_ThenByDescending_with_expression_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .OrderByDescending(x => x.X) + .ThenByDescending(x => x.Y + 1); + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $project : { _id : 0, _document : '$$ROOT', _key1 : { $add : ['$Y', 1] } } }", + "{ $sort : { '_document.X' : -1, _key1 : -1 } }", + "{ $replaceRoot : { newRoot : '$_document' } }"); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(2, 1); + } + + [Fact] + public void OrderByDescending_with_field_and_ThenByDescending_with_field_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .OrderByDescending(x => x.X) + .ThenByDescending(x => x.Y); + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $sort : { X : -1, Y : -1 } }"); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(2, 1); + } + + private IMongoCollection CreateCollection() + { + var collection = GetCollection(); + + var documents = new C[] + { + new C + { + Id = 1, + X = 1, + Y = 1, + }, + new C + { + Id = 2, + X = 1, + Y = 2 + } + + }; + CreateCollection(collection, documents); + + return collection; + } + + private class C + { + public int Id { get; set; } + public int X { get; set; } + public int Y { get; set; } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4048Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4048Tests.cs index 1cff2edaa99..b4b6a3c4ed1 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4048Tests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4048Tests.cs @@ -311,8 +311,8 @@ public void IGrouping_All_of_scalar_should_work() var stages = Translate(collection, queryable); var expectedStages = new[] { - "{ $group : { _id : '$_id', _elements : { $push: '$X' } } }", // MQL could be optimized further - "{ $project : { Id : '$_id', Result : { $allElementsTrue : { $map : { input : '$_elements', as : 'e', in : { $gt : ['$$e', 0] } } } }, _id : 0 } }", + "{ $group : { _id : '$_id', __agg0 : { $push: { $gt : ['$X', 0] } } } }", // MQL could be optimized further + "{ $project : { Id : '$_id', Result : { $allElementsTrue : '$__agg0' }, _id : 0 } }", "{ $sort : { Id : 1 } }" }; AssertStages(stages, expectedStages); @@ -361,8 +361,8 @@ public void IGrouping_Any_of_scalar_should_work() var stages = Translate(collection, queryable); var expectedStages = new[] { - "{ $group : { _id : '$_id', _elements : { $push : '$X' } } }", // MQL could be optimized further - "{ $project : { Id : '$_id', Result : { $gt : [{ $size : '$_elements' }, 0] }, _id : 0 } }", + "{ $group : { _id : '$_id', __agg0 : { $sum : 1 } } }", + "{ $project : { Id : '$_id', Result : { $gt : ['$__agg0', 0] }, _id : 0 } }", "{ $sort : { Id : 1 } }" }; AssertStages(stages, expectedStages); @@ -411,8 +411,8 @@ public void IGrouping_Any_with_predicate_of_scalar_should_work() var stages = Translate(collection, queryable); var expectedStages = new[] { - "{ $group : { _id : '$_id', _elements : { $push : '$X' } } }", // MQL could be optimized further - "{ $project : { Id : '$_id', Result : { $anyElementTrue : { $map : { input : '$_elements', as : 'e', in : { $gt : ['$$e', 0] } } } }, _id : 0 } }", + "{ $group : { _id : '$_id', __agg0 : { $push : { $gt : ['$X', 0] } } } }", // MQL could be optimized further + "{ $project : { Id : '$_id', Result : { $anyElementTrue : '$__agg0' }, _id : 0 } }", "{ $sort : { Id : 1 } }" }; AssertStages(stages, expectedStages); @@ -461,8 +461,8 @@ public void IGrouping_Average_of_scalar_should_work() var stages = Translate(collection, queryable); var expectedStages = new[] { - "{ $group : { _id : '$_id', _elements : { $push : '$X' } } }", // MQL could be optimized further - "{ $project : { Id : '$_id', Result : { $avg : '$_elements' }, _id : 0 } }", + "{ $group : { _id : '$_id', __agg0 : { $avg : '$X' } } }", + "{ $project : { Id : '$_id', Result : '$__agg0', _id : 0 } }", "{ $sort : { Id : 1 } }" }; AssertStages(stages, expectedStages); @@ -511,8 +511,8 @@ public void IGrouping_Average_with_selector_of_scalar_should_work() var stages = Translate(collection, queryable); var expectedStages = new[] { - "{ $group : { _id : '$_id', _elements : { $push : '$X' } } }", // MQL could be optimized further - "{ $project : { Id : '$_id', Result : { $avg : { $map : { input : '$_elements', as : 'e', in : '$$e' } } }, _id : 0 } }", + "{ $group : { _id : '$_id', __agg0 : { $avg : '$X' } } }", + "{ $project : { Id : '$_id', Result : '$__agg0', _id : 0 } }", "{ $sort : { Id : 1 } }" }; AssertStages(stages, expectedStages); @@ -661,8 +661,8 @@ public void IGrouping_Count_of_scalar_should_work() var stages = Translate(collection, queryable); var expectedStages = new[] { - "{ $group : { _id : '$_id', _elements : { $push : '$X' } } }", // MQL could be optimized further - "{ $project : { Id : '$_id', Result : { $size : '$_elements' }, _id : 0 } }", + "{ $group : { _id : '$_id', __agg0 : { $sum : 1 } } }", + "{ $project : { Id : '$_id', Result : '$__agg0', _id : 0 } }", "{ $sort : { Id : 1 } }" }; AssertStages(stages, expectedStages); @@ -989,8 +989,8 @@ public void IGrouping_First_of_scalar_should_work() var stages = Translate(collection, queryable); var expectedStages = new[] { - "{ $group : { _id : '$_id', _elements : { $push : '$X' } } }", // MQL could be optimized further - "{ $project : { Id : '$_id', Result : { $arrayElemAt : ['$_elements', 0] }, _id : 0 } }", + "{ $group : { _id : '$_id', __agg0 : { $first : '$X' } } }", + "{ $project : { Id : '$_id', Result : '$__agg0', _id : 0 } }", "{ $sort : { Id : 1 } }" }; AssertStages(stages, expectedStages); @@ -1195,8 +1195,8 @@ public void IGrouping_Last_of_scalar_should_work() var stages = Translate(collection, queryable); var expectedStages = new[] { - "{ $group : { _id : '$_id', _elements : { $push : '$X' } } }", // MQL could be optimized further - "{ $project : { Id : '$_id', Result : { $arrayElemAt : ['$_elements', -1] }, _id : 0 } }", + "{ $group : { _id : '$_id', __agg0 : { $last : '$X' } } }", + "{ $project : { Id : '$_id', Result : '$__agg0', _id : 0 } }", "{ $sort : { Id : 1 } }" }; AssertStages(stages, expectedStages); @@ -1351,8 +1351,8 @@ public void IGrouping_LongCount_of_scalar_should_work() var stages = Translate(collection, queryable); var expectedStages = new[] { - "{ $group : { _id : '$_id', _elements : { $push : '$X' } } }", // MQL could be optimized further - "{ $project : { Id : '$_id', Result : { $size : '$_elements' }, _id : 0 } }", + "{ $group : { _id : '$_id', __agg0 : { $sum : 1 } } }", + "{ $project : { Id : '$_id', Result : '$__agg0', _id : 0 } }", "{ $sort : { Id : 1 } }" }; AssertStages(stages, expectedStages); @@ -1451,8 +1451,8 @@ public void IGrouping_Max_of_scalar_should_work() var stages = Translate(collection, queryable); var expectedStages = new[] { - "{ $group : { _id : '$_id', _elements : { $push : '$X' } } }", // MQL could be optimized further - "{ $project : { Id : '$_id', Result : { $max : '$_elements' }, _id : 0 } }", + "{ $group : { _id : '$_id', __agg0 : { $max : '$X' } } }", + "{ $project : { Id : '$_id', Result : '$__agg0', _id : 0 } }", "{ $sort : { Id : 1 } }" }; AssertStages(stages, expectedStages); @@ -1501,8 +1501,8 @@ public void IGrouping_Max_with_selector_of_scalar_should_work() var stages = Translate(collection, queryable); var expectedStages = new[] { - "{ $group : { _id : '$_id', _elements : { $push : '$X' } } }", // MQL could be optimized further - "{ $project : { Id : '$_id', Result : { $max : { $map : { input : '$_elements', as : 'e', in : '$$e' } } }, _id : 0 } }", + "{ $group : { _id : '$_id', __agg0 : { $max : '$X' } } }", + "{ $project : { Id : '$_id', Result : '$__agg0', _id : 0 } }", "{ $sort : { Id : 1 } }" }; AssertStages(stages, expectedStages); @@ -1551,8 +1551,8 @@ public void IGrouping_Min_of_scalar_should_work() var stages = Translate(collection, queryable); var expectedStages = new[] { - "{ $group : { _id : '$_id', _elements : { $push : '$X' } } }", // MQL could be optimized further - "{ $project : { Id : '$_id', Result : { $min : '$_elements' }, _id : 0 } }", + "{ $group : { _id : '$_id', __agg0 : { $min : '$X' } } }", + "{ $project : { Id : '$_id', Result : '$__agg0', _id : 0 } }", "{ $sort : { Id : 1 } }" }; AssertStages(stages, expectedStages); @@ -1601,8 +1601,8 @@ public void IGrouping_Min_with_selector_of_scalar_should_work() var stages = Translate(collection, queryable); var expectedStages = new[] { - "{ $group : { _id : '$_id', _elements : { $push : '$X' } } }", // MQL could be optimized further - "{ $project : { Id : '$_id', Result : { $min : { $map : { input : '$_elements', as : 'e', in : '$$e' } } }, _id : 0 } }", + "{ $group : { _id : '$_id', __agg0 : { $min : '$X' } } }", + "{ $project : { Id : '$_id', Result : '$__agg0', _id : 0 } }", "{ $sort : { Id : 1 } }" }; AssertStages(stages, expectedStages); @@ -1701,8 +1701,8 @@ public void IGrouping_Select_of_scalar_should_work() var stages = Translate(collection, queryable); var expectedStages = new[] { - "{ $group : { _id : '$_id', _elements : { $push : '$X' } } }", - "{ $project : { Id : '$_id', Result : { $map : { input : '$_elements', as : 'e', in : '$$e' } }, _id : 0 } }", // MQL could be optimized further + "{ $group : { _id : '$_id', __agg0 : { $push : '$X' } } }", + "{ $project : { Id : '$_id', Result : '$__agg0', _id : 0 } }", "{ $sort : { Id : 1 } }" }; AssertStages(stages, expectedStages); @@ -1751,8 +1751,8 @@ public void IGrouping_StandardDeviationPopulation_of_scalar_should_work() var stages = Translate(collection, queryable); var expectedStages = new[] { - "{ $group : { _id : '$_id', _elements : { $push : '$X' } } }", // MQL could be optimized further - "{ $project : { Id : '$_id', Result : { $stdDevPop : '$_elements' }, _id : 0 } }", + "{ $group : { _id : '$_id', __agg0 : { $stdDevPop : '$X' } } }", + "{ $project : { Id : '$_id', Result : '$__agg0', _id : 0 } }", "{ $sort : { Id : 1 } }" }; AssertStages(stages, expectedStages); @@ -1801,8 +1801,8 @@ public void IGrouping_StandardDeviationPopulation_with_selector_of_scalar_should var stages = Translate(collection, queryable); var expectedStages = new[] { - "{ $group : { _id : '$_id', _elements : { $push : '$X' } } }", // MQL could be optimized further - "{ $project : { Id : '$_id', Result : { $stdDevPop : { $map : { input : '$_elements', as : 'e', in : '$$e' } } }, _id : 0 } }", + "{ $group : { _id : '$_id', __agg0 : { $stdDevPop : '$X' } } }", + "{ $project : { Id : '$_id', Result : '$__agg0', _id : 0 } }", "{ $sort : { Id : 1 } }" }; AssertStages(stages, expectedStages); @@ -1851,8 +1851,8 @@ public void IGrouping_StandardDeviationSample_of_scalar_should_work() var stages = Translate(collection, queryable); var expectedStages = new[] { - "{ $group : { _id : '$_id', _elements : { $push : '$X' } } }", // MQL could be optimized further - "{ $project : { Id : '$_id', Result : { $stdDevSamp : '$_elements' }, _id : 0 } }", + "{ $group : { _id : '$_id', __agg0 : { $stdDevSamp : '$X' } } }", + "{ $project : { Id : '$_id', Result : '$__agg0', _id : 0 } }", "{ $sort : { Id : 1 } }" }; AssertStages(stages, expectedStages); @@ -1901,8 +1901,8 @@ public void IGrouping_StandardDeviationSample_with_selector_of_scalar_should_wor var stages = Translate(collection, queryable); var expectedStages = new[] { - "{ $group : { _id : '$_id', _elements : { $push : '$X' } } }", // MQL could be optimized further - "{ $project : { Id : '$_id', Result : { $stdDevSamp : { $map : { input : '$_elements', as : 'e', in : '$$e' } } }, _id : 0 } }", + "{ $group : { _id : '$_id', __agg0 : { $stdDevSamp : '$X' } } }", + "{ $project : { Id : '$_id', Result : '$__agg0', _id : 0 } }", "{ $sort : { Id : 1 } }" }; AssertStages(stages, expectedStages); @@ -1951,8 +1951,8 @@ public void IGrouping_Sum_of_scalar_should_work() var stages = Translate(collection, queryable); var expectedStages = new[] { - "{ $group : { _id : '$_id', _elements : { $push : '$X' } } }", // MQL could be optimized further - "{ $project : { Id : '$_id', Result : { $sum : '$_elements' }, _id : 0 } }", + "{ $group : { _id : '$_id', __agg0 : { $sum : '$X' } } }", + "{ $project : { Id : '$_id', Result : '$__agg0', _id : 0 } }", "{ $sort : { Id : 1 } }" }; AssertStages(stages, expectedStages); diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4079Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4079Tests.cs index 5a7a29df111..e822bbea69f 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4079Tests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4079Tests.cs @@ -17,7 +17,7 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.Serialization; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Linq; using Xunit; @@ -28,15 +28,14 @@ public class CSharp4079Tests : Linq3IntegrationTest [Theory] [ParameterAttributeData] public void Positional_operator_with_negative_one_array_index_should_work_or_throw_depending_on_Linq_provider( - [Values(false, true)] bool useLinq2) + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { - var linqProvider = useLinq2 ? LinqProvider.V2 : LinqProvider.V3; var collection = GetCollection(); var negativeOne = -1; var update = Builders.Update.Set(x => x.A[negativeOne], 0); // using -1 constant is a compile time error - if (useLinq2) + if (linqProvider == LinqProvider.V2) { var rendered = Render(update, linqProvider); rendered.Should().Be("{ $set : { 'A.$' : 0 } }"); @@ -51,14 +50,13 @@ public void Positional_operator_with_negative_one_array_index_should_work_or_thr [Theory] [ParameterAttributeData] public void Positional_operator_with_negative_one_ElementAt_should_work_or_throw_depending_on_Linq_provider( - [Values(false, true)] bool useLinq2) + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { - var linqProvider = useLinq2 ? LinqProvider.V2 : LinqProvider.V3; var collection = GetCollection(); var update = Builders.Update.Set(x => x.A.ElementAt(-1), 0); - if (useLinq2) + if (linqProvider == LinqProvider.V2) { var rendered = Render(update, linqProvider); rendered.Should().Be("{ $set : { 'A.$' : 0 } }"); diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4220Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4220Tests.cs new file mode 100644 index 00000000000..54592dd93e6 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4220Tests.cs @@ -0,0 +1,185 @@ +/* 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; +using MongoDB.Bson.TestHelpers; +using MongoDB.TestHelpers.XunitExtensions; +using MongoDB.Driver.Core.Misc; +using MongoDB.Driver.Core.TestHelpers.XunitExtensions; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira +{ + public class CSharp4220Tests : Linq3IntegrationTest + { + [Fact] + public void Test_a_pipeline_stage_using_aggregate_with_aggregate_expression_should_work() + { + RequireServer.Check().Supports(Feature.DocumentsStage); + var database = GetDatabase(); + var json = + @" + [ + { X : 10 }, + { X : 2 }, + { X : 5 } + ] + "; + var array = BsonSerializer.Deserialize(json); + var documents = new BsonValueAggregateExpressionDefinition>(array); + + var pipeline = + new EmptyPipelineDefinition() + .Documents(documents) + .BucketAuto(groupBy: d => d.X, buckets: 4); + + var stages = Translate(pipeline); + AssertStages( + stages, + "{ $documents : [{ X : 10 }, { X : 2 }, { X : 5 }] }", + "{ $bucketAuto : { groupBy : '$X', buckets : 4 } }"); + + var results = database.Aggregate(pipeline).ToList(); + var comparer = AggregateBucketAutoResultEqualityComparer.Instance; + results.OrderBy(r => r.Id.Min).WithComparer(comparer).Should().Equal( + new AggregateBucketAutoResult(2, 5, 1), + new AggregateBucketAutoResult(5, 10, 1), + new AggregateBucketAutoResult(10, 10, 1)); + } + + [Fact] + public void Test_a_pipeline_stage_using_aggregate_with_enumerable_documents_should_work() + { + RequireServer.Check().Supports(Feature.DocumentsStage); + var database = GetDatabase(); + var documents = new C[] + { + new C { X = 10 }, + new C { X = 2 }, + new C { X = 5 } + }; + + var pipeline = + new EmptyPipelineDefinition() + .Documents(documents) + .BucketAuto(groupBy: d => d.X, buckets: 4); + + var stages = Translate(pipeline); + AssertStages( + stages, + "{ $documents : [{ X : 10 }, { X : 2 }, { X : 5 }] }", + "{ $bucketAuto : { groupBy : '$X', buckets : 4 } }"); + + var results = database.Aggregate(pipeline).ToList(); + var comparer = AggregateBucketAutoResultEqualityComparer.Instance; + results.OrderBy(r => r.Id.Min).WithComparer(comparer).Should().Equal( + new AggregateBucketAutoResult(2, 5, 1), + new AggregateBucketAutoResult(5, 10, 1), + new AggregateBucketAutoResult(10, 10, 1)); + } + + [Fact] + public void Test_a_pipeline_stage_using_fluent_aggregate_with_aggregate_expression_should_work() + { + RequireServer.Check().Supports(Feature.DocumentsStage); + var database = GetDatabase(); + var json = + @" + [ + { X : 10 }, + { X : 2 }, + { X : 5 } + ] + "; + var array = BsonSerializer.Deserialize(json); + var documents = new BsonValueAggregateExpressionDefinition>(array); + + var aggregate = database.Aggregate() + .Documents(documents) + .BucketAuto(groupBy: d => d.X, buckets: 4); + + var stages = Translate(database, aggregate); + AssertStages( + stages, + "{ $documents : [{ X : 10 }, { X : 2 }, { X : 5 }] }", + "{ $bucketAuto : { groupBy : '$X', buckets : 4 } }"); + + var results = aggregate.ToList(); + var comparer = AggregateBucketAutoResultEqualityComparer.Instance; + results.OrderBy(r => r.Id.Min).WithComparer(comparer).Should().Equal( + new AggregateBucketAutoResult(2, 5, 1), + new AggregateBucketAutoResult(5, 10, 1), + new AggregateBucketAutoResult(10, 10, 1)); + } + + [Fact] + public void Test_a_pipeline_stage_using_fluent_aggregate_with_enumerable_documents_should_work() + { + RequireServer.Check().Supports(Feature.DocumentsStage); + var database = GetDatabase(); + var documents = new C[] + { + new C { X = 10 }, + new C { X = 2 }, + new C { X = 5 } + }; + + var aggregate = database.Aggregate() + .Documents(documents) + .BucketAuto(groupBy: d => d.X, buckets: 4); + + var stages = Translate(database, aggregate); + AssertStages( + stages, + "{ $documents : [{ X : 10 }, { X : 2 }, { X : 5 }] }", + "{ $bucketAuto : { groupBy : '$X', buckets : 4 } }"); + + var results = aggregate.ToList(); + var comparer = AggregateBucketAutoResultEqualityComparer.Instance; + results.OrderBy(r => r.Id.Min).WithComparer(comparer).Should().Equal( + new AggregateBucketAutoResult(2, 5, 1), + new AggregateBucketAutoResult(5, 10, 1), + new AggregateBucketAutoResult(10, 10, 1)); + } + + [Theory] + [ParameterAttributeData] + public void Documents_with_documents_of_type_NoPipelineInput_should_throw( + [Values(0, 1, 2)] int count) + { + var documents = new NoPipelineInput[count]; + + var exception = Record.Exception(() => + { + new EmptyPipelineDefinition() + .Documents(documents); + }); + + var argumentException = exception.Should().BeOfType().Subject; + argumentException.ParamName.Should().Be("documents"); + argumentException.Message.Should().Contain("Documents cannot be of type NoPipelineInput."); + } + + private class C + { + public int X { get; set; } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4234Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4234Tests.cs index 85926e30202..2ad1802e53b 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4234Tests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4234Tests.cs @@ -16,6 +16,7 @@ using System.Linq; using FluentAssertions; using MongoDB.Bson; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Linq; using Xunit; @@ -23,12 +24,13 @@ namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira { public class CSharp4234Tests : Linq3IntegrationTest { - [Fact] - public void AppendStage_should_work() + [Theory] + [ParameterAttributeData] + public void AppendStage_should_work([Values(false, true)] bool useResultSerializer) { var collection = CreateProductsCollection(); var textStage = "{ $match : { $text : { $search : 'apples' } } }"; - var resultSerializer = collection.DocumentSerializer; + var resultSerializer = useResultSerializer ? collection.DocumentSerializer : null; var queryable = collection .AsQueryable() diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4244Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4244Tests.cs new file mode 100644 index 00000000000..86bbb1c5f48 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4244Tests.cs @@ -0,0 +1,63 @@ +/* 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.Linq; +using FluentAssertions; +using MongoDB.Driver.Core.TestHelpers.XunitExtensions; +using MongoDB.Driver.Linq; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira +{ + public class CSharp4244Tests : Linq3IntegrationTest + { + [Fact] + public void Where_with_root_should_work() + { + RequireServer.Check().VersionGreaterThanOrEqualTo("6.0"); + + var collection = CreateCollection(); + var person = new Person { Id = 2, Name = "Jane Doe" }; + + var queryable = collection + .AsQueryable() + .Where(p => p == person); + + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $match : { $expr : { $eq : ['$$ROOT', { _id : 2, Name : 'Jane Doe' }] } } }"); + + var results = queryable.ToList(); + results.Single().ShouldBeEquivalentTo(person); + } + + private IMongoCollection CreateCollection() + { + var collection = GetCollection("C"); + + CreateCollection( + collection, + new Person { Id = 1, Name = "John Doe" }, + new Person { Id = 2, Name = "Jane Doe" }); + + return collection; + } + + private class Person + { + public int Id { get; set; } + public string Name { get; set; } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4289Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4289Tests.cs new file mode 100644 index 00000000000..250e9ca299e --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4289Tests.cs @@ -0,0 +1,87 @@ +/* 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.Linq; +using FluentAssertions; +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver.Linq; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira +{ + public class CSharp4289Tests : Linq3IntegrationTest + { + [Fact] + public void Select_using_anonymous_class_should_work() + { + var collection = CreateCollection(); + + var queryable = collection.AsQueryable() + .Select(x => new { V = x.Id, W = x.Id }); + + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $project : { V : '$_id', W : '$_id', _id : 0 } }"); + + var results = queryable.ToList(); + results.Select(r => r.V).Should().Equal("111111111111111111111111"); + results.Select(r => r.W).Should().Equal("111111111111111111111111"); + } + + [Fact] + public void Select_using_named_class_should_work() + { + var collection = CreateCollection(); + + var queryable = collection.AsQueryable() + .Select(x => new R(x.Id) { W = x.Id }); + + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $project : { v : '$_id', w : '$_id', _id : 0 } }"); + + var results = queryable.ToList(); + results.Select(r => r.V).Should().Equal("111111111111111111111111"); + results.Select(r => r.W).Should().Equal("111111111111111111111111"); + } + + private IMongoCollection CreateCollection() + { + var collection = GetCollection(); + + CreateCollection( + collection, + new C { Id = "111111111111111111111111" }); + + return collection; + } + + public class C + { + [BsonRepresentation(BsonType.ObjectId)] + public string Id { get; set; } + } + + public class R + { + public R(string v) + { + V = v; + } + + [BsonElement("v")] public string V { get; set; } + [BsonElement("w")] public string W { get; set; } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4304Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4304Tests.cs new file mode 100644 index 00000000000..ee060089e9a --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4304Tests.cs @@ -0,0 +1,97 @@ +/* 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.Linq; +using FluentAssertions; +using MongoDB.Driver.Linq; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira +{ + public class CSharp4304Tests : Linq3IntegrationTest + { + [Fact] + public void Example_should_work() + { + var collection = GetCollection(); + var database = collection.Database; + + var queryable = GetChildrenQueryable(database, "Child"); + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $project : { _outer : '$$ROOT', _id : 0 } }", + "{ $lookup : { from : 'Child', localField : '_outer.ChildId', foreignField : '_id', as : '_inner' } }", + "{ $unwind : '$_inner' }", + "{ $project : { _v : '$_inner', _id : 0 } }"); + + CreateParentCollection(); + CreateChildCollection(); + var results = queryable.ToList(); + results.OrderBy(r => r.Id).Select(r => r.Id).Should().Equal("11", "22"); + } + + private IMongoQueryable GetChildrenQueryable(IMongoDatabase db, string childCollectionName) where TChild : IEntity + { + var parentCollection = db.GetCollection("Parent"); + var childCollection = db.GetCollection(childCollectionName).AsQueryable(); + + return parentCollection + .AsQueryable() + .Join( + childCollection, + p => p.ChildId, + c => c.Id, + (_, c) => c); + } + + private void CreateParentCollection() + { + var parentCollection = GetCollection("Parent"); + + CreateCollection( + parentCollection, + new Parent { Id = "1", ChildId = "11" }, + new Parent { Id = "2", ChildId = "22" }); + } + + private void CreateChildCollection() + { + var childCollection = GetCollection("Child"); + + CreateCollection( + childCollection, + new Child { Id = "11" }, + new Child { Id = "22" }); + } + + public interface IEntity + { + string Id { get; set; } + } + + public class Parent : IEntity + { + public string Id { get; set; } + public string ChildId { get; set; } + } + + public class Child : IEntity + { + public string Id { get; set; } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4316Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4316Tests.cs new file mode 100644 index 00000000000..964d5d0342e --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4316Tests.cs @@ -0,0 +1,87 @@ +/* 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.Linq; +using FluentAssertions; +using MongoDB.Driver.Linq; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira +{ + public class CSharp4316Tests : Linq3IntegrationTest + { + [Fact] + public void Value_and_HasValue_should_work_when_properties_on_Nullable_type() + { + var collection = CreateCollection(); + var matchStage = "{ $match : { 'ActualNullable' : { $ne : null } } }"; + var projectStage = "{ $project : { Id : '$_id', Value : '$ActualNullable', HasValue : { $ne : ['$ActualNullable', null] }, _id : 0 } }"; + + var queryable = collection.AsQueryable() + .Where(x => x.ActualNullable.HasValue) + .Select(x => new { x.Id, x.ActualNullable.Value, x.ActualNullable.HasValue }); + + var stages = Translate(collection, queryable); + AssertStages(stages, matchStage, projectStage); + + var results = queryable.ToList(); + results.Select(r => r.Id).Should().Equal(2); + } + + [Fact] + public void Value_and_HasValue_should_work_when_properties_not_on_Nullable_type() + { + var collection = CreateCollection(); + var matchStage = "{ $match : { 'OnlyLooksLikeNullable.HasValue' : true } }"; + var projectStage = "{ $project : { Id : '$_id', Value : '$OnlyLooksLikeNullable.Value', HasValue : '$OnlyLooksLikeNullable.HasValue', _id : 0 } }"; + + var queryable = collection + .AsQueryable() + .Where(x => x.OnlyLooksLikeNullable.HasValue) + .Select(x => new { x.Id, Value = x.OnlyLooksLikeNullable.Value, HasValue = x.OnlyLooksLikeNullable.HasValue }); + + var stages = Translate(collection, queryable); + AssertStages(stages, matchStage, projectStage); + + var results = queryable.ToList(); + results.Select(r => r.Id).Should().Equal(1); + } + + private IMongoCollection CreateCollection() + { + var collection = GetCollection("C"); + + CreateCollection( + collection, + new C { Id = 1, OnlyLooksLikeNullable = new OnlyLooksLikeNullable { Value = "SomeValue", HasValue = true }, ActualNullable = null }, + new C { Id = 2, OnlyLooksLikeNullable = new OnlyLooksLikeNullable { Value = null, HasValue = false }, ActualNullable = true }); + + return collection; + } + + private class C + { + public int Id { get; set; } + public OnlyLooksLikeNullable OnlyLooksLikeNullable { get; set; } + public bool? ActualNullable { get; set; } + } + + private class OnlyLooksLikeNullable + { + public string Value { get; set; } + public bool HasValue { get; set; } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4317Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4317Tests.cs new file mode 100644 index 00000000000..fd6297b803d --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4317Tests.cs @@ -0,0 +1,68 @@ +/* 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.Collections.Generic; +using System.Linq; +using FluentAssertions; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Bson.Serialization.Options; +using MongoDB.Driver.Linq; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira +{ + public class CSharp4317Tests : Linq3IntegrationTest + { + [Fact] + public void Projection_of_ArrayOfDocuments_dictionary_keys_and_values_should_work() + { + var collection = CreateCollection(); + var projectStage = "{ $project : { Keys : '$Data.k', Values : '$Data.v', _id : 0 } }"; + + var queryable = collection + .AsQueryable() + .Select(x => new { + Keys = x.Data.Select(y => y.Key), + Values = x.Data.Select(y => y.Value) + }); + + var stages = Translate(collection, queryable); + AssertStages(stages, projectStage); + + var result = queryable.First(); + result.Keys.Should().Equal(1, 2); + result.Values.Should().Equal("v1", "v2"); + } + + private IMongoCollection CreateCollection() + { + var collection = GetCollection("C"); + + CreateCollection( + collection, + new C { Id = 1, Data = new Dictionary { { 1, "v1" }, { 2, "v2" } } }); + + return collection; + } + + private class C + { + public int Id { get; set; } + + [BsonDictionaryOptions(DictionaryRepresentation.ArrayOfDocuments)] + public Dictionary Data { get; set; } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4328Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4328Tests.cs new file mode 100644 index 00000000000..d8b2281b896 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4328Tests.cs @@ -0,0 +1,122 @@ +/* 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.Driver.Linq; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira +{ + public class CSharp4328Tests : Linq3IntegrationTest + { + [Theory] + [InlineData(null)] + [InlineData("USA")] + public void Filter_using_First_should_work(string country) + { + var collection = CreateCollection(); + var startTargetDeliveryDate = new DateTime(2022, 1, 1, 0, 0, 0, DateTimeKind.Utc); + var endTargetDeliveryDate = new DateTime(2022, 12, 31, 0, 0, 0, DateTimeKind.Utc); + + var filterBuilder = Builders.Filter; + var statusFilter = filterBuilder.Where(x => x.SubscriptionStatus == SubscriptionStatus.Active); + var dateFilter = filterBuilder.Where( + x => + x.UpcomingOrders.First().NextTargetDeliveryDate >= startTargetDeliveryDate && + x.UpcomingOrders.First().NextTargetDeliveryDate <= endTargetDeliveryDate); + var filter = filterBuilder.And(statusFilter, dateFilter); + if (!string.IsNullOrEmpty(country)) + { + filter = filterBuilder.And(filter, filterBuilder.Where(x => x.ShippingAddress.CountryCode == country)); + } + + var renderedFilter = Translate(collection, filter); + var expectedFilter = BsonDocument.Parse( + @" + { + SubscriptionStatus : 1, + 'UpcomingOrders.0.NextTargetDeliveryDate' : { + $gte : ISODate('2022-01-01T00:00:00Z'), + $lte : ISODate('2022-12-31T00:00:00Z') + } + }"); + if (!string.IsNullOrEmpty(country)) + { + expectedFilter["ShippingAddress.CountryCode"] = country; + } + renderedFilter.Should().Be(expectedFilter); + + var result = collection.Distinct(x => x.CustomerId, filter).ToList(); + result.Should().Equal(2); + } + + private IMongoCollection CreateCollection() + { + var collection = GetCollection(); + + var documents = new SubscriptionRepositorySubscriptionModel[] + { + new SubscriptionRepositorySubscriptionModel + { + Id = 1, + CustomerId = 1, + SubscriptionStatus = SubscriptionStatus.Inactive, + UpcomingOrders = new Order[0], + ShippingAddress = null + }, + new SubscriptionRepositorySubscriptionModel + { + Id = 2, + CustomerId = 2, + SubscriptionStatus = SubscriptionStatus.Active, + UpcomingOrders = new Order[] { new Order { NextTargetDeliveryDate = new DateTime(2022, 12, 1, 0, 0, 0, DateTimeKind.Utc) } }, + ShippingAddress = new Address { CountryCode = "USA" } + } + + }; + CreateCollection(collection, documents); + + return collection; + } + + private class SubscriptionRepositorySubscriptionModel + { + public int Id { get; set; } + public int CustomerId { get; set; } + public SubscriptionStatus SubscriptionStatus { get; set; } + public Order[] UpcomingOrders { get; set; } + public Address ShippingAddress { get; set; } + } + + private class Order + { + public DateTime NextTargetDeliveryDate { get; set; } + } + + private class Address + { + public string CountryCode { get; set; } + } + + private enum SubscriptionStatus + { + Inactive = 0, + Active = 1 + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4332Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4332Tests.cs new file mode 100644 index 00000000000..3bd5f80cf8d --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4332Tests.cs @@ -0,0 +1,160 @@ +/* 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. +*/ + +#if NET6_0_OR_GREATER // because tests use readonly record struct +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using FluentAssertions; +using MongoDB.Bson; +using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Bson.Serialization.Serializers; +using MongoDB.Driver.Linq; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira +{ + public class CSharp4332Tests : Linq3IntegrationTest + { + private static readonly (Expression> Predicate, string ExpectedFilter)[] __testCases; + + static CSharp4332Tests() + { + var guid = Guid.Parse("0102030405060708090a0b0c0d0e0f10"); + var invoiceId = new InvoiceId(guid); + var guidNullable = (Guid?)guid; + var invoiceIdNullable = (InvoiceId?)invoiceId; + + __testCases = new (Expression> Predicate, string ExpectedFilter)[] + { + (c => c.Guid == guid, "{ Guid : '01020304-0506-0708-090a-0b0c0d0e0f10' }"), + (c => c.GuidNullable == guid, "{ GuidNullable : '01020304-0506-0708-090a-0b0c0d0e0f10' }"), + (c => c.Guid == invoiceId, "{ Guid : '01020304-0506-0708-090a-0b0c0d0e0f10' }"), + (c => c.GuidNullable == invoiceId, "{ GuidNullable : '01020304-0506-0708-090a-0b0c0d0e0f10' }"), + (c => c.InvoiceId == invoiceId, "{ InvoiceId : '01020304-0506-0708-090a-0b0c0d0e0f10' }"), + (c => c.InvoiceIdNullable == invoiceId, "{ InvoiceIdNullable : '01020304-0506-0708-090a-0b0c0d0e0f10' }"), + + (c => c.Guid == guidNullable, "{ Guid : '01020304-0506-0708-090a-0b0c0d0e0f10' }"), + (c => c.GuidNullable == guidNullable, "{ GuidNullable : '01020304-0506-0708-090a-0b0c0d0e0f10' }"), + (c => c.Guid == invoiceIdNullable, "{ Guid : '01020304-0506-0708-090a-0b0c0d0e0f10' }"), + (c => c.GuidNullable == invoiceIdNullable, "{ GuidNullable : '01020304-0506-0708-090a-0b0c0d0e0f10' }"), + (c => c.InvoiceId == invoiceIdNullable, "{ InvoiceId : '01020304-0506-0708-090a-0b0c0d0e0f10' }"), + (c => c.InvoiceIdNullable == invoiceIdNullable, "{ InvoiceIdNullable : '01020304-0506-0708-090a-0b0c0d0e0f10' }"), + + (c => c.InvoiceId == new InvoiceId(guidNullable.Value), "{ InvoiceId : '01020304-0506-0708-090a-0b0c0d0e0f10' }"), + (c => c.InvoiceIdNullable == (guidNullable.HasValue ? new InvoiceId(guidNullable.Value) : null), "{ InvoiceIdNullable : '01020304-0506-0708-090a-0b0c0d0e0f10' }"), + (c => c.InvoiceId == new InvoiceId(guid), "{ InvoiceId : '01020304-0506-0708-090a-0b0c0d0e0f10' }"), + (c => c.InvoiceIdNullable == new InvoiceId(guid), "{ InvoiceIdNullable : '01020304-0506-0708-090a-0b0c0d0e0f10' }"), + }; + } + + [Theory] + [MemberData(nameof(GetTestCases))] + public void Where_expression_should_work(string predicateAsString, string expectedFilter, int i) + { + var collection = CreateCollection(); + var predicate = __testCases[i].Predicate; + + var queryable = collection.AsQueryable().Where(predicate); + + var stages = Translate(collection, queryable); + var filter = stages.Single()["$match"]; + filter.Should().Be(expectedFilter); + + var results = queryable.ToList(); + results.Single().Id.Should().Be(1); + } + + public static IEnumerable GetTestCases() + { + for (var i = 0; i < __testCases.Length; i++) + { + var predicateAsString = __testCases[0].Predicate.ToString(); + var expectedFilter = __testCases[i].ExpectedFilter; + yield return new object[] { predicateAsString, expectedFilter, i }; + } + } + + private IMongoCollection CreateCollection() + { + var collection = GetCollection("C") ; + + var guid = Guid.Parse("0102030405060708090a0b0c0d0e0f10"); + var invoiceId = new InvoiceId(guid); + var guidNullable = (Guid?)guid; + var invoiceIdNullable = (InvoiceId?)invoiceId; + + CreateCollection( + collection, + new Document + { + Id = 1, + Guid = guid, + GuidNullable = guidNullable, + InvoiceId = invoiceId, + InvoiceIdNullable = invoiceIdNullable + }); + + return collection; + } + + public class Document + { + public int Id { get; set; } + + [BsonRepresentation(BsonType.String)] + public Guid Guid { get; set; } + + [BsonRepresentation(BsonType.String)] + public Guid? GuidNullable { get; set; } + + public InvoiceId InvoiceId { get; set; } + public InvoiceId? InvoiceIdNullable { get; set; } + } + + [BsonSerializer(typeof(InvoiceIdSerializer))] + public readonly record struct InvoiceId(Guid Value) + { + public static implicit operator Guid(InvoiceId s) => s.Value; + } + + public class InvoiceIdSerializer : SerializerBase + { + public override InvoiceId Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) + { + if (context.Reader.CurrentBsonType == BsonType.Null) + { + context.Reader.ReadNull(); + return default; + } + + if (Guid.TryParse(context.Reader.ReadString(), out var guid)) + { + return new InvoiceId(guid); + } + + return new InvoiceId(default); + } + + public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, InvoiceId value) + { + context.Writer.WriteString(value.Value.ToString()); + } + } + } +} +#endif diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4337Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4337Tests.cs new file mode 100644 index 00000000000..c082398dd34 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4337Tests.cs @@ -0,0 +1,172 @@ +/* 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.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver.Linq; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira +{ + public class CSharp4337Tests : Linq3IntegrationTest + { + private static (Expression>> Projection, string ExpectedStage, bool[] ExpectedResults)[] __predicate_should_use_correct_representation_test_cases = new (Expression>> Projection, string ExpectedStage, bool[] ExpectedResults)[] + { + (d => new R { N = d.Id, V = d.I1 == E.E1 ? true : false }, "{ $project : { N : '$_id', V : { $cond : { if : { $eq : ['$I1', 1] }, then : true, else : false } }, _id : 0 } }", new[] { true, false }), + (d => new R { N = d.Id, V = d.S1 == E.E1 ? true : false }, "{ $project : { N : '$_id', V : { $cond : { if : { $eq : ['$S1', 'E1'] }, then : true, else : false } }, _id : 0 } }", new[] { true, false }), + (d => new R { N = d.Id, V = E.E1 == d.I1 ? true : false }, "{ $project : { N : '$_id', V : { $cond : { if : { $eq : [1, '$I1'] }, then : true, else : false } }, _id : 0 } }", new[] { true, false }), + (d => new R { N = d.Id, V = E.E1 == d.S1 ? true : false }, "{ $project : { N : '$_id', V : { $cond : { if : { $eq : ['E1', '$S1'] }, then : true, else : false } }, _id : 0 } }", new[] { true, false }) + }; + + public static IEnumerable Predicate_should_use_correct_representation_member_data() + { + var testCases = __predicate_should_use_correct_representation_test_cases; + for (var i = 0; i < testCases.Length; i++) + { + yield return new object[] { i, testCases[i].Projection.ToString(), testCases[i].ExpectedStage, testCases[i].ExpectedResults }; + } + } + + [Theory] + [MemberData(nameof(Predicate_should_use_correct_representation_member_data))] + public void Predicate_should_use_correct_representation(int i, string projectionAsString, string expectedStage, bool[] expectedResults) + { + var collection = CreateCollection(); + var projection = __predicate_should_use_correct_representation_test_cases[i].Projection; + + var aggregate = collection.Aggregate() + .Project(projection); + + var stages = Translate(collection, aggregate); + AssertStages(stages, expectedStage); + + var results = aggregate.ToList(); + results.OrderBy(r => r.N).Select(r => r.V).Should().Equal(expectedResults); + } + + private static (Expression>> Projection, string ExpectedStage, E[] ExpectedResults)[] __result_should_use_correct_representation_test_cases = new (Expression>> Projection, string ExpectedStage, E[] ExpectedResults)[] + { + (d => new R { N = d.Id, V = true ? E.E1 : E.E2 }, "{ $project : { N : '$_id', V : { $literal : 1 }, _id : 0 } }", new[] { E.E1, E.E1 }), + (d => new R { N = d.Id, V = d.I1 == E.E1 ? E.E1 : E.E2 }, "{ $project : { N : '$_id', V : { $cond : { if : { $eq : ['$I1', 1] }, then : 1, else : 2 } }, _id : 0 } }", new[] { E.E1, E.E2 }), + (d => new R { N = d.Id, V = d.I1 == E.E1 ? d.I1 : E.E2 }, "{ $project : { N : '$_id', V : { $cond : { if : { $eq : ['$I1', 1] }, then : '$I1', else : 2 } }, _id : 0 } }", new[] { E.E1, E.E2 }), + (d => new R { N = d.Id, V = d.I1 == E.E1 ? E.E1 : d.I2 }, "{ $project : { N : '$_id', V : { $cond : { if : { $eq : ['$I1', 1] }, then : 1, else : '$I2' } }, _id : 0 } }", new[] { E.E1, E.E2 }), + (d => new R { N = d.Id, V = d.I1 == E.E1 ? d.I1 : d.I2 }, "{ $project : { N : '$_id', V : { $cond : { if : { $eq : ['$I1', 1] }, then : '$I1', else : '$I2' } }, _id : 0 } }", new[] { E.E1, E.E2 }), + (d => new R { N = d.Id, V = d.I1 == E.E1 ? d.S1 : E.E2 }, "{ $project : { N : '$_id', V : { $cond : { if : { $eq : ['$I1', 1] }, then : '$S1', else : 'E2' } }, _id : 0 } }", new[] { E.E1, E.E2 }), + (d => new R { N = d.Id, V = d.I1 == E.E1 ? E.E1 : d.S2 }, "{ $project : { N : '$_id', V : { $cond : { if : { $eq : ['$I1', 1] }, then : 'E1', else : '$S2' } }, _id : 0 } }", new[] { E.E1, E.E2 }), + (d => new R { N = d.Id, V = d.I1 == E.E1 ? d.S1 : d.S2 }, "{ $project : { N : '$_id', V : { $cond : { if : { $eq : ['$I1', 1] }, then : '$S1', else : '$S2' } }, _id : 0 } }", new[] { E.E1, E.E2 }), + (d => new R { N = d.Id, V = d.S1 == E.E1 ? E.E1 : E.E2 }, "{ $project : { N : '$_id', V : { $cond : { if : { $eq : ['$S1', 'E1'] }, then : 'E1', else : 'E2' } }, _id : 0 } }", new[] { E.E1, E.E2 }), + (d => new R { N = d.Id, V = d.S1 == E.E1 ? d.I1 : E.E2 }, "{ $project : { N : '$_id', V : { $cond : { if : { $eq : ['$S1', 'E1'] }, then : '$I1', else : 2 } }, _id : 0 } }", new[] { E.E1, E.E2 }), + (d => new R { N = d.Id, V = d.S1 == E.E1 ? E.E1 : d.I2 }, "{ $project : { N : '$_id', V : { $cond : { if : { $eq : ['$S1', 'E1'] }, then : 1, else : '$I2' } }, _id : 0 } }", new[] { E.E1, E.E2 }), + (d => new R { N = d.Id, V = d.S1 == E.E1 ? d.I1 : d.I2 }, "{ $project : { N : '$_id', V : { $cond : { if : { $eq : ['$S1', 'E1'] }, then : '$I1', else : '$I2' } }, _id : 0 } }", new[] { E.E1, E.E2 }), + (d => new R { N = d.Id, V = d.S1 == E.E1 ? d.S1 : E.E2 }, "{ $project : { N : '$_id', V : { $cond : { if : { $eq : ['$S1', 'E1'] }, then : '$S1', else : 'E2' } }, _id : 0 } }", new[] { E.E1, E.E2 }), + (d => new R { N = d.Id, V = d.S1 == E.E1 ? E.E1 : d.S2 }, "{ $project : { N : '$_id', V : { $cond : { if : { $eq : ['$S1', 'E1'] }, then : 'E1', else : '$S2' } }, _id : 0 } }", new[] { E.E1, E.E2 }), + (d => new R { N = d.Id, V = d.S1 == E.E1 ? d.S1 : d.S2 }, "{ $project : { N : '$_id', V : { $cond : { if : { $eq : ['$S1', 'E1'] }, then : '$S1', else : '$S2' } }, _id : 0 } }", new[] { E.E1, E.E2 }), + }; + + public static IEnumerable Result_should_use_correct_representation_member_data() + { + var testCases = __result_should_use_correct_representation_test_cases; + for (var i = 0; i < testCases.Length; i++) + { + yield return new object[] { i, testCases[i].Projection.ToString(), testCases[i].ExpectedStage, testCases[i].ExpectedResults }; + } + } + + [Theory] + [MemberData(nameof(Result_should_use_correct_representation_member_data))] + public void Result_should_use_correct_representation(int i, string projectionAsString, string expectedStage, E[] expectedResults) + { + var collection = CreateCollection(); + var projection = __result_should_use_correct_representation_test_cases[i].Projection; + + var aggregate = collection.Aggregate() + .Project(projection); + + var stages = Translate(collection, aggregate); + AssertStages(stages, expectedStage); + + var results = aggregate.ToList(); + results.OrderBy(r => r.N).Select(r => r.V).Should().Equal(expectedResults); + } + + private static Expression>>[] __result_with_mixed_representations_should_throw_test_cases = new Expression>>[] + { + d => new R { N = d.Id, V = d.I1 == E.E1 ? d.I1 : d.S2 }, + d => new R { N = d.Id, V = d.I1 == E.E1 ? d.S1 : d.I2 } + }; + + public static IEnumerable Result_with_mixed_representations_should_throw_member_data() + { + var testCases = __result_with_mixed_representations_should_throw_test_cases; + for (var i = 0; i < testCases.Length; i++) + { + yield return new object[] { i, testCases[i].ToString() }; + } + } + + [Theory] + [MemberData(nameof(Result_with_mixed_representations_should_throw_member_data))] + public void Result_with_mixed_representations_should_throw(int i, string projectionAsString) + { + var collection = CreateCollection(); + var projection = __result_with_mixed_representations_should_throw_test_cases[i]; + + var aggregate = collection.Aggregate() + .Project(projection); + + List stages; + var exception = Record.Exception(() => stages = Translate(collection, aggregate)); + + var notSupportedException = exception.Should().BeOfType().Subject; + notSupportedException.Message.Should().Contain("because IfTrue and IfFalse expressions have different serializers"); + } + + private IMongoCollection CreateCollection() + { + var collection = GetCollection(); + + CreateCollection( + collection, + new C { Id = 1, I1 = E.E1, I2 = E.E1, S1 = E.E1, S2 = E.E1 }, + new C { Id = 2, I1 = E.E2, I2 = E.E2, S1 = E.E2, S2 = E.E2 }); + + return collection; + } + + public class C + { + public int Id { get; set; } + + public E I1 { get; set; } + public E I2 { get; set; } + + [BsonRepresentation(BsonType.String)] public E S1 { get; set; } + [BsonRepresentation(BsonType.String)] public E S2 { get; set; } + } + + public class R + { + public int N { get; set; } + public TValue V { get; set; } + } + + public enum E { E1 = 1, E2 } + } +} diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4339Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4339Tests.cs new file mode 100644 index 00000000000..1d9bf9e7617 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4339Tests.cs @@ -0,0 +1,166 @@ +/* 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.IO; +using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Serializers; +using MongoDB.Driver.Core.Misc; +using MongoDB.Driver.Core.TestHelpers.XunitExtensions; +using MongoDB.Driver.Linq; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira +{ + public class CSharp4339Tests : Linq3IntegrationTest + { + [Fact] + public void Documents_should_work() + { + RequireServer.Check().Supports(Feature.DocumentsStage); + var database = GetDatabase(); + var documents = new[] { new C { X = 1 }, new C { X = 2 }, new C { X = 3 } }; + + var queryable = database + .AsQueryable() + .Documents(documents); + + var stages = Translate(database, queryable); + AssertStages( + stages, + "{ $documents : [{ X : 1 }, { X : 2 }, { X : 3 }] }"); + + var results = queryable.ToList(); + results.Select(x => x.X).Should().Equal(1, 2, 3); + } + + [Fact] + public void Documents_should_throw_when_source_is_null() + { + var source = (IMongoQueryable)null; + var documents = new C[0]; + + var exception = Record.Exception(() => source.Documents(documents)); + + var argumentNullException = exception.Should().BeOfType().Subject; + argumentNullException.ParamName.Should().Be("source"); + } + + [Fact] + public void Documents_should_throw_when_documents_is_null() + { + var database = GetDatabase(); + var source = database.AsQueryable(); + var documents = (C[])null; + + var exception = Record.Exception(() => source.Documents(documents)); + + var argumentNullException = exception.Should().BeOfType().Subject; + argumentNullException.ParamName.Should().Be("documents"); + } + + [Fact] + public void Documents_with_documentSerializer_should_work() + { + RequireServer.Check().Supports(Feature.DocumentsStage); + var database = GetDatabase(); + var documents = new[] { new C { X = 1 }, new C { X = 2 }, new C { X = 3 } }; + var documentSerializer = new CSerializer(); + + var queryable = database + .AsQueryable() + .Documents(documents, documentSerializer); + + var stages = Translate(database, queryable); + AssertStages( + stages, + "{ $documents : [{ X : '1' }, { X : '2' }, { X : '3' }] }"); + + var results = queryable.ToList(); + results.Select(x => x.X).Should().Equal(1, 2, 3); + } + + [Fact] + public void Documents_with_documentSerializer_should_throw_when_source_is_null() + { + var source = (IMongoQueryable)null; + var documents = new C[0]; + var documentSerializer = BsonSerializer.LookupSerializer(); + + var exception = Record.Exception(() => source.Documents(documents, documentSerializer)); + + var argumentNullException = exception.Should().BeOfType().Subject; + argumentNullException.ParamName.Should().Be("source"); + } + + [Fact] + public void Documents_with_documentSerializer_should_throw_when_documents_is_null() + { + var database = GetDatabase(); + var source = database.AsQueryable(); + var documents = (C[])null; + var documentSerializer = BsonSerializer.LookupSerializer(); + + var exception = Record.Exception(() => source.Documents(documents, documentSerializer)); + + var argumentNullException = exception.Should().BeOfType().Subject; + argumentNullException.ParamName.Should().Be("documents"); + } + + [Fact] + public void Documents_with_documentSerializer_should_throw_when_documentSerializer_is_null() + { + var database = GetDatabase(); + var source = database.AsQueryable(); + var documents = new C[0]; + var documentSerializer = (IBsonSerializer)null; + + var exception = Record.Exception(() => source.Documents(documents, documentSerializer)); + + var argumentNullException = exception.Should().BeOfType().Subject; + argumentNullException.ParamName.Should().Be("documentSerializer"); + } + + public class C + { + public int X { get; set; } + } + + public class CSerializer : ClassSerializerBase + { + public override C Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) + { + var reader = context.Reader; + reader.ReadStartDocument(); + reader.ReadName("X"); + var x = int.Parse(reader.ReadString()); + reader.ReadEndDocument(); + + return new C { X = x }; + } + + public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, C value) + { + var writer = context.Writer; + writer.WriteStartDocument(); + writer.WriteName("X"); + writer.WriteString(value.X.ToString()); + writer.WriteEndDocument(); + } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4368Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4368Tests.cs new file mode 100644 index 00000000000..065a2e9f271 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4368Tests.cs @@ -0,0 +1,196 @@ +/* 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.Expressions; +using System.Text.RegularExpressions; +using FluentAssertions; +using MongoDB.Bson; +using MongoDB.Bson.IO; +using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Serializers; +using MongoDB.Bson.TestHelpers; +using MongoDB.Driver.Linq; +using MongoDB.Driver.Linq.Linq3Implementation.Serializers; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira +{ + public class CSharp4368Tests : Linq3IntegrationTest + { + private static readonly IBsonSerializer __guidSerializerWithStandardRepresentation; + private static readonly IBsonSerializer __nullableGuidSerializerWithStandardRepresentation; + + static CSharp4368Tests() + { + __guidSerializerWithStandardRepresentation = new GuidSerializer(GuidRepresentation.Standard); + __nullableGuidSerializerWithStandardRepresentation = new NullableSerializer(__guidSerializerWithStandardRepresentation); + + var guidClassMap = BsonClassMap.RegisterClassMap>( + cm => + { + cm.MapMember(x => x.V).SetSerializer(__guidSerializerWithStandardRepresentation); + }); + + var nullableGuidClassMap = BsonClassMap.RegisterClassMap>( + cm => + { + cm.MapMember(x => x.V).SetSerializer(__nullableGuidSerializerWithStandardRepresentation); + }); + } + + public class TestCase + { + public Type ValueType { get; set; } + public string ValueAsJson { get; set; } + public LambdaExpression Projection { get; set; } + } + + public static TestCase CreateTestCase( + string valueAsJson, + Expression, BsonValue>> projection) + { + return new TestCase { ValueType = typeof(TValue), ValueAsJson = valueAsJson, Projection = projection }; + } + + public static TestCase[] __testCases = new TestCase[] + { + CreateTestCase("{ X : 1 }", x => (BsonValue)(object)x.V), + CreateTestCase("null", x => (BsonValue)(object)x.V), + CreateTestCase("BinData(0, 'AQID')'", x => (BsonValue)x.V), + CreateTestCase("null", x => (BsonValue)x.V), + CreateTestCase("true", x => (BsonValue)x.V), + CreateTestCase("true", x => (BsonValue)x.V), + CreateTestCase("null", x => (BsonValue)x.V), + CreateTestCase("ISODate('2021-01-02T03:04:05.123')", x => (BsonValue)x.V), + CreateTestCase("ISODate('2021-01-02T03:04:05.123')", x => (BsonValue)x.V), + CreateTestCase("null", x => (BsonValue)x.V), + CreateTestCase("'1'", x => (BsonValue)x.V), + CreateTestCase("'1'", x => (BsonValue)x.V), + CreateTestCase("null", x => (BsonValue)x.V), + CreateTestCase("'1'", x => (BsonValue)x.V), + CreateTestCase("'1'", x => (BsonValue)x.V), + CreateTestCase("null", x => (BsonValue)x.V), + CreateTestCase("{ $numberDouble : '1.0' }", x => (BsonValue)x.V), + CreateTestCase("{ $numberDouble : '1.0' }", x => (BsonValue)x.V), + CreateTestCase("null", x => (BsonValue)x.V), +#pragma warning disable CS0618 // Type or member is obsolete + CreateTestCase("UUID('01020304-0506-0708-090a-0b0c0d0e0f10')", x => (BsonValue)x.V), + CreateTestCase("UUID('01020304-0506-0708-090a-0b0c0d0e0f10')", x => (BsonValue)x.V), + CreateTestCase("null", x => (BsonValue)x.V), +#pragma warning restore CS0618 // Type or member is obsolete + CreateTestCase("UUID('01020304-0506-0708-090a-0b0c0d0e0f10')", x => (BsonValue)(object)x.V), + CreateTestCase("UUID('01020304-0506-0708-090a-0b0c0d0e0f10')", x => (BsonValue)(object)x.V), + CreateTestCase("null", x => (BsonValue)(object)x.V), + CreateTestCase("1", x => (BsonValue)x.V), + CreateTestCase("1", x => (BsonValue)x.V), + CreateTestCase("null", x => (BsonValue)x.V), + CreateTestCase("{ $numberLong : '1' }", x => (BsonValue)x.V), + CreateTestCase("{ $numberLong : '1' }", x => (BsonValue)x.V), + CreateTestCase("null", x => (BsonValue)x.V), + CreateTestCase("ObjectId('0102030405060708090a0b0c')", x => (BsonValue)x.V), + CreateTestCase("ObjectId('0102030405060708090a0b0c')", x => (BsonValue)x.V), + CreateTestCase("null", x => (BsonValue)x.V), + CreateTestCase("/abc/i", x => (BsonValue)x.V), + CreateTestCase("null", x => (BsonValue)x.V), + CreateTestCase("'abc'", x => (BsonValue)x.V), + CreateTestCase("null", x => (BsonValue)x.V) + }; + + public static IEnumerable Convert_to_BsonValue_from_TValue_should_work_MemberData() + { + for (var i = 0; i < __testCases.Length; i++) + { + var valueType = __testCases[i].ValueType; + var valueAsJson = __testCases[i].ValueAsJson; + var projectionAsString = __testCases[i].Projection.ToString(); + yield return new object[] { valueType, i, valueAsJson, projectionAsString }; + } + } + + [Theory] + [MemberData(nameof(Convert_to_BsonValue_from_TValue_should_work_MemberData))] + [ResetGuidModeAfterTest] + public void Convert_to_BsonValue_from_TValue_should_work_invoker(Type valueType, int i, string valueAsJson, string projectionAsString) + { + GuidMode.Set(GuidRepresentationMode.V3); + + var testMethodInfo = this.GetType().GetMethod(nameof(Convert_to_BsonValue_from_TValue_should_work)); + var testMethod = testMethodInfo.MakeGenericMethod(valueType); + testMethod.Invoke(this, new object[] { i, valueAsJson, projectionAsString }); + } + + public void Convert_to_BsonValue_from_TValue_should_work(int i, string valueAsJson, string projectionAsString) + { + var serializer = typeof(TValue) switch + { + var type when type == typeof(Guid) => (IBsonSerializer)__guidSerializerWithStandardRepresentation, + var type when type == typeof(Guid?) => (IBsonSerializer)__nullableGuidSerializerWithStandardRepresentation, + _ => BsonSerializer.LookupSerializer() + }; + var value = Deserialize(serializer, valueAsJson); + var projection = (Expression, BsonValue>>)__testCases[i].Projection; + var expectedResult = BsonSerializer.Deserialize(valueAsJson); + + var collection = CreateCollection(value); + + var queryable = collection + .AsQueryable() + .Select(projection); + + var stages = Translate(collection, queryable, out var outputSerializer); + AssertStages( + stages, + "{ $project : { _v : '$V', _id : 0 } }"); + + var wrappedValueSerializer = outputSerializer.Should().BeOfType>().Subject; + wrappedValueSerializer.ValueSerializer.Should().Be(BsonValueSerializer.Instance); + + var results = queryable.ToList(); + results.Should().Equal(expectedResult); + } + + private IMongoCollection> CreateCollection(TValue value) + { + var collection = GetCollection>(); + + CreateCollection( + collection, + new Document { V = value }); + + return collection; + } + + private TValue Deserialize(IBsonSerializer serializer, string json) + { + using (var reader = new JsonReader(json)) + { + var context = BsonDeserializationContext.CreateRoot(reader); + return serializer.Deserialize(context); + } + } + + public class Document + { + public TValue V { get; set; } + } + + public class Anything + { + public int X { get; set; } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4370.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4370.cs new file mode 100644 index 00000000000..afbcaaaf496 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4370.cs @@ -0,0 +1,62 @@ +/* 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.Linq; +using FluentAssertions; +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver.Linq; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira +{ + public class CSharp4370Tests : Linq3IntegrationTest + { + [Fact] + public void Where_with_Id_represented_as_ObjectId_should_work() + { + var collection = CreateCollection(); + + var queryable = collection.AsQueryable() + .Where(x => x.SomeId == "bbbbbbbbbbbbbbbbbbbbbbbb"); + + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $match : { SomeId : ObjectId('bbbbbbbbbbbbbbbbbbbbbbbb') } }"); + + var results = queryable.ToList(); + results.Select(r => r.Id).Should().Equal(2); + } + + private IMongoCollection CreateCollection() + { + var collection = GetCollection("products"); + var database = collection.Database; + + CreateCollection( + collection, + new C { Id = 1, SomeId = "aaaaaaaaaaaaaaaaaaaaaaaa" }, + new C { Id = 2, SomeId = "bbbbbbbbbbbbbbbbbbbbbbbb" }); + + return collection; + } + + public class C + { + public int Id { get; set; } + [BsonRepresentation(BsonType.ObjectId)] + public string SomeId { get; set; } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4391Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4391Tests.cs new file mode 100644 index 00000000000..fe095202435 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4391Tests.cs @@ -0,0 +1,168 @@ +/* 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.Linq; +using FluentAssertions; +using MongoDB.Bson.Serialization; +using MongoDB.Bson; +using Xunit; +using MongoDB.Bson.Serialization.Attributes; + +namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira +{ + public class CSharp4391Tests : Linq3IntegrationTest + { + [Fact] + public void Serializing_struct_with_constructor_should_work() + { + var s = new S(1, 2, 3); + + var result = s.ToJson(); + + result.Should().Be("{ \"_id\" : 1, \"X\" : 2, \"Y\" : 3 }"); + } + + [Fact] + public void Serializing_struct_with_partial_constructor_should_work() + { + var t = new T(1, 2) { Y = 3 }; + + var result = t.ToJson(); + + result.Should().Be("{ \"Y\" : 3, \"_id\" : 1, \"X\" : 2 }"); + } + + [Fact] + public void Serializing_struct_with_no_constructor_should_work() + { + var u = new U() { Id = 1, X = 2, Y = 3 }; + + var result = u.ToJson(); + + result.Should().Be("{ \"_id\" : 1, \"X\" : 2, \"Y\" : 3 }"); + } + + [Fact] + public void Deserializing_struct_with_constructor_should_work() + { + var json = "{ _id : 1, X : 2, Y : 3 }"; + + var result = BsonSerializer.Deserialize(json); + + result.Should().Be(new S(1, 2, 3)); + } + + [Fact] + public void Deserializing_struct_with_partial_constructor_should_fail() + { + var json = "{ _id : 1, X : 2, Y : 3 }"; + + var expection = Record.Exception(() => { _ = BsonSerializer.Deserialize(json); }); + + expection.Should().BeOfType(); + expection.Message.Should().Contain("cannot be deserialized unless all values can be passed to a constructor"); + } + + [Fact] + public void Deserializing_struct_with_no_constructor_should_fail() + { + var json = "{ _id : 1, X : 2, Y : 3 }"; + + var expection = Record.Exception(() => { _ = BsonSerializer.Deserialize(json); }); + + expection.Should().BeOfType(); + expection.Message.Should().Contain("cannot be deserialized without a constructor"); + } + + [Fact] + public void Queryable_with_struct_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable(); + + var stages = Translate(collection, queryable); + AssertStages(stages, new string[0]); + + var results = queryable.ToList(); + results.Should().Equal(new S(1, 2, 3), new S(2, 3, 4)); + } + + [Fact] + public void Queryable_with_projected_struct_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .Select(x => new S(x.Id, x.X + 1, x.Y + 1)); + + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $project : { Id : '$_id', X : { $add : ['$X', 1] }, Y : { $add : ['$Y', 1] }, _id : 0 } }"); + + var results = queryable.ToList(); + results.Should().Equal(new S(1, 3, 4), new S(2, 4, 5)); + } + + private IMongoCollection CreateCollection() + { + var collection = GetCollection("C"); + + CreateCollection( + collection, + new S(1, 2, 3), + new S(2, 3, 4)); + + return collection; + } + + private struct S + { + public S(int id, int x, int y) + { + Id = id; + X = x; + Y = y; + } + + public int Id { get; } + public int X { get; } + public int Y { get; } + } + + private struct T + { + [BsonConstructor] + public T(int id, int x) + { + Id = id; + X = x; + Y = 0; + } + + public int Id { get; } + [BsonElement("X")] public int X { get; } + public int Y { get; set; } + } + + private struct U + { + public int Id { get; set; } + public int X { get; set; } + public int Y { get; set; } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4401Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4401Tests.cs new file mode 100644 index 00000000000..0e182241518 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4401Tests.cs @@ -0,0 +1,287 @@ +/* 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.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver.Linq; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira +{ + public class CSharp4401Tests : Linq3IntegrationTest + { + private static readonly WhereTestCase[] __whereTestCases; + + private class WhereTestCase + { + public Expression> Predicate { get; set; } + public string PredicateAsString { get; set; } + public string ExpectedStage { get; set; } + public int[] ExpectedResults { get; set; } + } + + private static WhereTestCase CreateWhereTestCase( + Expression> predicate, + string expectedStage, + params int[] expectedResults) + { + var predicateAsString = predicate.ToString(); + return new WhereTestCase { Predicate = predicate, PredicateAsString = predicateAsString, ExpectedStage = expectedStage, ExpectedResults = expectedResults }; + } + + static CSharp4401Tests() + { + __whereTestCases = new WhereTestCase[] + { + // test all comparison operators + CreateWhereTestCase(x => x.E == E.X, "{ $match : { E : 6 } }", 4), + CreateWhereTestCase(x => x.E != E.X, "{ $match : { E : { $ne : 6 } } }", 1, 2, 3, 5, 6), + CreateWhereTestCase(x => x.E >= E.X, "{ $match : { E : { $gte : 6 } } }", 4, 5, 6), + CreateWhereTestCase(x => x.E <= E.X, "{ $match : { E : { $lte : 6 } } }", 1, 2, 3, 4), + CreateWhereTestCase(x => x.E > E.X, "{ $match : { E : { $gt : 6 } } }", 5, 6), + CreateWhereTestCase(x => x.E < E.X, "{ $match : { E : { $lt : 6 } } }", 1, 2, 3), + + // test all combinations of field/constant and nullable/not nullable with == + CreateWhereTestCase(x => x.E == E.B, "{ $match : { E : 2 } }", 2), + CreateWhereTestCase(x => x.E == (E?)E.B, "{ $match : { E : 2 } }", 2), + CreateWhereTestCase(x => x.E == x.F, "{ $match : { $expr : { $eq : ['$E', '$F'] } } }", 1, 4), + CreateWhereTestCase(x => x.E == x.NF, "{ $match : { $expr : { $eq : ['$E', '$NF'] } } }", 1), + CreateWhereTestCase(x => x.NE == E.B, "{ $match : { NE : 2 } }", 2), + CreateWhereTestCase(x => x.NE == (E?)E.B, "{ $match : { NE : 2 } }", 2), + CreateWhereTestCase(x => x.NE == (E?)null, "{ $match : { NE : null } }", 5, 6), + CreateWhereTestCase(x => x.NE == null, "{ $match : { NE : null } }", 5, 6), + CreateWhereTestCase(x => x.NE == x.F, "{ $match : { $expr : { $eq : ['$NE', '$F'] } } }", 1, 4), + CreateWhereTestCase(x => x.NE == x.NF, "{ $match : { $expr : { $eq : ['$NE', '$NF'] } } }", 1, 6), + CreateWhereTestCase(x => E.A == x.F, "{ $match : { F : 1 } }", 1), + CreateWhereTestCase(x => E.A == x.NF, "{ $match : { NF : 1 } }", 1), + CreateWhereTestCase(x => (E?)E.A == x.F, "{ $match : { F : 1 } }", 1), + CreateWhereTestCase(x => (E?)E.A == x.NF, "{ $match : { NF : 1 } }", 1), + CreateWhereTestCase(x => (E?)null == x.NF, "{ $match : { NF : null } }", 4, 6), + CreateWhereTestCase(x => null == x.NF, "{ $match : { NF : null } }", 4, 6), + + // test all combinations of field/constant and nullable/not nullable != + CreateWhereTestCase(x => x.E != E.B, "{ $match : { E : { $ne : 2 } } }", 1, 3, 4, 5, 6), + CreateWhereTestCase(x => x.E != (E?)E.B, "{ $match : { E : { $ne : 2 } } }", 1, 3, 4, 5, 6), + CreateWhereTestCase(x => x.E != x.F, "{ $match : { $expr : { $ne : ['$E', '$F'] } } }", 2, 3, 5, 6), + CreateWhereTestCase(x => x.E != x.NF, "{ $match : { $expr : { $ne : ['$E', '$NF'] } } }", 2, 3, 4, 5, 6), + CreateWhereTestCase(x => x.NE != E.B, "{ $match : { NE : { $ne : 2 } } }", 1, 3, 4, 5, 6), + CreateWhereTestCase(x => x.NE != (E?)E.B, "{ $match : { NE : { $ne : 2 } } }", 1, 3, 4, 5, 6), + CreateWhereTestCase(x => x.NE != (E?)null, "{ $match : { NE : { $ne : null } } }", 1, 2, 3, 4), + CreateWhereTestCase(x => x.NE != null, "{ $match : { NE : { $ne : null } } }", 1, 2, 3, 4), + CreateWhereTestCase(x => x.NE != x.F, "{ $match : { $expr : { $ne : ['$NE', '$F'] } } }", 2, 3, 5, 6), + CreateWhereTestCase(x => x.NE != x.NF, "{ $match : { $expr : { $ne : ['$NE', '$NF'] } } }", 2, 3, 4, 5), + CreateWhereTestCase(x => E.A != x.F, "{ $match : { F : { $ne : 1 } } }", 2, 3, 4, 5, 6), + CreateWhereTestCase(x => E.A != x.NF, "{ $match : { NF : { $ne : 1 } } }", 2, 3, 4, 5, 6), + CreateWhereTestCase(x => (E?)E.A != x.F, "{ $match : { F : { $ne : 1 } } }", 2, 3, 4, 5, 6), + CreateWhereTestCase(x => (E?)E.A != x.NF, "{ $match : { NF : { $ne : 1 } } }", 2, 3, 4, 5, 6), + CreateWhereTestCase(x => (E?)null != x.NF, "{ $match : { NF : { $ne : null } } }", 1, 2, 3, 5), + CreateWhereTestCase(x => null != x.NF, "{ $match : { NF : { $ne : null } } }", 1, 2, 3, 5), + + // test all combinations of field/constant and nullable/not nullable with '+' (sometimes combined with '-' on other side) + CreateWhereTestCase(x => x.E + x.One == E.B, "{ $match : { $expr : { $eq : [{ $add : ['$E', '$One'] }, 2] } } }", 1), + CreateWhereTestCase(x => x.E + x.One == (E?)E.B, "{ $match : { $expr : { $eq : [{ $add : ['$E', '$One'] }, 2] } } }", 1), + CreateWhereTestCase(x => x.E + x.One == x.F, "{ $match : { $expr : { $eq : [{ $add : ['$E', '$One'] }, '$F'] } } }", 2), + CreateWhereTestCase(x => x.E + x.One == x.F - x.One, "{ $match : { $expr : { $eq : [{ $add : ['$E', '$One'] }, { $subtract : ['$F', '$One'] }] } } }", 5), + CreateWhereTestCase(x => x.E + x.One == x.NF, "{ $match : { $expr : { $eq : [{ $add : ['$E', '$One'] }, '$NF'] } } }", 2), + CreateWhereTestCase(x => x.E + x.One == x.NF - x.One, "{ $match : { $expr : { $eq : [{ $add : ['$E', '$One'] }, { $subtract : ['$NF', '$One'] }] } } }", 5), + CreateWhereTestCase(x => x.NE + x.One == E.B, "{ $match : { $expr : { $eq : [{ $add : ['$NE', '$One'] }, 2] } } }", 1), + CreateWhereTestCase(x => x.NE + x.One == (E?)E.B, "{ $match : { $expr : { $eq : [{ $add : ['$NE', '$One'] }, 2] } } }", 1), + CreateWhereTestCase(x => x.NE + x.One == (E?)null, "{ $match : { $expr : { $eq : [{ $add : ['$NE', '$One'] }, null] } } }", 5, 6), + CreateWhereTestCase(x => x.NE + x.One == null, "{ $match : { $expr : { $eq : [{ $add : ['$NE', '$One'] }, null] } } }", 5, 6), + CreateWhereTestCase(x => x.NE + x.One == x.F, "{ $match : { $expr : { $eq : [{ $add : ['$NE', '$One'] }, '$F'] } } }", 2), + CreateWhereTestCase(x => x.NE + x.One == x.F - x.One, "{ $match : { $expr : { $eq : [{ $add : ['$NE', '$One'] }, { $subtract : ['$F', '$One'] }] } } }"), + CreateWhereTestCase(x => x.NE + x.One == x.NF, "{ $match : { $expr : { $eq : [{ $add : ['$NE', '$One'] }, '$NF'] } } }", 2, 6), + CreateWhereTestCase(x => x.NE + x.One == x.NF - x.One, "{ $match : { $expr : { $eq : [{ $add : ['$NE', '$One'] }, { $subtract : ['$NF', '$One'] }] } } }", 6), + CreateWhereTestCase(x => E.A == x.F + x.One, "{ $match : { $expr : { $eq : [1, { $add : ['$F', '$One'] }] } } }"), + CreateWhereTestCase(x => E.A == x.NF + x.One, "{ $match : { $expr : { $eq : [1, { $add : ['$NF', '$One'] }] } } }"), + CreateWhereTestCase(x => (E?)E.A == x.F + x.One, "{ $match : { $expr : { $eq : [1, { $add : ['$F', '$One'] }] } } }"), + CreateWhereTestCase(x => (E?)E.A == x.NF + x.One, "{ $match : { $expr : { $eq : [1, { $add : ['$NF', '$One'] }] } } }"), + CreateWhereTestCase(x => (E?)null == x.NF + x.One, "{ $match : { $expr : { $eq : [null, { $add : ['$NF', '$One'] }] } } }", 4, 6), + CreateWhereTestCase(x => null == x.NF + x.One, "{ $match : { $expr : { $eq : [null, { $add : ['$NF', '$One'] }] } } }", 4, 6), + + // test all combinations of field/constant and nullable/not nullable with '-' (sometimes combined with '+' on other side) + CreateWhereTestCase(x => x.E - x.One == E.B, "{ $match : { $expr : { $eq : [{ $subtract : ['$E', '$One'] }, 2] } } }", 3), + CreateWhereTestCase(x => x.E - x.One == (E?)E.B, "{ $match : { $expr : { $eq : [{ $subtract : ['$E', '$One'] }, 2] } } }", 3), + CreateWhereTestCase(x => x.E - x.One == x.F, "{ $match : { $expr : { $eq : [{ $subtract : ['$E', '$One'] }, '$F'] } } }", 3), + CreateWhereTestCase(x => x.E - x.One == x.F + x.One, "{ $match : { $expr : { $eq : [{ $subtract : ['$E', '$One'] }, { $add : ['$F', '$One'] }] } } }", 6), + CreateWhereTestCase(x => x.E - x.One == x.NF, "{ $match : { $expr : { $eq : [{ $subtract : ['$E', '$One'] }, '$NF'] } } }", 3), + CreateWhereTestCase(x => x.E - x.One == x.NF + x.One, "{ $match : { $expr : { $eq : [{ $subtract : ['$E', '$One'] }, { $add : ['$NF', '$One'] }] } } }"), + CreateWhereTestCase(x => x.NE - x.One == E.B, "{ $match : { $expr : { $eq : [{ $subtract : ['$NE', '$One'] }, 2] } } }", 3), + CreateWhereTestCase(x => x.NE - x.One == (E?)E.B, "{ $match : { $expr : { $eq : [{ $subtract : ['$NE', '$One'] }, 2] } } }", 3), + CreateWhereTestCase(x => x.NE - x.One == (E?)null, "{ $match : { $expr : { $eq : [{ $subtract : ['$NE', '$One'] }, null] } } }", 5, 6), + CreateWhereTestCase(x => x.NE - x.One == null, "{ $match : { $expr : { $eq : [{ $subtract : ['$NE', '$One'] }, null] } } }", 5, 6), + CreateWhereTestCase(x => x.NE - x.One == x.F, "{ $match : { $expr : { $eq : [{ $subtract : ['$NE', '$One'] }, '$F'] } } }", 3), + CreateWhereTestCase(x => x.NE - x.One == x.F + x.One, "{ $match : { $expr : { $eq : [{ $subtract : ['$NE', '$One'] }, { $add : ['$F', '$One'] }] } } }"), + CreateWhereTestCase(x => x.NE - x.One == x.NF, "{ $match : { $expr : { $eq : [{ $subtract : ['$NE', '$One'] }, '$NF'] } } }", 3, 6), + CreateWhereTestCase(x => x.NE - x.One == x.NF + x.One, "{ $match : { $expr : { $eq : [{ $subtract : ['$NE', '$One'] }, { $add : ['$NF', '$One'] }] } } }", 6), + CreateWhereTestCase(x => E.A == x.F - x.One, "{ $match : { $expr : { $eq : [1, { $subtract : ['$F', '$One'] }] } } }", 3), + CreateWhereTestCase(x => E.A == x.NF - x.One, "{ $match : { $expr : { $eq : [1, { $subtract : ['$NF', '$One'] }] } } }", 3), + CreateWhereTestCase(x => (E?)E.A == x.F - x.One, "{ $match : { $expr : { $eq : [1, { $subtract : ['$F', '$One'] }] } } }", 3), + CreateWhereTestCase(x => (E?)E.A == x.NF - x.One, "{ $match : { $expr : { $eq : [1, { $subtract : ['$NF', '$One'] }] } } }", 3), + CreateWhereTestCase(x => (E?)null == x.NF - x.One, "{ $match : { $expr : { $eq : [null, { $subtract : ['$NF', '$One'] }] } } }", 4, 6), + CreateWhereTestCase(x => null == x.NF - x.One, "{ $match : { $expr : { $eq : [null, { $subtract : ['$NF', '$One'] }] } } }", 4, 6), + + // test all combinations of field/constant and nullable/ not nullable with string representation (only comparisons allowed with string representation) + CreateWhereTestCase(x => x.S == E.B, "{ $match : { S : 'B' } }", 2), + CreateWhereTestCase(x => x.S == (E?)E.B, "{ $match : { S : 'B' } }", 2), + CreateWhereTestCase(x => x.S == x.T, "{ $match : { $expr : { $eq : ['$S', '$T'] } } }", 1, 4), + CreateWhereTestCase(x => x.S == x.NT, "{ $match : { $expr : { $eq : ['$S', '$NT'] } } }", 1), + CreateWhereTestCase(x => x.NS == E.B, "{ $match : { NS : 'B' } }", 2), + CreateWhereTestCase(x => x.NS == (E?)E.B, "{ $match : { NS : 'B' } }", 2), + CreateWhereTestCase(x => x.NS == (E?)null, "{ $match : { NS : null } }", 5, 6), + CreateWhereTestCase(x => x.NS == null, "{ $match : { NS : null } }", 5, 6), + CreateWhereTestCase(x => x.NS == x.T, "{ $match : { $expr : { $eq : ['$NS', '$T'] } } }", 1, 4), + CreateWhereTestCase(x => x.NS == x.NT, "{ $match : { $expr : { $eq : ['$NS', '$NT'] } } }", 1, 6), + CreateWhereTestCase(x => E.A == x.T, "{ $match : { T : 'A' } }", 1), + CreateWhereTestCase(x => E.A == x.NT, "{ $match : { NT : 'A' } }", 1), + CreateWhereTestCase(x => (E?)E.A == x.T, "{ $match : { T : 'A' } }", 1), + CreateWhereTestCase(x => (E?)E.A == x.NT, "{ $match : { NT : 'A' } }", 1), + CreateWhereTestCase(x => (E?)null == x.NT, "{ $match : { NT : null } }", 4, 6), + CreateWhereTestCase(x => null == x.NT, "{ $match : { NT : null } }", 4, 6) + }; + } + + public static IEnumerable Where_with_enum_comparison_should_work_member_data() + { + for (var i = 0; i < __whereTestCases.Length; i++) + { + var predicateAsString = __whereTestCases[i].PredicateAsString; + var expectedStage = __whereTestCases[i].ExpectedStage; + var expectedResults = __whereTestCases[i].ExpectedResults; + yield return new object[] { i, predicateAsString, expectedStage, expectedResults }; + } + } + + [Theory] + [MemberData(nameof(Where_with_enum_comparison_should_work_member_data))] + public void Where_with_enum_comparison_should_work(int i, string predicateAsString, string expectedMatchStage, int[] expectedResults) + { + var collection = CreateCollection(); + var predicate = __whereTestCases[i].Predicate; + expectedResults ??= new int[0]; + + var queryable = collection + .AsQueryable() + .OrderBy(x => x.Id) + .Where(predicate); + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $sort : { _id : 1 } }", + expectedMatchStage); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(expectedResults); + } + + [Fact] + public void Comparison_of_enum_and_enum_with_mismatched_serializers_should_throw() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .Where(x => x.E == x.S); + + var exception = Record.Exception(() => Translate(collection, queryable)); + + exception.Should().BeOfType(); + exception.Message.Should().Contain("because the two enums being compared are serialized using different serializers"); + } + + [Fact] + public void Comparison_of_enum_and_nullable_enum_with_mismatched_serializers_should_throw() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .Where(x => x.E == x.NS); + + var exception = Record.Exception(() => Translate(collection, queryable)); + + exception.Should().BeOfType(); + exception.Message.Should().Contain("because the two enums being compared are serialized using different serializers"); + } + + [Fact] + public void Comparison_of_nullable_enum_and_enum_with_mismatched_serializers_should_throw() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .Where(x => x.NE == x.S); + + var exception = Record.Exception(() => Translate(collection, queryable)); + + exception.Should().BeOfType(); + exception.Message.Should().Contain("because the two enums being compared are serialized using different serializers"); + } + + [Fact] + public void Comparison_of_nullable_enum_and_nullable_enum_with_mismatched_serializers_should_throw() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .Where(x => x.NE == x.NS); + + var exception = Record.Exception(() => Translate(collection, queryable)); + + exception.Should().BeOfType(); + exception.Message.Should().Contain("because the two enums being compared are serialized using different serializers"); + } + + private IMongoCollection CreateCollection() + { + var collection = GetCollection(); + + CreateCollection( + collection, + new C { Id = 1, One = 1, E = E.A, F = E.A, NE = E.A, NF = E.A, S = E.A, T = E.A, NS = E.A, NT = E.A }, + new C { Id = 2, One = 1, E = E.B, F = E.C, NE = E.B, NF = E.C, S = E.B, T = E.C, NS = E.B, NT = E.C }, + new C { Id = 3, One = 1, E = E.C, F = E.B, NE = E.C, NF = E.B, S = E.C, T = E.B, NS = E.C, NT = E.B }, + new C { Id = 4, One = 1, E = E.X, F = E.X, NE = E.X, NF = null, S = E.X, T = E.X, NS = E.X, NT = null }, + new C { Id = 5, One = 1, E = E.Y, F = E.Z, NE = null, NF = E.Z, S = E.Y, T = E.Z, NS = null, NT = E.Z }, + new C { Id = 6, One = 1, E = E.Z, F = E.Y, NE = null, NF = null, S = E.Z, T = E.Y, NS = null, NT = null }); + + return collection; + } + + private class C + { + public int Id { get; set; } + public int One { get; set; } + public E E { get; set; } + public E F { get; set; } + public E? NE { get; set; } + public E? NF { get; set; } + [BsonRepresentation(BsonType.String)] public E S { get; set; } + [BsonRepresentation(BsonType.String)] public E T { get; set; } + [BsonRepresentation(BsonType.String)] public E? NS { get; set; } + [BsonRepresentation(BsonType.String)] public E? NT { get; set; } + } + + private enum E { A = 1, B = 2, C = 3, X = 6, Y = 7, Z = 9 } + } +} diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4405Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4405Tests.cs new file mode 100644 index 00000000000..21492805a83 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4405Tests.cs @@ -0,0 +1,78 @@ +/* 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.Globalization; +using System.Linq; +using FluentAssertions; +using MongoDB.Driver.Linq; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira +{ + public class CSharp4405Tests : Linq3IntegrationTest + { + [Fact] + public void Week_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .Select(x => x.D.Week()); + + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $project : { _v : { $week : '$D' }, _id : 0 } }"); + + var results = queryable.ToList(); + results.Should().Equal(0, 0); + } + + [Fact] + public void Week_with_timezone_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .Select(x => x.D.Week(x.TZ)); + + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $project : { _v : { $week : { date : '$D', timezone : '$TZ' } }, _id : 0 } }"); + + var results = queryable.ToList(); + results.Should().Equal(0, 52); + } + + private IMongoCollection CreateCollection() + { + var collection = GetCollection("C"); + + CreateCollection( + collection, + new C { Id = 1, D = DateTime.Parse("2022-01-01T00:00:00Z", null, DateTimeStyles.AdjustToUniversal), TZ = "UTC" }, + new C { Id = 2, D = DateTime.Parse("2022-01-01T00:00:00Z", null, DateTimeStyles.AdjustToUniversal), TZ = "America/New_York" }); + + return collection; + } + + private class C + { + public int Id { get; set; } + public DateTime D { get; set; } + public string TZ { get; set; } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4410Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4410Tests.cs new file mode 100644 index 00000000000..ffa7de87764 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4410Tests.cs @@ -0,0 +1,299 @@ +/* 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.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver.Linq; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira +{ + public class CSharp4410Tests : Linq3IntegrationTest + { + private static readonly SelectTestCase[] __selectTestCases; + + private class SelectTestCase + { + public Expression> Projection { get; set; } + public string ProjectionAsString { get; set; } + public string ExpectedStage { get; set; } + public int[] ExpectedResults { get; set; } + } + + private static SelectTestCase CreateSelectTestCase( + Expression> projection, + string expectedStage, + params int[] expectedResults) + { + var projectionAsString = projection.ToString(); + return new SelectTestCase { Projection = projection, ProjectionAsString = projectionAsString, ExpectedStage = expectedStage, ExpectedResults = expectedResults }; + } + + static CSharp4410Tests() + { + __selectTestCases = new SelectTestCase[] + { + // test all comparison operators + CreateSelectTestCase(x => x.E == E.X, "{ $project : { _v : { $eq : ['$E', 6] }, _id : 0 } }", 4), + CreateSelectTestCase(x => x.E != E.X, "{ $project : { _v : { $ne : ['$E', 6] }, _id : 0 } }", 1, 2, 3, 5, 6), + CreateSelectTestCase(x => x.E >= E.X, "{ $project : { _v : { $gte : ['$E', 6] }, _id : 0 } }", 4, 5, 6), + CreateSelectTestCase(x => x.E <= E.X, "{ $project : { _v : { $lte : ['$E', 6] }, _id : 0 } }", 1, 2, 3, 4), + CreateSelectTestCase(x => x.E > E.X, "{ $project : { _v : { $gt : ['$E', 6] }, _id : 0 } }", 5, 6), + CreateSelectTestCase(x => x.E < E.X, "{ $project : { _v : { $lt : ['$E', 6] }, _id : 0 } }", 1, 2, 3), + + // test all combinations of field/constant and nullable/not nullable + CreateSelectTestCase(x => x.E == E.B, "{ $project : { _v : { $eq : ['$E', 2] }, _id : 0 } }", 2), + CreateSelectTestCase(x => x.E == (E?)E.B, "{ $project : { _v : { $eq : ['$E', 2] }, _id : 0 } }", 2), + CreateSelectTestCase(x => x.E == x.F, "{ $project : { _v : { $eq : ['$E', '$F'] }, _id : 0 } }", 1, 4), + CreateSelectTestCase(x => x.E == x.NF, "{ $project : { _v : { $eq : ['$E', '$NF'] }, _id : 0 } }", 1), + CreateSelectTestCase(x => x.NE == E.B, "{ $project : { _v : { $eq : ['$NE', 2] }, _id : 0 } }", 2), + CreateSelectTestCase(x => x.NE == (E?)E.B, "{ $project : { _v : { $eq : ['$NE', 2] }, _id : 0 } }", 2), + CreateSelectTestCase(x => x.NE == (E?)null, "{ $project : { _v : { $eq : ['$NE', null] }, _id : 0 } }", 5, 6), + CreateSelectTestCase(x => x.NE == null, "{ $project : { _v : { $eq : ['$NE', null] }, _id : 0 } }", 5, 6), + CreateSelectTestCase(x => x.NE == x.F, "{ $project : { _v : { $eq : ['$NE', '$F'] }, _id : 0 } }", 1, 4), + CreateSelectTestCase(x => x.NE == x.NF, "{ $project : { _v : { $eq : ['$NE', '$NF'] }, _id : 0 } }", 1, 6), + CreateSelectTestCase(x => E.A == x.F, "{ $project : { _v : { $eq : [1, '$F'] }, _id : 0 } }", 1), + CreateSelectTestCase(x => E.A == x.NF, "{ $project : { _v : { $eq : [1, '$NF'] }, _id : 0 } }", 1), + CreateSelectTestCase(x => (E?)E.A == x.F, "{ $project : { _v : { $eq : [1, '$F'] }, _id : 0 } }", 1), + CreateSelectTestCase(x => (E?)E.A == x.NF, "{ $project : { _v : { $eq : [1, '$NF'] }, _id : 0 } }", 1), + CreateSelectTestCase(x => (E?)null == x.NF, "{ $project : { _v : { $eq : [null, '$NF'] }, _id : 0 } }", 4, 6), + CreateSelectTestCase(x => null == x.NF, "{ $project : { _v : { $eq : [null, '$NF'] }, _id : 0 } }", 4, 6), + + // test all combinations of field/constant and nullable/not nullable with '+' (sometimes combined with '-' on other side) + CreateSelectTestCase(x => x.E + x.One == E.B, "{ $project : { _v : { $eq : [{ $add : ['$E', '$One'] }, 2] }, _id : 0 } }", 1), + CreateSelectTestCase(x => x.E + x.One == (E?)E.B, "{ $project : { _v : { $eq : [{ $add : ['$E', '$One'] }, 2] }, _id : 0 } }", 1), + CreateSelectTestCase(x => x.E + x.One == x.F, "{ $project : { _v : { $eq : [{ $add : ['$E', '$One'] }, '$F'] }, _id : 0 } }", 2), + CreateSelectTestCase(x => x.E + x.One == x.F - x.One, "{ $project : { _v : { $eq : [{ $add : ['$E', '$One'] }, { $subtract : ['$F', '$One'] }] }, _id : 0 } }", 5), + CreateSelectTestCase(x => x.E + x.One == x.NF, "{ $project : { _v : { $eq : [{ $add : ['$E', '$One'] }, '$NF'] }, _id : 0 } }", 2), + CreateSelectTestCase(x => x.E + x.One == x.NF - x.One, "{ $project : { _v : { $eq : [{ $add : ['$E', '$One'] }, { $subtract : ['$NF', '$One'] }] }, _id : 0 } }", 5), + CreateSelectTestCase(x => x.NE + x.One == E.B, "{ $project : { _v : { $eq : [{ $add : ['$NE', '$One'] }, 2] }, _id : 0 } }", 1), + CreateSelectTestCase(x => x.NE + x.One == (E?)E.B, "{ $project : { _v : { $eq : [{ $add : ['$NE', '$One'] }, 2] }, _id : 0 } }", 1), + CreateSelectTestCase(x => x.NE + x.One == (E?)null, "{ $project : { _v : { $eq : [{ $add : ['$NE', '$One'] }, null] }, _id : 0 } }", 5, 6), + CreateSelectTestCase(x => x.NE + x.One == null, "{ $project : { _v : { $eq : [{ $add : ['$NE', '$One'] }, null] }, _id : 0 } }", 5, 6), + CreateSelectTestCase(x => x.NE + x.One == x.F, "{ $project : { _v : { $eq : [{ $add : ['$NE', '$One'] }, '$F'] }, _id : 0 } }", 2), + CreateSelectTestCase(x => x.NE + x.One == x.F - x.One, "{ $project : { _v : { $eq : [{ $add : ['$NE', '$One'] }, { $subtract : ['$F', '$One'] }] }, _id : 0 } }"), + CreateSelectTestCase(x => x.NE + x.One == x.NF, "{ $project : { _v : { $eq : [{ $add : ['$NE', '$One'] }, '$NF'] }, _id : 0 } }", 2, 6), + CreateSelectTestCase(x => x.NE + x.One == x.NF - x.One, "{ $project : { _v : { $eq : [{ $add : ['$NE', '$One'] }, { $subtract : ['$NF', '$One'] }] }, _id : 0 } }", 6), + CreateSelectTestCase(x => E.A == x.F + x.One, "{ $project : { _v : { $eq : [1, { $add : ['$F', '$One'] }] }, _id : 0 } }"), + CreateSelectTestCase(x => E.A == x.NF + x.One, "{ $project : { _v : { $eq : [1, { $add : ['$NF', '$One'] }] }, _id : 0 } }"), + CreateSelectTestCase(x => (E?)E.A == x.F + x.One, "{ $project : { _v : { $eq : [1, { $add : ['$F', '$One'] }] }, _id : 0 } }"), + CreateSelectTestCase(x => (E?)E.A == x.NF + x.One, "{ $project : { _v : { $eq : [1, { $add : ['$NF', '$One'] }] }, _id : 0 } }"), + CreateSelectTestCase(x => (E?)null == x.NF + x.One, "{ $project : { _v : { $eq : [null, { $add : ['$NF', '$One'] }] }, _id : 0 } }", 4, 6), + CreateSelectTestCase(x => null == x.NF + x.One, "{ $project : { _v : { $eq : [null, { $add : ['$NF', '$One'] }] }, _id : 0 } }", 4, 6), + + // test all combinations of field/constant and nullable/not nullable with '-' (sometimes combined with '+' on other side) + CreateSelectTestCase(x => x.E - x.One == E.B, "{ $project : { _v : { $eq : [{ $subtract : ['$E', '$One'] }, 2] }, _id : 0 } }", 3), + CreateSelectTestCase(x => x.E - x.One == (E?)E.B, "{ $project : { _v : { $eq : [{ $subtract : ['$E', '$One'] }, 2] }, _id : 0 } }", 3), + CreateSelectTestCase(x => x.E - x.One == x.F, "{ $project : { _v : { $eq : [{ $subtract : ['$E', '$One'] }, '$F'] }, _id : 0 } }", 3), + CreateSelectTestCase(x => x.E - x.One == x.F + x.One, "{ $project : { _v : { $eq : [{ $subtract : ['$E', '$One'] }, { $add : ['$F', '$One'] }] }, _id : 0 } }", 6), + CreateSelectTestCase(x => x.E - x.One == x.NF, "{ $project : { _v : { $eq : [{ $subtract : ['$E', '$One'] }, '$NF'] }, _id : 0 } }", 3), + CreateSelectTestCase(x => x.E - x.One == x.NF + x.One, "{ $project : { _v : { $eq : [{ $subtract : ['$E', '$One'] }, { $add : ['$NF', '$One'] }] }, _id : 0 } }"), + CreateSelectTestCase(x => x.NE - x.One == E.B, "{ $project : { _v : { $eq : [{ $subtract : ['$NE', '$One'] }, 2] }, _id : 0 } }", 3), + CreateSelectTestCase(x => x.NE - x.One == (E?)E.B, "{ $project : { _v : { $eq : [{ $subtract : ['$NE', '$One'] }, 2] }, _id : 0 } }", 3), + CreateSelectTestCase(x => x.NE - x.One == (E?)null, "{ $project : { _v : { $eq : [{ $subtract : ['$NE', '$One'] }, null] }, _id : 0 } }", 5, 6), + CreateSelectTestCase(x => x.NE - x.One == null, "{ $project : { _v : { $eq : [{ $subtract : ['$NE', '$One'] }, null] }, _id : 0 } }", 5, 6), + CreateSelectTestCase(x => x.NE - x.One == x.F, "{ $project : { _v : { $eq : [{ $subtract : ['$NE', '$One'] }, '$F'] }, _id : 0 } }", 3), + CreateSelectTestCase(x => x.NE - x.One == x.F + x.One, "{ $project : { _v : { $eq : [{ $subtract : ['$NE', '$One'] }, { $add : ['$F', '$One'] }] }, _id : 0 } }"), + CreateSelectTestCase(x => x.NE - x.One == x.NF, "{ $project : { _v : { $eq : [{ $subtract : ['$NE', '$One'] }, '$NF'] }, _id : 0 } }", 3, 6), + CreateSelectTestCase(x => x.NE - x.One == x.NF + x.One, "{ $project : { _v : { $eq : [{ $subtract : ['$NE', '$One'] }, { $add : ['$NF', '$One'] }] }, _id : 0 } }", 6), + CreateSelectTestCase(x => E.A == x.F - x.One, "{ $project : { _v : { $eq : [1, { $subtract : ['$F', '$One'] }] }, _id : 0 } }", 3), + CreateSelectTestCase(x => E.A == x.NF - x.One, "{ $project : { _v : { $eq : [1, { $subtract : ['$NF', '$One'] }] }, _id : 0 } }", 3), + CreateSelectTestCase(x => (E?)E.A == x.F - x.One, "{ $project : { _v : { $eq : [1, { $subtract : ['$F', '$One'] }] }, _id : 0 } }", 3), + CreateSelectTestCase(x => (E?)E.A == x.NF - x.One, "{ $project : { _v : { $eq : [1, { $subtract : ['$NF', '$One'] }] }, _id : 0 } }", 3), + CreateSelectTestCase(x => (E?)null == x.NF - x.One, "{ $project : { _v : { $eq : [null, { $subtract : ['$NF', '$One'] }] }, _id : 0 } }", 4, 6), + CreateSelectTestCase(x => null == x.NF - x.One, "{ $project : { _v : { $eq : [null, { $subtract : ['$NF', '$One'] }] }, _id : 0 } }", 4, 6), + + // test all combinations of field/constant and nullable/ not nullable with string representation (only comparisons allowed with string representation) + CreateSelectTestCase(x => x.S == E.B, "{ $project : { _v : { $eq : ['$S', 'B'] }, _id : 0 } }", 2), + CreateSelectTestCase(x => x.S == (E?)E.B, "{ $project : { _v : { $eq : ['$S', 'B'] }, _id : 0 } }", 2), + CreateSelectTestCase(x => x.S == x.T, "{ $project : { _v : { $eq : ['$S', '$T'] }, _id : 0 } }", 1, 4), + CreateSelectTestCase(x => x.S == x.NT, "{ $project : { _v : { $eq : ['$S', '$NT'] }, _id : 0 } }", 1), + CreateSelectTestCase(x => x.NS == E.B, "{ $project : { _v : { $eq : ['$NS', 'B'] }, _id : 0 } }", 2), + CreateSelectTestCase(x => x.NS == (E?)E.B, "{ $project : { _v : { $eq : ['$NS', 'B'] }, _id : 0 } }", 2), + CreateSelectTestCase(x => x.NS == (E?)null, "{ $project : { _v : { $eq : ['$NS', null] }, _id : 0 } }", 5, 6), + CreateSelectTestCase(x => x.NS == null, "{ $project : { _v : { $eq : ['$NS', null] }, _id : 0 } }", 5, 6), + CreateSelectTestCase(x => x.NS == x.T, "{ $project : { _v : { $eq : ['$NS', '$T'] }, _id : 0 } }", 1, 4), + CreateSelectTestCase(x => x.NS == x.NT, "{ $project : { _v : { $eq : ['$NS', '$NT'] }, _id : 0 } }", 1, 6), + CreateSelectTestCase(x => E.A == x.T, "{ $project : { _v : { $eq : ['A', '$T'] }, _id : 0 } }", 1), + CreateSelectTestCase(x => E.A == x.NT, "{ $project : { _v : { $eq : ['A', '$NT'] }, _id : 0 } }", 1), + CreateSelectTestCase(x => (E?)E.A == x.T, "{ $project : { _v : { $eq : ['A', '$T'] }, _id : 0 } }", 1), + CreateSelectTestCase(x => (E?)E.A == x.NT, "{ $project : { _v : { $eq : ['A', '$NT'] }, _id : 0 } }", 1), + CreateSelectTestCase(x => (E?)null == x.NT, "{ $project : { _v : { $eq : [null, '$NT'] }, _id : 0 } }", 4, 6), + CreateSelectTestCase(x => null == x.NT, "{ $project : { _v : { $eq : [null, '$NT'] }, _id : 0 } }", 4, 6) + }; + } + + public static IEnumerable Select_with_enum_comparison_should_work_member_data() + { + for (var i = 0; i < __selectTestCases.Length; i++) + { + var projectionAsString = __selectTestCases[i].ProjectionAsString; + var expectedStage = __selectTestCases[i].ExpectedStage; + var expectedResults = __selectTestCases[i].ExpectedResults; + yield return new object[] { i, projectionAsString, expectedStage, expectedResults }; + } + } + + [Theory] + [MemberData(nameof(Select_with_enum_comparison_should_work_member_data))] + public void Select_with_enum_comparison_should_work(int i, string projectionAsString, string expectedProjectStage, int[] expectedResults) + { + var collection = CreateCollection(); + var projection = __selectTestCases[i].Projection; + expectedResults ??= new int[0]; + + var queryable = collection + .AsQueryable() + .OrderBy(x => x.Id) + .Select(projection); + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $sort : { _id : 1 } }", + expectedProjectStage); + + var results = queryable.ToList(); + Enumerable.Range(1, 6).Where(i => results[i - 1] == true).Should().Equal(expectedResults); + } + + [Fact] + public void Comparison_of_enum_and_enum_with_mismatched_serializers_should_throw() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .Select(x => x.E == x.S); + + var exception = Record.Exception(() => Translate(collection, queryable)); + + exception.Should().BeOfType(); + exception.Message.Should().Contain("because the two enums being compared are serialized using different serializers"); + } + + [Fact] + public void Comparison_of_enum_and_nullable_enum_with_mismatched_serializers_should_throw() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .Select(x => x.E == x.NS); + + var exception = Record.Exception(() => Translate(collection, queryable)); + + exception.Should().BeOfType(); + exception.Message.Should().Contain("because the two enums being compared are serialized using different serializers"); + } + + [Fact] + public void Comparison_of_nullable_enum_and_enum_with_mismatched_serializers_should_throw() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .Select(x => x.NE == x.S); + + var exception = Record.Exception(() => Translate(collection, queryable)); + + exception.Should().BeOfType(); + exception.Message.Should().Contain("because the two enums being compared are serialized using different serializers"); + } + + [Fact] + public void Comparison_of_nullable_enum_and_nullable_enum_with_mismatched_serializers_should_throw() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .Select(x => x.NE == x.NS); + + var exception = Record.Exception(() => Translate(collection, queryable)); + + exception.Should().BeOfType(); + exception.Message.Should().Contain("because the two enums being compared are serialized using different serializers"); + } + + [Fact] + public void Arithmetic_with_enum_represented_as_string_should_throw() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .Select(x => x.S + 1); + + var exception = Record.Exception(() => Translate(collection, queryable)); + + exception.Should().BeOfType(); + exception.Message.Should().Contain("because arithmetic on enums is only allowed when the enum is represented as an integer"); + } + + [Fact] + public void Arithmetic_with_nullable_enum_represented_as_string_should_throw() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .Select(x => x.NS + 1); + + var exception = Record.Exception(() => Translate(collection, queryable)); + + exception.Should().BeOfType(); + exception.Message.Should().Contain("because arithmetic on enums is only allowed when the enum is represented as an integer"); + } + + private IMongoCollection CreateCollection() + { + var collection = GetCollection(); + + CreateCollection( + collection, + new C { Id = 1, One = 1, E = E.A, F = E.A, NE = E.A, NF = E.A, S = E.A, T = E.A, NS = E.A, NT = E.A }, + new C { Id = 2, One = 1, E = E.B, F = E.C, NE = E.B, NF = E.C, S = E.B, T = E.C, NS = E.B, NT = E.C }, + new C { Id = 3, One = 1, E = E.C, F = E.B, NE = E.C, NF = E.B, S = E.C, T = E.B, NS = E.C, NT = E.B }, + new C { Id = 4, One = 1, E = E.X, F = E.X, NE = E.X, NF = null, S = E.X, T = E.X, NS = E.X, NT = null }, + new C { Id = 5, One = 1, E = E.Y, F = E.Z, NE = null, NF = E.Z, S = E.Y, T = E.Z, NS = null, NT = E.Z }, + new C { Id = 6, One = 1, E = E.Z, F = E.Y, NE = null, NF = null, S = E.Z, T = E.Y, NS = null, NT = null }); + + return collection; + } + + private class C + { + public int Id { get; set; } + public int One { get; set; } + public E E { get; set; } + public E F { get; set; } + public E? NE { get; set; } + public E? NF { get; set; } + [BsonRepresentation(BsonType.String)] public E S { get; set; } + [BsonRepresentation(BsonType.String)] public E T { get; set; } + [BsonRepresentation(BsonType.String)] public E? NS { get; set; } + [BsonRepresentation(BsonType.String)] public E? NT { get; set; } + } + + private enum E { A = 1, B = 2, C = 3, X = 6, Y = 7, Z = 9 } + } +} diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4415Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4415Tests.cs new file mode 100644 index 00000000000..d9d4b138718 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4415Tests.cs @@ -0,0 +1,65 @@ +/* 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.Globalization; +using System.Linq; +using FluentAssertions; +using MongoDB.Driver.Linq; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira +{ + public class CSharp4415Tests : Linq3IntegrationTest + { + [Fact] + public void Year_should_work() + { + var collection = CreateCollection(); + + var queryable = collection + .AsQueryable() + .Select(x => x.D) + .Select(x => x.Year); + + var stages = Translate(collection, queryable); + AssertStages( + stages, + "{ $project : { _v : '$D', _id : 0 } }", + "{ $project : { _v : { $year : '$_v' }, _id : 0 } }"); + + var results = queryable.ToList(); + results.Should().Equal(2021, 2022); + } + + private IMongoCollection CreateCollection() + { + var collection = GetCollection("C"); + + CreateCollection( + collection, + new C { Id = 1, D = DateTime.Parse("2021-01-01T01:01:01Z", null, DateTimeStyles.AdjustToUniversal) }, + new C { Id = 2, D = DateTime.Parse("2022-02-02T02:02:02Z", null, DateTimeStyles.AdjustToUniversal) }); + + return collection; + } + + private class C + { + public int Id { get; set; } + public DateTime D { get; set; } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4445Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4445Tests.cs new file mode 100644 index 00000000000..10eda6fc2ca --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4445Tests.cs @@ -0,0 +1,271 @@ +/* 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.Linq; +using FluentAssertions; +using MongoDB.Bson; +using MongoDB.Driver.Linq; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira +{ + public class CSharp4445Tests : Linq3IntegrationTest + { + [Fact] + public void AggregateFluent_Limit_with_int_should_work() + { + var collection = CreateCollection(); + int limit = 1; + + var aggregate = + collection.Aggregate() + .Limit(limit); + + var stages = Translate(collection, aggregate); + + AssertStages(stages, "{ $limit : 1 }"); + stages[0]["$limit"].BsonType.Should().Be(BsonType.Int64); + + var results = aggregate.ToList(); + results.Select(x => x.Id).Should().Equal(1); + } + + [Fact] + public void AggregateFluent_Limit_with_long_should_work() + { + var collection = CreateCollection(); + long limit = 1; + + var aggregate = + collection.Aggregate() + .Limit(limit); + + var stages = Translate(collection, aggregate); + + AssertStages(stages, "{ $limit : 1 }"); + stages[0]["$limit"].BsonType.Should().Be(BsonType.Int64); + + var results = aggregate.ToList(); + results.Select(x => x.Id).Should().Equal(1); + } + + [Fact] + public void AggregateFluent_Skip_with_int_should_work() + { + var collection = CreateCollection(); + int skip = 1; + + var aggregate = + collection.Aggregate() + .Skip(skip); + + var stages = Translate(collection, aggregate); + + AssertStages(stages, "{ $skip : 1 }"); + stages[0]["$skip"].BsonType.Should().Be(BsonType.Int64); + + var results = aggregate.ToList(); + results.Select(x => x.Id).Should().Equal(2); + } + + [Fact] + public void AggregateFluent_Skip_with_long_should_work() + { + var collection = CreateCollection(); + long limit = 1; + + var aggregate = + collection.Aggregate() + .Skip(limit); + + var stages = Translate(collection, aggregate); + + AssertStages(stages, "{ $skip : 1 }"); + stages[0]["$skip"].BsonType.Should().Be(BsonType.Int64); + + var results = aggregate.ToList(); + results.Select(x => x.Id).Should().Equal(2); + } + + [Fact] + public void PipelineDefinitionBuilder_Limit_with_int_should_work() + { + var collection = CreateCollection(); + int limit = 1; + + var pipeline = + new EmptyPipelineDefinition() + .Limit(limit); + + var stages = Translate(pipeline); + + AssertStages(stages, "{ $limit : 1 }"); + stages[0]["$limit"].BsonType.Should().Be(BsonType.Int64); + + var results = collection.Aggregate(pipeline).ToList(); + results.Select(x => x.Id).Should().Equal(1); + } + + [Fact] + public void PipelineDefinitionBuilder_Limit_with_long_should_work() + { + var collection = CreateCollection(); + long limit = 1; + + var pipeline = + new EmptyPipelineDefinition() + .Limit(limit); + + var stages = Translate(pipeline); + + AssertStages(stages, "{ $limit : 1 }"); + stages[0]["$limit"].BsonType.Should().Be(BsonType.Int64); + + var results = collection.Aggregate(pipeline).ToList(); + results.Select(x => x.Id).Should().Equal(1); + } + + [Fact] + public void PipelineDefinitionBuilder_Skip_with_int_should_work() + { + var collection = CreateCollection(); + int skip = 1; + + var pipeline = + new EmptyPipelineDefinition() + .Skip(skip); + + var stages = Translate(pipeline); + + AssertStages(stages, "{ $skip : 1 }"); + stages[0]["$skip"].BsonType.Should().Be(BsonType.Int64); + + var results = collection.Aggregate(pipeline).ToList(); + results.Select(x => x.Id).Should().Equal(2); + } + + [Fact] + public void PipelineDefinitionBuilder_Skip_with_long_should_work() + { + var collection = CreateCollection(); + long skip = 1; + + var pipeline = + new EmptyPipelineDefinition() + .Skip(skip); + + var stages = Translate(pipeline); + + AssertStages(stages, "{ $skip : 1 }"); + stages[0]["$skip"].BsonType.Should().Be(BsonType.Int64); + + var results = collection.Aggregate(pipeline).ToList(); + results.Select(x => x.Id).Should().Equal(2); + } + + [Fact] + public void Queryable_Skip_with_int_should_work() + { + var collection = CreateCollection(); + int count = 1; + + var queryable = + collection.AsQueryable() + .Skip(count); + + var stages = Translate(collection, queryable); + + AssertStages(stages, "{ $skip : 1 }"); + stages[0]["$skip"].BsonType.Should().Be(BsonType.Int64); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(2); + } + + [Fact] + public void Queryable_Skip_with_long_should_work() + { + var collection = CreateCollection(); + long count = 1; + + var queryable = + collection.AsQueryable() + .Skip(count); + + var stages = Translate(collection, queryable); + + AssertStages(stages, "{ $skip : 1 }"); + stages[0]["$skip"].BsonType.Should().Be(BsonType.Int64); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(2); + } + + [Fact] + public void Queryable_Take_with_int_should_work() + { + var collection = CreateCollection(); + int count = 1; + + var queryable = + collection.AsQueryable() + .Take(count); + + var stages = Translate(collection, queryable); + + AssertStages(stages, "{ $limit : 1 }"); + stages[0]["$limit"].BsonType.Should().Be(BsonType.Int64); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(1); + } + + [Fact] + public void Queryable_Take_with_long_should_work() + { + var collection = CreateCollection(); + long count = 1; + + var queryable = + collection.AsQueryable() + .Take(count); + + var stages = Translate(collection, queryable); + + AssertStages(stages, "{ $limit : 1 }"); + stages[0]["$limit"].BsonType.Should().Be(BsonType.Int64); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(1); + } + + private IMongoCollection CreateCollection() + { + var collection = GetCollection("C"); + + CreateCollection( + collection, + new C { Id = 1 }, + new C { Id = 2 }); + + return collection; + } + + private class C + { + public int Id { get; set; } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4457Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4457Tests.cs new file mode 100644 index 00000000000..d016da8ffc2 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4457Tests.cs @@ -0,0 +1,183 @@ +/* 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.Linq; +using FluentAssertions; +using MongoDB.Bson; +using MongoDB.Bson.Serialization; +using MongoDB.Driver.Linq; +using MongoDB.TestHelpers.XunitExtensions; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira +{ + public class CSharp4457Tests : Linq3IntegrationTest + { + [Theory] + [ParameterAttributeData] + public void Filter_with_bool_field_should_work([Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = CreateCollection(linqProvider); + var builder = Builders.Filter; + var filter = builder.Where(x => x.BoolField); + + var rendered = RenderFilter(filter, linqProvider); + rendered.Should().Be("{ BoolField : true }"); + + var results = collection.FindSync(filter).ToList(); + results.Select(x => x.Id).Should().Equal(1); + } + + [Theory] + [ParameterAttributeData] + public void Filter_with_bool_property_should_work([Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = CreateCollection(linqProvider); + var builder = Builders.Filter; + var filter = builder.Where(x => x.BoolProperty); + + var rendered = RenderFilter(filter, linqProvider); + rendered.Should().Be("{ BoolProperty : true }"); + + var results = collection.FindSync(filter).ToList(); + results.Select(x => x.Id).Should().Equal(1); + } + + [Theory] + [ParameterAttributeData] + public void Filter_with_not_bool_field_should_work([Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = CreateCollection(linqProvider); + var builder = Builders.Filter; + var filter = builder.Where(x => !x.BoolField); + + var rendered = RenderFilter(filter, linqProvider); + rendered.Should().Be("{ BoolField : { $ne : true } }"); + + var results = collection.FindSync(filter).ToList(); + results.Select(x => x.Id).Should().Equal(2); + } + + [Theory] + [ParameterAttributeData] + public void Filter_with_not_bool_property_should_work([Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = CreateCollection(linqProvider); + var builder = Builders.Filter; + var filter = builder.Where(x => !x.BoolProperty); + + var rendered = RenderFilter(filter, linqProvider); + rendered.Should().Be("{ BoolProperty : { $ne : true } }"); + + var results = collection.FindSync(filter).ToList(); + results.Select(x => x.Id).Should().Equal(2); + } + + [Theory] + [ParameterAttributeData] + public void Where_with_bool_field_should_work([Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = CreateCollection(linqProvider); + + var queryable = + collection.AsQueryable() + .Where(x => x.BoolField); + + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $match : { BoolField : true } }"); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(1); + } + + [Theory] + [ParameterAttributeData] + public void Where_with_bool_property_should_work([Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = CreateCollection(linqProvider); + + var queryable = + collection.AsQueryable() + .Where(x => x.BoolProperty); + + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $match : { BoolProperty : true } }"); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(1); + } + + [Theory] + [ParameterAttributeData] + public void Where_with_not_bool_field_should_work([Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = CreateCollection(linqProvider); + + var queryable = + collection.AsQueryable() + .Where(x => !x.BoolField); + + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $match : { BoolField : { $ne : true } } }"); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(2); + } + + [Theory] + [ParameterAttributeData] + public void Where_with_not_bool_property_should_work([Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = CreateCollection(linqProvider); + + var queryable = + collection.AsQueryable() + .Where(x => !x.BoolProperty); + + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $match : { BoolProperty : { $ne : true } } }"); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(2); + } + + private IMongoCollection CreateCollection(LinqProvider linqProvider) + { + var collection = GetCollection("C", linqProvider); + + CreateCollection( + collection, + new C { Id = 1, BoolField = true, BoolProperty = true }, + new C { Id = 2, BoolField = false, BoolProperty = false }); + + return collection; + } + + private BsonDocument RenderFilter(FilterDefinition filter, LinqProvider linqProvider) + { + var serializerRegistry = BsonSerializer.SerializerRegistry; + var documentSerializer = serializerRegistry.GetSerializer(); + return filter.Render(documentSerializer, serializerRegistry, linqProvider); + } + + private class C + { + public bool BoolField; + + public int Id { get; set; } + public bool BoolProperty { get; set; } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4466Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4466Tests.cs new file mode 100644 index 00000000000..4c6a34db5d7 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4466Tests.cs @@ -0,0 +1,85 @@ +/* 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.Linq; +using FluentAssertions; +using MongoDB.Bson.Serialization; +using MongoDB.Driver.Linq; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira +{ + public class CSharp4466Tests : Linq3IntegrationTest + { + [Fact] + public void AppendStage_with_null_resultSerializer_should_work() + { + var collection = CreateCollection(); + + var queryable = + collection.AsQueryable() + .AppendStage("{ $project : { X : '$_id', _id : 0 } }", resultSerializer: null); + + var stages = Translate(collection, queryable, out var outputSerializer); + + AssertStages(stages, "{ $project : { X : '$_id', _id : 0 } }"); + outputSerializer.ValueType.Should().Be(typeof(D)); + + var results = queryable.ToList(); + results.Select(x => x.X).Should().Equal(1, 2); + } + + [Fact] + public void AppendStage_with_resultSerializer_should_work() + { + var collection = CreateCollection(); + var resultSerializer = BsonSerializer.LookupSerializer(); + + var queryable = + collection.AsQueryable() + .AppendStage("{ $project : { X : '$_id', _id : 0 } }", resultSerializer); + + var stages = Translate(collection, queryable, out var outputSerializer); + + AssertStages(stages, "{ $project : { X : '$_id', _id : 0 } }"); + outputSerializer.Should().BeSameAs(resultSerializer); + + var results = queryable.ToList(); + results.Select(x => x.X).Should().Equal(1, 2); + } + + private IMongoCollection CreateCollection() + { + var collection = GetCollection("C"); + + CreateCollection( + collection, + new C { Id = 1 }, + new C { Id = 2 }); + + return collection; + } + + private class C + { + public int Id { get; set; } + } + + private class D + { + public int X { get; set; } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4468Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4468Tests.cs new file mode 100644 index 00000000000..330e9c44801 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Jira/CSharp4468Tests.cs @@ -0,0 +1,131 @@ +/* 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 MongoDB.Driver.Linq; +using MongoDB.TestHelpers.XunitExtensions; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira +{ + public class CSharp4468Tests : Linq3IntegrationTest + { + [Theory] + [ParameterAttributeData] + public void Query1_should_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = CreateCollection(linqProvider); + + var queryable = + collection.AsQueryable() + .SelectMany(i => i.Lines) + .GroupBy(l => l.ItemId) + .Select(g => new ItemSummary + { + Id = g.Key, + TotalAmount = g.Sum(l => l.TotalAmount) + }); + + var stages = Translate(collection, queryable); + string[] expectedStages; + if (linqProvider == LinqProvider.V2) + { + expectedStages = new[] + { + "{ $unwind : '$Lines' }", + "{ $project : { Lines : '$Lines', _id : 0 } }", + "{ $group : { _id : '$Lines.ItemId', __agg0 : { $sum : '$Lines.TotalAmount' } } }", + "{ $project : { Id : '$_id', TotalAmount : '$__agg0', _id : 0 } }" + }; + } + else + { + expectedStages = new[] + { + "{ $project : { _v : '$Lines', _id : 0 } }", + "{ $unwind : '$_v' }", + "{ $group : { _id : '$_v.ItemId', __agg0 : { $sum : '$_v.TotalAmount' } } }", + "{ $project : { _id : '$_id', TotalAmount : '$__agg0' } }" + }; + } + AssertStages(stages, expectedStages); + } + + [Theory] + [ParameterAttributeData] + public void Query2_should_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = CreateCollection(linqProvider); + + var queryable = + collection.AsQueryable() + .GroupBy(l => l.Id) + .Select(g => new ItemSummary + { + Id = g.Key, + TotalAmount = g.Sum(l => l.TotalAmount) + }); + + var stages = Translate(collection, queryable); + string[] expectedStages; + if (linqProvider == LinqProvider.V2) + { + expectedStages = new[] + { + "{ $group : { _id : '$_id', __agg0 : { $sum : '$TotalAmount' } } }", + "{ $project : { Id : '$_id', TotalAmount : '$__agg0', _id : 0 } }" + }; + } + else + { + expectedStages = new[] + { + "{ $group : { _id : '$_id', __agg0 : { $sum : '$TotalAmount' } } }", + "{ $project : { _id : '$_id', TotalAmount : '$__agg0' } }" // only difference from LINQ2 is "_id" vs "Id" + }; + } + AssertStages(stages, expectedStages); + } + + private IMongoCollection CreateCollection(LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider: linqProvider); + return collection; + } + + public class OrderDao + { + public OrderLineDao[] Lines { get; set; } + + public decimal TotalAmount { get; set; } + public Guid Id { get; set; } + } + + public class OrderLineDao + { + public decimal TotalAmount { get; set; } + public Guid ItemId { get; set; } + } + + public class ItemSummary + { + public Guid Id { get; set; } + public decimal TotalAmount { get; set; } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Linq3IntegrationTest.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Linq3IntegrationTest.cs index 3e46c90de71..ad6de06958c 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Linq3IntegrationTest.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Linq3IntegrationTest.cs @@ -13,7 +13,6 @@ * limitations under the License. */ -using System; using System.Collections.Generic; using System.Linq; using FluentAssertions; @@ -58,58 +57,103 @@ protected void CreateCollection(IMongoCollection collectio CreateCollection(collection, (IEnumerable)documents); ; } - protected IMongoClient GetClient(LinqProvider linqProvider) + protected IMongoCollection GetCollection(string collectionName = null, LinqProvider linqProvider = LinqProvider.V3) { - return linqProvider switch - { - LinqProvider.V2 => DriverTestConfiguration.Client, - LinqProvider.V3 => DriverTestConfiguration.Linq3Client, - _ => throw new ArgumentException($"Invalid linqProvider: {linqProvider}.", nameof(linqProvider)) - }; + return GetCollection(databaseName: null, collectionName, linqProvider); } - protected IMongoCollection GetCollection(string collectionName = null) + protected IMongoCollection GetCollection(string databaseName, string collectionName, LinqProvider linqProvider = LinqProvider.V3) { - var databaseName = DriverTestConfiguration.DatabaseNamespace.DatabaseName; - return GetCollection(databaseName, collectionName); + var database = GetDatabase(databaseName, linqProvider); + return database.GetCollection(collectionName ?? DriverTestConfiguration.CollectionNamespace.CollectionName); } - protected IMongoCollection GetCollection(string databaseName, string collectionName, LinqProvider linqProvider = LinqProvider.V3) + protected IMongoDatabase GetDatabase(string databaseName = null, LinqProvider linqProvider = LinqProvider.V3) { - databaseName ??= DriverTestConfiguration.DatabaseNamespace.DatabaseName; - collectionName ??= DriverTestConfiguration.CollectionNamespace.CollectionName; - var client = GetClient(linqProvider); - var database = client.GetDatabase(databaseName); - return database.GetCollection(collectionName); + var client = DriverTestConfiguration.GetLinqClient(linqProvider); + return client.GetDatabase(databaseName ?? DriverTestConfiguration.DatabaseNamespace.DatabaseName); } protected static List Translate(IMongoCollection collection, IAggregateFluent aggregate) { var pipelineDefinition = ((AggregateFluent)aggregate).Pipeline; var documentSerializer = collection.DocumentSerializer; - var serializerRegistry = BsonSerializer.SerializerRegistry; var linqProvider = collection.Database.Client.Settings.LinqProvider; - var renderedPipeline = pipelineDefinition.Render(documentSerializer, serializerRegistry, linqProvider); - return renderedPipeline.Documents.ToList(); + return Translate(pipelineDefinition, documentSerializer, linqProvider); } // in this overload the collection argument is used only to infer the TDocument type protected List Translate(IMongoCollection collection, IQueryable queryable) { - return Translate(queryable); + var linqProvider = collection.Database.Client.Settings.LinqProvider; + return Translate(queryable, linqProvider); + } + + // in this overload the collection argument is used only to infer the TDocument type + protected List Translate(IMongoCollection collection, IQueryable queryable, out IBsonSerializer outputSerializer) + { + return Translate(queryable, out outputSerializer); + } + + protected static List Translate(IMongoDatabase database, IAggregateFluent aggregate) + { + var pipelineDefinition = ((AggregateFluent)aggregate).Pipeline; + var linqProvider = database.Client.Settings.LinqProvider; + return Translate(pipelineDefinition, NoPipelineInputSerializer.Instance, linqProvider); + } + + // in this overload the database argument is used only to infer the NoPipelineInput type + protected List Translate(IMongoDatabase database, IQueryable queryable) + { + return Translate(queryable); } - protected List Translate(IQueryable queryable) + protected List Translate(IQueryable queryable, LinqProvider linqProvider = LinqProvider.V3) { - var provider = (MongoQueryProvider)queryable.Provider; - var executableQuery = ExpressionToExecutableQueryTranslator.Translate(provider, queryable.Expression); - var stages = executableQuery.Pipeline.Stages; - return stages.Select(s => s.Render().AsBsonDocument).ToList(); + return Translate(queryable, linqProvider, out _); + } + + protected List Translate(IQueryable queryable, out IBsonSerializer outputSerializer) + { + return Translate(queryable, LinqProvider.V3, out outputSerializer); + } + + protected List Translate(IQueryable queryable, LinqProvider linqProvider, out IBsonSerializer outputSerializer) + { + if (linqProvider == LinqProvider.V2) + { + var linq2QueryProvider = (MongoDB.Driver.Linq.Linq2Implementation.MongoQueryProviderImpl)queryable.Provider; + var executionModel = linq2QueryProvider.GetExecutionModel(queryable.Expression); + var executionModelType = executionModel.GetType(); + var stagesPropertyInfo = executionModelType.GetProperty("Stages"); + var stages = (IEnumerable)stagesPropertyInfo.GetValue(executionModel); + var outputSerializerPropertyInfo = executionModelType.GetProperty("OutputSerializer"); + outputSerializer = (IBsonSerializer)outputSerializerPropertyInfo.GetValue(executionModel); + return stages.ToList(); + } + else + { + var linq3QueryProvider = (MongoQueryProvider)queryable.Provider; + var executableQuery = ExpressionToExecutableQueryTranslator.Translate(linq3QueryProvider, queryable.Expression); + var stages = executableQuery.Pipeline.Stages; + outputSerializer = (IBsonSerializer)executableQuery.Pipeline.OutputSerializer; + return stages.Select(s => s.Render().AsBsonDocument).ToList(); + } + } + + protected static List Translate( + PipelineDefinition pipelineDefinition, + IBsonSerializer documentSerializer = null, + LinqProvider linqProvider = LinqProvider.V3) + { + var serializerRegistry = BsonSerializer.SerializerRegistry; + documentSerializer ??= serializerRegistry.GetSerializer(); + var renderedPipeline = pipelineDefinition.Render(documentSerializer, serializerRegistry, linqProvider); + return renderedPipeline.Documents.ToList(); } - protected BsonDocument TranslateFilter(IMongoCollection collection, IFindFluent find) + protected BsonDocument Translate(IMongoCollection collection, FilterDefinition filterDefinition) { - var filterDefinition = find.Filter; var documentSerializer = collection.DocumentSerializer; var serializerRegistry = BsonSerializer.SerializerRegistry; var linqProvider = collection.Database.Client.Settings.LinqProvider; diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Linq3TestHelpers.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Linq3TestHelpers.cs index ead0822e9d9..1369d6656de 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Linq3TestHelpers.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Linq3TestHelpers.cs @@ -18,7 +18,6 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.Serialization; -using MongoDB.Bson.TestHelpers; using MongoDB.Driver.Linq; using MongoDB.Driver.Linq.Linq3Implementation; using MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToExecutableQueryTranslators; @@ -38,10 +37,10 @@ public static IList Render(PipelineDefinition(PipelineStageDefinition stage, IBsonSerializer inputSerializer, LinqProvider linqProvider) + public static IReadOnlyList Render(PipelineStageDefinition stage, IBsonSerializer inputSerializer, LinqProvider linqProvider) { var rendered = stage.Render(inputSerializer, BsonSerializer.SerializerRegistry, linqProvider); - return rendered.Document; + return rendered.Documents; } public static List Translate(IMongoCollection collection, IAggregateFluent aggregate) @@ -62,10 +61,22 @@ public static List Translate(IMongoCollection< public static List Translate(IQueryable queryable) { - var provider = (MongoQueryProvider)queryable.Provider; - var executableQuery = ExpressionToExecutableQueryTranslator.Translate(provider, queryable.Expression); - var stages = executableQuery.Pipeline.Stages; - return stages.Select(s => s.Render().AsBsonDocument).ToList(); + var provider = queryable.Provider; + if (provider is MongoDB.Driver.Linq.Linq2Implementation.MongoQueryProviderImpl linq2Provider) + { + var executionModel = linq2Provider.GetExecutionModel(queryable.Expression); + var executionModelType = executionModel.GetType(); + var stagesPropertyInfo = executionModelType.GetProperty("Stages"); + var stages = (IEnumerable)stagesPropertyInfo.GetValue(executionModel); + return stages.ToList(); + } + else + { + var linq3Provider = (MongoQueryProvider)provider; + var executableQuery = ExpressionToExecutableQueryTranslator.Translate(linq3Provider, queryable.Expression); + var stages = executableQuery.Pipeline.Stages; + return stages.Select(s => s.Render().AsBsonDocument).ToList(); + } } } } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/LinqProviderV3Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/LinqProviderV3Tests.cs index de37e67e1f7..b37de455038 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/LinqProviderV3Tests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/LinqProviderV3Tests.cs @@ -16,7 +16,6 @@ using System; using System.Linq; using System.Linq.Expressions; -using System.Threading; using FluentAssertions; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; @@ -62,7 +61,7 @@ public void TranslateExpressionToAggregateExpression_should_return_expected_resu } [Fact] - public void TranslateExpressionToBucketOutputProjection_should_return_expected_result() + public void TranslateExpressionToBucketOutputProjection_should_throw() { var subject = LinqProviderAdapter.V3; Expression> valueExpression = c => c.X; @@ -71,13 +70,9 @@ public void TranslateExpressionToBucketOutputProjection_should_return_expected_r var documentSerializer = serializerRegistry.GetSerializer(); var translationOptions = new ExpressionTranslationOptions(); - var result = subject.TranslateExpressionToBucketOutputProjection(valueExpression, outputExpression, documentSerializer, serializerRegistry, translationOptions); + var exception = Record.Exception(() => subject.TranslateExpressionToBucketOutputProjection(valueExpression, outputExpression, documentSerializer, serializerRegistry, translationOptions)); - var expectedResult = LinqProviderAdapter.V2.TranslateExpressionToBucketOutputProjection(valueExpression, outputExpression, documentSerializer, serializerRegistry, translationOptions); - expectedResult.Document.Should().Be("{ $sum : 1 }"); - expectedResult.ProjectionSerializer.Should().BeOfType(); - result.Document.Should().Be(expectedResult.Document); - result.ProjectionSerializer.Should().BeOfType(expectedResult.ProjectionSerializer.GetType()); + exception.Should().BeOfType(); } [Fact] @@ -139,9 +134,7 @@ public void TranslateExpressionToFindProjection_should_return_expected_result() var result = subject.TranslateExpressionToFindProjection(expression, documentSerializer, serializerRegistry); - var expectedResult = LinqProviderAdapter.V2.TranslateExpressionToFindProjection(expression, documentSerializer, serializerRegistry); - expectedResult.Document.Should().Be("{ X : 1, _id : 0 }"); - result.Document.Should().Be(expectedResult.Document); + result.Document.Should().Be("{ _v : '$X', _id : 0 }"); result.ProjectionSerializer.ValueType.Should().Be(typeof(int)); } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/MongoQueryProviderTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/MongoQueryProviderTests.cs index 16a5f97acca..e60d53a9fe5 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/MongoQueryProviderTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/MongoQueryProviderTests.cs @@ -13,14 +13,67 @@ * limitations under the License. */ +using System; +using System.Linq; +using System.Linq.Expressions; +using FluentAssertions; using MongoDB.Bson.TestHelpers; using MongoDB.Driver.Linq.Linq3Implementation; +using MongoDB.Driver.Linq.Linq3Implementation.Reflection; +using Xunit; namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests { public class MongoQueryProviderTests { - // TODO: implement MongoQueryProviderTests + [Fact] + public void CreateQuery_non_generic_should_return_expected_result() + { + var (subject, expression) = CreateSubject(); + + var result = subject.CreateQuery(expression); + + result.ElementType.Should().Be(typeof(int)); + result.Expression.Should().BeSameAs(expression); + result.Provider.Should().BeOfType>(); + } + + [Fact] + public void CreateQuery_generic_should_return_expected_result() + { + var (subject, expression) = CreateSubject(); + + var result = subject.CreateQuery(expression); + + result.ElementType.Should().Be(typeof(int)); + result.Expression.Should().BeSameAs(expression); + result.Provider.Should().BeOfType>(); + } + + private (IQueryProvider, Expression) CreateSubject() + { + var client = DriverTestConfiguration.Linq3Client; + var database = client.GetDatabase("test"); + var collection = database.GetCollection("test"); + var provider = new MongoQueryProvider(collection, session: null, options: null); + var queryable = collection.AsQueryable(); + var parameter = Expression.Parameter(typeof(C), "x"); + var expression = + Expression.Call( + QueryableMethod.Select.MakeGenericMethod(typeof(C), typeof(int)), + Expression.Constant(queryable, typeof(IQueryable)), + Expression.Quote( + Expression.Lambda>( + Expression.Property(parameter, "X"), + parameter))); + return (provider, expression); + } + + public class C + { + public int Id { get; set; } + public int X { get; set; } + } } internal static class MongoQueryProviderExtensions diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Serializers/KnownSerializers/KnownSerializerFinderTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Serializers/KnownSerializers/KnownSerializerFinderTests.cs index bfeef38da17..d215d052caa 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Serializers/KnownSerializers/KnownSerializerFinderTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationTests/Serializers/KnownSerializers/KnownSerializerFinderTests.cs @@ -19,6 +19,7 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver.Linq; using MongoDB.Driver.Linq.Linq3Implementation.Serializers; using MongoDB.Driver.Linq.Linq3Implementation.Serializers.KnownSerializers; using Xunit; @@ -89,20 +90,6 @@ public void Enum_property_expression_should_return_enum_serializer_with_int_repr serializer.Should().Be(expectedPropertySerializationInfo.Serializer); } - [Fact] - public void Enum_comparison_expression_should_use_underlying_type_serializer_for_constant_represented_as_int() - { - Expression> expression = x => x.Ei == E.A; - var collectionSerializer = GetCollectionSerializer(); - - var result = KnownSerializerFinder.FindKnownSerializers(expression, collectionSerializer); - - var equalsExpression = (BinaryExpression)expression.Body; - var leftSerializer = result.GetSerializer(equalsExpression.Left); - var rightSerializer = (EnumUnderlyingTypeSerializer)result.GetSerializer(equalsExpression.Right); - rightSerializer.EnumSerializer.Should().BeSameAs(leftSerializer); - } - [Fact] public void Enum_property_expression_should_return_enum_serializer_with_string_representation() { @@ -116,20 +103,6 @@ public void Enum_property_expression_should_return_enum_serializer_with_string_r serializer.Should().Be(expectedPropertySerializationInfo.Serializer); } - [Fact] - public void Enum_comparison_expression_should_use_underlying_type_serializer_for_constant_represented_as_string() - { - Expression> expression = x => x.Es == E.A; - var collectionSerializer = GetCollectionSerializer(); - - var result = KnownSerializerFinder.FindKnownSerializers(expression, collectionSerializer); - - var equalsExpression = (BinaryExpression)expression.Body; - var leftSerializer = result.GetSerializer(equalsExpression.Left); - var rightSerializer = (EnumUnderlyingTypeSerializer)result.GetSerializer(equalsExpression.Right); - rightSerializer.EnumSerializer.Should().BeSameAs(leftSerializer); - } - [Fact] public void Conditional_expression_should_return_enum_serializer_with_int_representation() { @@ -161,13 +134,13 @@ public void Conditional_expression_should_return_enum_serializer_with_string_rep [Fact] public void Conditional_expression_with_different_enum_representations_should_throw() { - Expression> expression = x => x.Ei == E.A ? E.B : x.Es; + Expression> expression = x => x.Ei == E.A ? x.Ei : x.Es; var collectionSerializer = GetCollectionSerializer(); - var result = KnownSerializerFinder.FindKnownSerializers(expression, collectionSerializer); + var exception = Record.Exception(() => KnownSerializerFinder.FindKnownSerializers(expression, collectionSerializer)); - var conditionalExpression = (ConditionalExpression)expression.Body; - Assert.Throws(() => result.GetSerializer(conditionalExpression.IfTrue)); + var notSupportedExpression = exception.Should().BeOfType().Subject; + notSupportedExpression.Message.Should().Contain("because IfTrue and IfFalse expressions have different serializers"); } [Fact] @@ -184,23 +157,6 @@ public void Property_chain_should_return_correct_nested_serializer() serializer.Should().Be(bSerializationInfo.Serializer); } - [Fact] - public void Two_property_chains_should_each_return_correct_nested_serializer() - { - Expression> expression = x => x.A.B == null ? x.Ei : x.Es; - var collectionSerializer = GetCollectionSerializer(); - - var result = KnownSerializerFinder.FindKnownSerializers(expression, collectionSerializer); - - var conditionalExpression = (ConditionalExpression)expression.Body; - var trueBranchSerializer = result.GetSerializer(conditionalExpression.IfTrue); - collectionSerializer.TryGetMemberSerializationInfo(nameof(C.Ei), out var eiSerializationInfo).Should().BeTrue(); - trueBranchSerializer.Should().Be(eiSerializationInfo.Serializer); - var falseBranchSerializer = result.GetSerializer(conditionalExpression.IfFalse); - collectionSerializer.TryGetMemberSerializationInfo(nameof(C.Es), out var esSerializationInfo).Should().BeTrue(); - falseBranchSerializer.Should().Be(esSerializationInfo.Serializer); - } - [Fact] public void Projection_into_new_type_should_return_correct_serializer() { diff --git a/tests/MongoDB.Driver.Tests/ListDatabasesTests.cs b/tests/MongoDB.Driver.Tests/ListDatabasesTests.cs index de1418c0da0..b4922954423 100644 --- a/tests/MongoDB.Driver.Tests/ListDatabasesTests.cs +++ b/tests/MongoDB.Driver.Tests/ListDatabasesTests.cs @@ -16,7 +16,7 @@ using System; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; using Xunit; @@ -30,7 +30,7 @@ public class ListDatabasesTests private string _roleName = $"listDatabases{Guid.NewGuid()}"; private string _userName = $"authorizedDatabases{Guid.NewGuid()}"; - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Execute_should_return_the_expected_result_when_AuthorizedDatabases_is_used( [Values(null, false, true)] bool? authorizedDatabases) diff --git a/tests/MongoDB.Driver.Tests/LoggingTests.cs b/tests/MongoDB.Driver.Tests/LoggingTests.cs new file mode 100644 index 00000000000..7ea759f4d11 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/LoggingTests.cs @@ -0,0 +1,192 @@ +/* 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 Microsoft.Extensions.Logging; +using MongoDB.Bson; +using MongoDB.Driver.Core.Configuration; +using MongoDB.Driver.Core.Logging; +using MongoDB.Driver.Core.TestHelpers.Logging; +using MongoDB.Driver.Linq; +using MongoDB.Driver.TestHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace MongoDB.Driver.Tests +{ + public class LoggingTests : LoggableTestClass + { + public LoggingTests(ITestOutputHelper output) : base(output, includeAllCategories: true) + { + } + + [Fact] + public void MongoClient_should_log() + { + using (var client = DriverTestConfiguration.CreateDisposableClient(LoggingSettings)) + { + client.ListDatabases(new ListDatabasesOptions()); + } + + var logs = Logs; + + AssertLogs(new[] + { + SDAM("Description changed"), + SDAM("Server opening"), + Connection("Connection pool opening"), + Connection("Connection pool created"), + SDAM("Server opened"), + SDAM("Cluster opened"), + Connection("Connection checkout started"), + Connection("Connection created"), + Connection("Connection ready"), + Connection("Connection added"), + Connection("Connection checked out"), + TestsDebug("Disposing"), + SDAM("Cluster closing"), + SDAM("Removing server"), + SDAM("Server closing"), + Connection("Connection closing"), + Connection("Connection closed"), + Connection("Connection pool closed"), + SDAM("Server closed"), + SDAM("Removed server"), + SDAM("Disposing"), + SDAM("Description changed"), + SDAM("Disposed"), + SDAM("Cluster closed"), + TestsDebug("Cluster unregistered and disposed"), + TestsDebug("Disposed") + }, + logs); + + (LogLevel, string, string) Connection(string message) => (LogLevel.Debug, LogCategoryHelper.GetCategoryName(), message); + (LogLevel, string, string) SDAM(string message) => (LogLevel.Debug, LogCategoryHelper.GetCategoryName(), message); + (LogLevel, string, string) TestsDebug(string message) => (LogLevel.Debug, $"MongoDB.Tests.{typeof(T).Name}", message); + } + + [Fact] + public void MongoClient_should_not_throw_when_factory_is_null() + { + using (var client = DriverTestConfiguration.CreateDisposableClient(loggingSettings: null)) + { + client.ListDatabases(new ListDatabasesOptions()); + } + + Logs.Any().Should().BeFalse(); + } + + [Theory] + [InlineData(null)] + [InlineData(100)] + public void Prose_tests_truncation_limit_1(int? maxDocumentSize) + { + var expectedMaxSize = (maxDocumentSize ?? 1000) + 3; // 3 to account for '...' + const string collectionName = "proseLogTests"; + + var documents = Enumerable.Range(0, 100).Select(_ => new BsonDocument() { { "x", "y" } }).ToArray(); + + var loggingSettings = maxDocumentSize == null + ? new LoggingSettings(LoggerFactory) + : new LoggingSettings(LoggerFactory, maxDocumentSize.Value); + using (var client = DriverTestConfiguration.CreateDisposableClient(loggingSettings)) + { + + var db = client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName); + + try + { + var collection = db.GetCollection(collectionName); + collection.InsertMany(documents); + _ = collection.Find(FilterDefinition.Empty).ToList(); + } + finally + { + db.DropCollection(collectionName); + } + } + + var commandCategory = LogCategoryHelper.GetCategoryName(); + var commands = Logs.Where(l => l.Category == commandCategory).ToArray(); + + GetCommandParameter(commands, "insert", "Command started", StructuredLogTemplateProviders.Command) + .Length.Should().Be(expectedMaxSize); + GetCommandParameter(commands, "insert", "Command succeeded", StructuredLogTemplateProviders.Reply) + .Length.Should().BeLessOrEqualTo(expectedMaxSize); + GetCommandParameter(commands, "find", "Command succeeded", StructuredLogTemplateProviders.Reply) + .Length.Should().Be(expectedMaxSize); + } + + [Fact] + public void Prose_tests_truncation_limit_2() + { + const int truncationSize = 5; + const int maxDocumentSize = truncationSize + 3; // 3 to account for '...' + var loggingSettings = new LoggingSettings(LoggerFactory, truncationSize); + using (var client = DriverTestConfiguration.CreateDisposableClient(loggingSettings)) + { + var db = client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName); + + try + { + db.RunCommand(new BsonDocument() { { "hello", "true" } }); + db.RunCommand(new BsonDocument() { { "notARealCommand", "true" } }); + } + catch (MongoCommandException) { } + } + + var commandCategory = LogCategoryHelper.GetCategoryName(); + var commands = Logs.Where(l => l.Category == commandCategory).ToArray(); + + GetCommandParameter(commands, "hello", "Command started", StructuredLogTemplateProviders.Command) + .Length.Should().Be(maxDocumentSize); + GetCommandParameter(commands, "hello", "Command succeeded", StructuredLogTemplateProviders.Reply) + .Length.Should().Be(maxDocumentSize); + GetCommandParameter(commands, "notARealCommand", "Command failed", StructuredLogTemplateProviders.Failure) + .Length.Should().Be(maxDocumentSize); + } + + private void AssertLogs((LogLevel logLevel, string categorySubString, string messageSubString)[] expectedLogs, LogEntry[] actualLogs) + { + var actualLogIndex = 0; + foreach (var (logLevel, categorySubString, messageSubString) in expectedLogs) + { + actualLogIndex = Array.FindIndex(actualLogs, actualLogIndex, Match); + + if (actualLogIndex < 0) + { + throw new Exception($"Log entry '{logLevel}_{categorySubString}_{messageSubString}' not found"); + } + + bool Match(LogEntry logEntry) => + logEntry.LogLevel == logLevel && + logEntry.Category?.Contains(categorySubString) == true && + logEntry.FormattedMessage?.Contains(messageSubString) == true; + } + } + + private string GetCommandParameter(LogEntry[] commandLogs, string commandName, string message, string parameter) + { + var command = commandLogs.Single(c => + c.GetParameter(StructuredLogTemplateProviders.CommandName) == commandName && + c.GetParameter(StructuredLogTemplateProviders.Message) == message); + + return command.GetParameter(parameter); + } + } +} diff --git a/tests/MongoDB.Driver.Tests/MongoBulkWriteExceptionTests.cs b/tests/MongoDB.Driver.Tests/MongoBulkWriteExceptionTests.cs index 8975ecc6831..8e3f1f6da5e 100644 --- a/tests/MongoDB.Driver.Tests/MongoBulkWriteExceptionTests.cs +++ b/tests/MongoDB.Driver.Tests/MongoBulkWriteExceptionTests.cs @@ -149,9 +149,11 @@ public void Serialization_should_work() var formatter = new BinaryFormatter(); using (var stream = new MemoryStream()) { +#pragma warning disable SYSLIB0011 // BinaryFormatter serialization is obsolete formatter.Serialize(stream, subject); stream.Position = 0; var rehydrated = (MongoBulkWriteException)formatter.Deserialize(stream); +#pragma warning restore SYSLIB0011 // BinaryFormatter serialization is obsolete rehydrated.ConnectionId.Should().Be(subject.ConnectionId); rehydrated.ErrorLabels.Should().BeEquivalentTo(subject.ErrorLabels); diff --git a/tests/MongoDB.Driver.Tests/MongoClientSettingsTests.cs b/tests/MongoDB.Driver.Tests/MongoClientSettingsTests.cs index 1adb46b646e..ed64f7d05ea 100644 --- a/tests/MongoDB.Driver.Tests/MongoClientSettingsTests.cs +++ b/tests/MongoDB.Driver.Tests/MongoClientSettingsTests.cs @@ -14,17 +14,23 @@ */ using System; +using System.Collections.Generic; using System.Linq; using System.Net.Sockets; using System.Security.Authentication; +using System.Text; using System.Threading; using FluentAssertions; +using Microsoft.Extensions.Logging; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.Bson.IO; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Compression; using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; +using MongoDB.Driver.Encryption; +using Moq; using Xunit; namespace MongoDB.Driver.Tests @@ -102,7 +108,9 @@ public void TestClone() settings.Credential = MongoCredential.CreateMongoCRCredential("database", "username", "password").WithMechanismProperty("SERVICE_NAME", "other"); #pragma warning restore 618 settings.SslSettings = new SslSettings { CheckCertificateRevocation = false }; +#pragma warning disable CS0618 // Type or member is obsolete settings.SdamLogFilename = "stdout"; +#pragma warning restore CS0618 // Type or member is obsolete settings.ServerApi = new ServerApi(ServerApiVersion.V1, true, true); var clone = settings.Clone(); @@ -277,7 +285,9 @@ public void TestDefaults() #pragma warning restore 618 Assert.Equal(MongoDefaults.WaitQueueTimeout, settings.WaitQueueTimeout); Assert.Equal(WriteConcern.Acknowledged, settings.WriteConcern); +#pragma warning disable CS0618 // Type or member is obsolete Assert.Equal(null, settings.SdamLogFilename); +#pragma warning restore CS0618 // Type or member is obsolete } [Fact] @@ -540,8 +550,31 @@ public void TestEquals() Assert.False(clone.Equals(settings)); clone = settings.Clone(); +#pragma warning disable CS0618 // Type or member is obsolete clone.SdamLogFilename = "garbage"; +#pragma warning restore CS0618 // Type or member is obsolete Assert.False(clone.Equals(settings)); + + // set non default values + settings.AutoEncryptionOptions = new AutoEncryptionOptions(CollectionNamespace.FromFullName("encryption.__keyVault"), new Dictionary>()); + settings.LoggingSettings = new LoggingSettings(null, 123); + settings.ReadConcern = ReadConcern.Majority; + settings.ReadEncoding = new UTF8Encoding(false, false); + settings.ServerApi = new ServerApi(ServerApiVersion.V1); + settings.WriteConcern = WriteConcern.W2; + settings.WriteEncoding = new UTF8Encoding(false, false); + + clone = settings.Clone(); + clone.AutoEncryptionOptions = clone.AutoEncryptionOptions.With(bypassAutoEncryption: settings.AutoEncryptionOptions.BypassAutoEncryption); + clone.LoggingSettings = new LoggingSettings(null, settings.LoggingSettings.MaxDocumentSize); + clone.ReadConcern = ReadConcern.FromBsonDocument(settings.ReadConcern.ToBsonDocument()); + clone.ReadEncoding = new UTF8Encoding(false, false); + clone.ReadPreference = clone.ReadPreference.With(settings.ReadPreference.ReadPreferenceMode); + clone.ServerApi = new ServerApi(settings.ServerApi.Version); + clone.WriteConcern = WriteConcern.FromBsonDocument(settings.WriteConcern.ToBsonDocument()); + clone.WriteEncoding = new UTF8Encoding(false, false); + + Assert.True(clone.Equals(settings)); } [Fact] @@ -926,6 +959,23 @@ public void TestLocalThreshold() Assert.Throws(() => { settings.LocalThreshold = localThreshold; }); } + [Fact] + public void TestLoggingSettings() + { + var settings = new MongoClientSettings(); + Assert.Equal(null, settings.LoggingSettings); + + settings.LoggingSettings = null; + Assert.Equal(null, settings.LoggingSettings); + + var loggerFactory = new Mock(); + settings.LoggingSettings = new LoggingSettings(loggerFactory.Object); + Assert.Equal(loggerFactory.Object, settings.LoggingSettings.LoggerFactory); + + settings.Freeze(); + Assert.Throws(() => { settings.LoggingSettings = new LoggingSettings(loggerFactory.Object); }); + } + [Fact] public void TestMaxConnecting() { @@ -1105,6 +1155,7 @@ public void TestScheme() Assert.Throws(() => { settings.Scheme = scheme; }); } +#pragma warning disable CS0618 // Type or member is obsolete [Fact] public void TestSdamLogFileName() { @@ -1119,6 +1170,7 @@ public void TestSdamLogFileName() Assert.Same(sdamLogFileName, settings.SdamLogFilename); Assert.Throws(() => { settings.SdamLogFilename = sdamLogFileName; }); } +#pragma warning restore CS0618 // Type or member is obsolete [Fact] public void TestServer() @@ -1213,7 +1265,7 @@ public void TestServersWithSrvMaxHosts() settings.Servers.Should().HaveCount(2); } - [SkippableFact] + [Fact] public void TestSocketConfigurator() { RequireServer.Check(); @@ -1390,6 +1442,10 @@ public void ToClusterKey_should_copy_relevant_values() EnabledSslProtocols = SslProtocols.Tls }; +#pragma warning disable CS0618 // Type or member is obsolete +#pragma warning disable CS0618 // Type or member is obsolete +#pragma warning disable CS0618 // Type or member is obsolete +#pragma warning disable CS0618 // Type or member is obsolete var subject = new MongoClientSettings { AllowInsecureTls = false, @@ -1404,6 +1460,7 @@ public void ToClusterKey_should_copy_relevant_values() HeartbeatTimeout = TimeSpan.FromSeconds(8), IPv6 = true, LocalThreshold = TimeSpan.FromMilliseconds(20), + LoggingSettings = new LoggingSettings(), MaxConnecting = 3, MaxConnectionIdleTime = TimeSpan.FromSeconds(2), MaxConnectionLifeTime = TimeSpan.FromSeconds(3), @@ -1411,7 +1468,9 @@ public void ToClusterKey_should_copy_relevant_values() MinConnectionPoolSize = 5, ReplicaSetName = "rs", Scheme = ConnectionStringScheme.MongoDB, +#pragma warning disable CS0618 // Type or member is obsolete SdamLogFilename = "pokédex", +#pragma warning restore CS0618 // Type or member is obsolete ServerApi = serverApi, Servers = servers, ServerSelectionTimeout = TimeSpan.FromSeconds(6), @@ -1423,6 +1482,10 @@ public void ToClusterKey_should_copy_relevant_values() #pragma warning restore 618 WaitQueueTimeout = TimeSpan.FromSeconds(5) }; +#pragma warning restore CS0618 // Type or member is obsolete +#pragma warning restore CS0618 // Type or member is obsolete +#pragma warning restore CS0618 // Type or member is obsolete +#pragma warning restore CS0618 // Type or member is obsolete #pragma warning disable 618 if (BsonDefaults.GuidRepresentationMode == GuidRepresentationMode.V2) { @@ -1446,6 +1509,7 @@ public void ToClusterKey_should_copy_relevant_values() result.HeartbeatTimeout.Should().Be(subject.HeartbeatTimeout); result.IPv6.Should().Be(subject.IPv6); result.LocalThreshold.Should().Be(subject.LocalThreshold); + result.LoggingSettings.Should().Be(subject.LoggingSettings); result.MaxConnecting.Should().Be(subject.MaxConnecting); result.MaxConnectionIdleTime.Should().Be(subject.MaxConnectionIdleTime); result.MaxConnectionLifeTime.Should().Be(subject.MaxConnectionLifeTime); @@ -1454,7 +1518,9 @@ public void ToClusterKey_should_copy_relevant_values() result.ReceiveBufferSize.Should().Be(MongoDefaults.TcpReceiveBufferSize); result.ReplicaSetName.Should().Be(subject.ReplicaSetName); result.Scheme.Should().Be(subject.Scheme); +#pragma warning disable CS0618 // Type or member is obsolete result.SdamLogFilename.Should().Be(subject.SdamLogFilename); +#pragma warning restore CS0618 // Type or member is obsolete result.SendBufferSize.Should().Be(MongoDefaults.TcpSendBufferSize); result.ServerApi.Should().Be(subject.ServerApi); result.Servers.Should().Equal(subject.Servers); diff --git a/tests/MongoDB.Driver.Tests/MongoClientTests.cs b/tests/MongoDB.Driver.Tests/MongoClientTests.cs index 2a943640a98..e20ade4d073 100644 --- a/tests/MongoDB.Driver.Tests/MongoClientTests.cs +++ b/tests/MongoDB.Driver.Tests/MongoClientTests.cs @@ -21,7 +21,7 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Clusters.ServerSelectors; diff --git a/tests/MongoDB.Driver.Tests/MongoCollectionImplTests.cs b/tests/MongoDB.Driver.Tests/MongoCollectionImplTests.cs index c3ceaba7e4b..d26bac52729 100644 --- a/tests/MongoDB.Driver.Tests/MongoCollectionImplTests.cs +++ b/tests/MongoDB.Driver.Tests/MongoCollectionImplTests.cs @@ -24,7 +24,7 @@ using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Connections; @@ -3721,7 +3721,7 @@ public void Watch_should_throw_when_pipeline_is_null( e.ParamName.Should().Be("pipeline"); } - [SkippableFact] + [Fact] public void Watch_should_support_full_document_with_duplicate_elements() { RequireServer.Check().ClusterTypes(ClusterType.ReplicaSet, ClusterType.Sharded); diff --git a/tests/MongoDB.Driver.Tests/MongoDB.Driver.Tests.csproj b/tests/MongoDB.Driver.Tests/MongoDB.Driver.Tests.csproj index 44bca72a9b8..6ec7987e80b 100644 --- a/tests/MongoDB.Driver.Tests/MongoDB.Driver.Tests.csproj +++ b/tests/MongoDB.Driver.Tests/MongoDB.Driver.Tests.csproj @@ -1,34 +1,16 @@ - - true - + - netcoreapp2.1;netcoreapp3.1;net472 - netcoreapp2.1;netcoreapp3.1 - 9 - true - - false ..\..\MongoDBTest.ruleset MongoDB.Driver.Tests MongoDB.Driver.Tests - MongoDB Inc. - Copyright © 2010-present MongoDB Inc. MongoDB.Driver tests. - - 0.0.0-local - - - - TRACE - - 1701;1702; @@ -42,22 +24,13 @@ - - $(DefineConstants);WINDOWS - - - - - - - @@ -71,19 +44,7 @@ - - - - - - - - - - - - - + @@ -95,8 +56,4 @@ - - - - diff --git a/tests/MongoDB.Driver.Tests/MongoDBRefTests.cs b/tests/MongoDB.Driver.Tests/MongoDBRefTests.cs index 2d9753d4aee..04faef99397 100644 --- a/tests/MongoDB.Driver.Tests/MongoDBRefTests.cs +++ b/tests/MongoDB.Driver.Tests/MongoDBRefTests.cs @@ -19,7 +19,7 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.Tests diff --git a/tests/MongoDB.Driver.Tests/MongoDatabaseImplTests.cs b/tests/MongoDB.Driver.Tests/MongoDatabaseImplTests.cs index 90710661ccf..b9cfbdba878 100644 --- a/tests/MongoDB.Driver.Tests/MongoDatabaseImplTests.cs +++ b/tests/MongoDB.Driver.Tests/MongoDatabaseImplTests.cs @@ -22,7 +22,7 @@ using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.Serialization.Serializers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Misc; @@ -897,7 +897,7 @@ public void ListCollectionNames_should_execute_a_ListCollectionsOperation( op.RetryRequested.Should().BeTrue(); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void ListCollectionNames_should_return_expected_result( [Values(0, 1, 2, 10)] int numberOfCollections, diff --git a/tests/MongoDB.Driver.Tests/MongoIndexManagerTests.cs b/tests/MongoDB.Driver.Tests/MongoIndexManagerTests.cs index 8b34b415117..ac5fbef4289 100644 --- a/tests/MongoDB.Driver.Tests/MongoIndexManagerTests.cs +++ b/tests/MongoDB.Driver.Tests/MongoIndexManagerTests.cs @@ -15,7 +15,7 @@ using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Operations; using Xunit; diff --git a/tests/MongoDB.Driver.Tests/MongoUrlBuilderTests.cs b/tests/MongoDB.Driver.Tests/MongoUrlBuilderTests.cs index a44a8dededf..9b16920c6e7 100644 --- a/tests/MongoDB.Driver.Tests/MongoUrlBuilderTests.cs +++ b/tests/MongoDB.Driver.Tests/MongoUrlBuilderTests.cs @@ -19,7 +19,7 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.IO; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Compression; using MongoDB.Driver.Core.Configuration; diff --git a/tests/MongoDB.Driver.Tests/MongoUrlTests.cs b/tests/MongoDB.Driver.Tests/MongoUrlTests.cs index 961daae3850..3f2c5759ba8 100644 --- a/tests/MongoDB.Driver.Tests/MongoUrlTests.cs +++ b/tests/MongoDB.Driver.Tests/MongoUrlTests.cs @@ -19,7 +19,7 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Compression; using MongoDB.Driver.Core.Configuration; diff --git a/tests/MongoDB.Driver.Tests/MongocryptdFactoryTests.cs b/tests/MongoDB.Driver.Tests/MongocryptdFactoryTests.cs index 82ee27c8e02..5364e974b1c 100644 --- a/tests/MongoDB.Driver.Tests/MongocryptdFactoryTests.cs +++ b/tests/MongoDB.Driver.Tests/MongocryptdFactoryTests.cs @@ -17,6 +17,7 @@ using MongoDB.Bson; using MongoDB.Bson.TestHelpers; using MongoDB.Driver.Encryption; +using MongoDB.Driver.Tests.Specifications.client_side_encryption; using System; using System.Collections.Generic; using System.Linq; @@ -62,7 +63,7 @@ public void ShouldMongocryptdBeSpawned_should_correctly_handle_bypassQueryAnalys shouldMongocryptdBeSpawned.Should().Be(shouldBeSpawned); } - [SkippableTheory] + [Theory] [InlineData("{ mongocryptdBypassSpawn : true }", null, null, false)] [InlineData(null, "mongocryptd#extension#", "--idleShutdownTimeoutSecs 60 --logpath #logpath# --logappend", true)] [InlineData("{ mongocryptdBypassSpawn : false }", "mongocryptd#extension#", "--idleShutdownTimeoutSecs 60 --logpath #logpath# --logappend", true)] @@ -83,6 +84,8 @@ public void ShouldMongocryptdBeSpawned_should_correctly_handle_bypassQueryAnalys [InlineData("{ mongocryptdSpawnArgs : ['arg1 A', 'arg2 B', '--logpath path.txt', '--logappend'] }", "mongocryptd#extension#", "--arg1 A --arg2 B --logpath path.txt --logappend --idleShutdownTimeoutSecs 60", true)] [InlineData("{ mongocryptdSpawnArgs : ['arg1 A', 'arg2 B', '--logappend'] }", "mongocryptd#extension#", "--arg1 A --arg2 B --logappend --idleShutdownTimeoutSecs 60 --logpath #logpath#", true)] [InlineData("{ mongocryptdBypassSpawn : false, mongocryptdSpawnArgs : [ '--arg1 A', '--arg2 B', '--idleShutdownTimeoutSecs 50'] }", "mongocryptd#extension#", "--arg1 A --arg2 B --idleShutdownTimeoutSecs 50 --logpath #logpath# --logappend", true)] + // with extra space + [InlineData("{ mongocryptdSpawnArgs : [' --port=27030' ] }", "mongocryptd#extension#", "--port=27030 --idleShutdownTimeoutSecs 60 --logpath #logpath# --logappend", true)] public void Mongocryptd_should_be_spawned_with_correct_extra_arguments( string stringExtraOptions, string expectedPath, diff --git a/tests/MongoDB.Driver.Tests/OcspIntegrationTests.cs b/tests/MongoDB.Driver.Tests/OcspIntegrationTests.cs index 2a119d1cd4f..9518f0c10f1 100644 --- a/tests/MongoDB.Driver.Tests/OcspIntegrationTests.cs +++ b/tests/MongoDB.Driver.Tests/OcspIntegrationTests.cs @@ -16,7 +16,7 @@ using System; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.TestHelpers.Logging; using MongoDB.Driver.TestHelpers; using Xunit; @@ -24,6 +24,7 @@ namespace MongoDB.Driver.Tests { + [Trait("Category", "OCSP")] public class OcspIntegrationTests : LoggableTestClass { private static readonly string _shouldSucceedEnvironmentVariableName = "OCSP_TLS_SHOULD_SUCCEED"; @@ -46,7 +47,7 @@ public OcspIntegrationTests(ITestOutputHelper testOutputHelper) * When testing on Windows, the certificate should be added to the trust store prior to each run in order to * reduce the chances of Windows pruning the certificate from the trust store prior to the test running. */ - [SkippableFact] + [Fact] public void MongoClientShouldRespectCertificateStatusAndTlsInsecure() { /* We cannot call RequireServer.Check() because this would result in a connection being made to the mongod @@ -70,7 +71,11 @@ public void MongoClientShouldRespectCertificateStatusAndTlsInsecure() secureClientException.Should().BeOfType(); var message = secureClientException.Message; // The exception will lack this message if the heartbeat doesn't fire +#if NET6_0_OR_GREATER + message.Should().Contain("The remote certificate is invalid because of errors in the certificate chain"); +#else message.Should().Contain("The remote certificate is invalid according to the validation procedure."); +#endif } void Ping(bool tlsInsecure) @@ -78,10 +83,12 @@ void Ping(bool tlsInsecure) using (var client = CreateDisposableMongoClient(tlsInsecure)) { client.GetDatabase("admin").RunCommand(new BsonDocument("ping", 1)); +#pragma warning disable CS0618 // Type or member is obsolete if (client.Settings.SdamLogFilename != null) { // Log file needs a bit of time to be written before we dispose the client System.Threading.Thread.Sleep(2000); } +#pragma warning restore CS0618 // Type or member is obsolete } } } @@ -105,6 +112,8 @@ private DisposableMongoClient CreateDisposableMongoClient(bool tlsInsecure) settings.ServerSelectionTimeout = TimeSpan.FromSeconds(5 * 2); // must be > 5s // settings.SdamLogFilename = @"C:\temp\sdam" + $"{tlsInsecure}.log"; + settings.LoggingSettings = LoggingSettings; + return new DisposableMongoClient(new MongoClient(settings), CreateLogger()); } diff --git a/tests/MongoDB.Driver.Tests/OfTypeMongoCollectionTests.cs b/tests/MongoDB.Driver.Tests/OfTypeMongoCollectionTests.cs index ae24e361aea..725764b2a0f 100644 --- a/tests/MongoDB.Driver.Tests/OfTypeMongoCollectionTests.cs +++ b/tests/MongoDB.Driver.Tests/OfTypeMongoCollectionTests.cs @@ -22,8 +22,13 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Attributes; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; +using MongoDB.Driver.Core; using MongoDB.Driver.Core.Bindings; +using MongoDB.Driver.Core.Events; +using MongoDB.Driver.Core.Misc; +using MongoDB.Driver.Core.TestHelpers.XunitExtensions; +using MongoDB.Driver.TestHelpers; using Moq; using Xunit; @@ -947,15 +952,21 @@ public class UpdateTestCases : IValueGenerator } } - public class OfTypeCollectionIntegrationTests + public class OfTypeCollectionIntegrationTests : IDisposable { - private IMongoCollection _docsCollection; - private IMongoCollection _rootCollection; + private readonly IMongoCollection _docsCollection; + private readonly EventCapturer _eventsCapturer; + private readonly IMongoCollection _rootCollection; + private readonly DisposableMongoClient _client; public OfTypeCollectionIntegrationTests() { - var client = DriverTestConfiguration.Client; - var db = client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName); + var clientSettings = DriverTestConfiguration.Client.Settings.Clone(); + _eventsCapturer = new EventCapturer().Capture(); + clientSettings.ClusterConfigurator = (b) => b.Subscribe(_eventsCapturer); + _client = DriverTestConfiguration.CreateDisposableClient(clientSettings); + + var db = _client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName); db.DropCollection(DriverTestConfiguration.CollectionNamespace.CollectionName); _docsCollection = db.GetCollection(DriverTestConfiguration.CollectionNamespace.CollectionName); @@ -1119,11 +1130,82 @@ public void ReplaceOne_should_match_document_of_right_type( }); } + [Theory] + [ParameterAttributeData] + public void UpdateOne_should_match_document_of_right_type( + [Values(false, true)] bool upsert, + [Values(false, true)] bool async) + { + RequireServer.Check().Supports(Feature.UpdateWithAggregationPipeline); + + string filter = "{ PropA : 4 }"; + + var subject = CreateSubject(); + + var pipeline = new EmptyPipelineDefinition() + .AppendStage( + new BsonDocument + { + { "$set", new BsonDocument(nameof(B.PropA), "Pipeline") } + }) + .AppendStage( + new BsonDocument + { + { "$unset", nameof(B.PropB) } + }); + + var updateOptions = new UpdateOptions { IsUpsert = upsert }; + var result = async + ? subject.UpdateOneAsync(filter, pipeline, updateOptions).GetAwaiter().GetResult() + : subject.UpdateOne(filter, pipeline, updateOptions); + + result.MatchedCount.Should().Be(1); + result.ModifiedCount.Should().Be(1); + + var updateQuery = _eventsCapturer.Events.OfType().Last().Command["updates"]; + var expectedUpdateDocument = BsonDocument.Parse($@" + {{ + ""q"" : + {{ + ""_t"" : ""B"", + ""PropA"" : 4 + }}, + ""u"" : + [ + {{ + ""$set"" : {{ ""PropA"" : ""Pipeline"" }} + }}, + {{ ""$unset"" : ""PropB"" }}, + {{ + ""$set"" : + {{ + ""_t"" : + {{ + ""$cond"" : [{{ ""$eq"" : [{{ ""$type"" : ""$_id"" }}, ""missing""] }}, + [""A"", ""B""], + ""$_t""] + }} + }} + }} + ], + ""upsert"" : true + }}"); + if (!upsert) + { + expectedUpdateDocument.RemoveAt(expectedUpdateDocument.ElementCount - 1); // remove upsert + var uContent = expectedUpdateDocument["u"].AsBsonArray; + uContent.RemoveAt(uContent.Count - 1); + } + updateQuery.Should().Be(new BsonArray { expectedUpdateDocument }); + } + private IMongoCollection CreateSubject() { return _rootCollection.OfType(); } + public void Dispose() => _client?.Dispose(); + [BsonDiscriminator(RootClass = true)] [BsonKnownTypes(typeof(B), typeof(C))] public class A diff --git a/tests/MongoDB.Driver.Tests/Packaging/PackagingTests.cs b/tests/MongoDB.Driver.Tests/Packaging/PackagingTests.cs index 39a1a85784e..31fddadd3f8 100644 --- a/tests/MongoDB.Driver.Tests/Packaging/PackagingTests.cs +++ b/tests/MongoDB.Driver.Tests/Packaging/PackagingTests.cs @@ -17,7 +17,6 @@ #if CONSOLE_TEST using System; #endif -using System.Reflection; using FluentAssertions; using MongoDB.Libmongocrypt; using Xunit; @@ -30,6 +29,7 @@ namespace MongoDB.Driver.Tests.Packaging /// Also, use only public classes or reflection here. /// [Trait("Category", "Packaging")] + [Trait("Category", "CSFLE")] public class PackagingTests { // keep these tests in sync with CONSOLE_TEST's Main method @@ -38,61 +38,7 @@ public static void Libmongocrypt_library_should_provide_library_version() { var version = Library.Version; - version.Should().Be("1.5.1"); - } - - [Fact] - public static void Snappy_compression_should_provide_snappy_max_compressed_length_x64() - { - var result = InvokeStaticMethod( - assemblyName: "MongoDB.Driver.Core", - className: "MongoDB.Driver.Core.Compression.Snappy.Snappy64NativeMethods", - methodName: "snappy_max_compressed_length", - arguments: (ulong)10); - - result.Should().Be((ulong)43); - } - - [Fact] - public static void Zstandard_compression_should_provide_MaxCompressionLevel() - { - var result = GetStaticFieldValue( - assemblyName: "MongoDB.Driver.Core", - className: "MongoDB.Driver.Core.Compression.Zstandard.ZstandardNativeWrapper", - fieldName: "MaxCompressionLevel"); - - result.Should().Be(22); - } - - // private methods - private static T GetStaticFieldValue(string assemblyName, string className, string fieldName) - { - try - { - var assembly = Assembly.Load(new AssemblyName(assemblyName)); - var @class = assembly.GetType(className, throwOnError: true).GetTypeInfo(); - var property = @class.GetProperty(fieldName, BindingFlags.Public | BindingFlags.Static); - return (T)property.GetValue(null); - } - catch (TargetInvocationException ex) - { - throw ex.InnerException; - } - } - - private static T InvokeStaticMethod(string assemblyName, string className, string methodName, params object[] arguments) - { - try - { - var assembly = Assembly.Load(new AssemblyName(assemblyName)); - var @class = assembly.GetType(className, throwOnError: true).GetTypeInfo(); - var method = @class.GetMethod(methodName, BindingFlags.Public | BindingFlags.Static); - return (T)method.Invoke(null, arguments); - } - catch (TargetInvocationException ex) - { - throw ex.InnerException; - } + version.Should().Be("1.7.0"); } #pragma warning disable IDE0051 // Remove unused private members @@ -100,10 +46,6 @@ private static void RunAllTests() { // Left it outside Main method to protect us from losing sync between xunit and console modes Libmongocrypt_library_should_provide_library_version(); - - Snappy_compression_should_provide_snappy_max_compressed_length_x64(); - - Zstandard_compression_should_provide_MaxCompressionLevel(); } #pragma warning restore IDE0051 // Remove unused private members diff --git a/tests/MongoDB.Driver.Tests/PasswordEvidenceTests.cs b/tests/MongoDB.Driver.Tests/PasswordEvidenceTests.cs index 1232fb7d9af..99ae74cea61 100644 --- a/tests/MongoDB.Driver.Tests/PasswordEvidenceTests.cs +++ b/tests/MongoDB.Driver.Tests/PasswordEvidenceTests.cs @@ -16,7 +16,7 @@ using System; using System.Security; using FluentAssertions; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.Tests diff --git a/tests/MongoDB.Driver.Tests/PinnedShardRouterTests.cs b/tests/MongoDB.Driver.Tests/PinnedShardRouterTests.cs index bfcb77bfb2c..0395102d27b 100644 --- a/tests/MongoDB.Driver.Tests/PinnedShardRouterTests.cs +++ b/tests/MongoDB.Driver.Tests/PinnedShardRouterTests.cs @@ -18,19 +18,21 @@ using System.Threading; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Events; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; +using MongoDB.Driver.Core.TestHelpers.Logging; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; using MongoDB.Driver.TestHelpers; using Xunit; +using Xunit.Abstractions; namespace MongoDB.Driver.Tests { - public class PinnedShardRouterTests + public class PinnedShardRouterTests : LoggableTestClass { private static readonly HashSet __commandsToNotCapture = new HashSet { @@ -46,11 +48,15 @@ public class PinnedShardRouterTests private string _collectionName = "test"; private string _databaseName = "test"; + public PinnedShardRouterTests(ITestOutputHelper output) : base(output) + { + } + /// /// Test that starting a new transaction on a pinned ClientSession unpins the /// session and normal server selection is performed for the next operation. /// - [SkippableTheory] + [Theory] [ParameterAttributeData] public async void Test_Unpin_For_Next_Transaction([Values(false, true)] bool async) { @@ -110,7 +116,7 @@ public async void Test_Unpin_For_Next_Transaction([Values(false, true)] bool asy /// Test non-transaction operations using a pinned ClientSession unpins the /// session and normal server selection is performed. /// - [SkippableTheory] + [Theory] [ParameterAttributeData] public async void Test_Unpin_For_Non_Transaction_Operation([Values(false, true)] bool async) { @@ -180,7 +186,7 @@ private DisposableMongoClient CreateDisposableClient(EventCapturer eventCapturer settings.ClusterConfigurator = c => c.Subscribe(eventCapturer); settings.LocalThreshold = TimeSpan.FromMilliseconds(1000); }, - logger: null, + LoggingSettings, useMultipleShardRouters); var timeOut = TimeSpan.FromSeconds(60); bool AllServersConnected() => client.Cluster.Description.Servers.All(s => s.State == ServerState.Connected); diff --git a/tests/MongoDB.Driver.Tests/PipelineDefinitionBuilderTests.cs b/tests/MongoDB.Driver.Tests/PipelineDefinitionBuilderTests.cs index 0d3b38123e9..a393565a60d 100644 --- a/tests/MongoDB.Driver.Tests/PipelineDefinitionBuilderTests.cs +++ b/tests/MongoDB.Driver.Tests/PipelineDefinitionBuilderTests.cs @@ -20,6 +20,7 @@ using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; +using MongoDB.Driver.Search; using Moq; using Xunit; @@ -82,7 +83,7 @@ public void ChangeStream_should_throw_when_pipeline_is_null() argumentNullException.ParamName.Should().Be("pipeline"); } - [SkippableFact] + [Fact] public void Lookup_should_throw_when_pipeline_is_null() { RequireServer.Check(); @@ -117,6 +118,156 @@ public void Merge_should_add_expected_stage() stages[0].Should().Be("{ $merge : { into : { db : 'database', coll : 'collection' } } }"); } + [Fact] + public void Search_should_add_expected_stage() + { + var pipeline = new EmptyPipelineDefinition(); + var builder = new SearchDefinitionBuilder(); + + var result = pipeline.Search(builder.Text("bar", "foo")); + + var stages = RenderStages(result, BsonDocumentSerializer.Instance); + stages[0].Should().Be("{ $search: { text: { query: 'foo', path: 'bar' } } }"); + } + + [Fact] + public void Search_should_add_expected_stage_with_highlight() + { + var pipeline = new EmptyPipelineDefinition(); + var builder = new SearchDefinitionBuilder(); + + var result = pipeline.Search(builder.Text("bar", "foo"), new SearchHighlightOptions("foo")); + + var stages = RenderStages(result, BsonDocumentSerializer.Instance); + stages[0].Should().BeEquivalentTo("{ $search: { text: { query: 'foo', path: 'bar' }, highlight: { path: 'foo' } } }"); + } + + [Fact] + public void Search_should_add_expected_stage_with_index() + { + var pipeline = new EmptyPipelineDefinition(); + var builder = new SearchDefinitionBuilder(); + + var result = pipeline.Search(builder.Text("bar", "foo"), indexName: "foo"); + + var stages = RenderStages(result, BsonDocumentSerializer.Instance); + stages[0].Should().Be("{ $search: { text: { query: 'foo', path: 'bar' }, index: 'foo' } }"); + } + + [Fact] + public void Search_should_add_expected_stage_with_count() + { + var pipeline = new EmptyPipelineDefinition(); + var builder = new SearchDefinitionBuilder(); + var count = new SearchCountOptions() + { + Type = SearchCountType.Total + }; + + var result = pipeline.Search(builder.Text("bar", "foo"), count: count); + + var stages = RenderStages(result, BsonDocumentSerializer.Instance); + stages[0].Should().Be("{ $search: { text: { query: 'foo', path: 'bar' }, count: { type: 'total' } } }"); + } + + [Fact] + public void Search_should_add_expected_stage_with_return_stored_source() + { + var pipeline = new EmptyPipelineDefinition(); + var builder = new SearchDefinitionBuilder(); + + var result = pipeline.Search(builder.Text("bar", "foo"), returnStoredSource: true); + + var stages = RenderStages(result, BsonDocumentSerializer.Instance); + stages[0].Should().Be("{ $search: { text: { query: 'foo', path: 'bar' }, returnStoredSource: true } }"); + } + + [Fact] + public void Search_should_throw_when_pipeline_is_null() + { + PipelineDefinition pipeline = null; + var builder = new SearchDefinitionBuilder(); + + var exception = Record.Exception(() => pipeline.Search(builder.Text("bar", "foo"))); + + exception.Should().BeOfType() + .Which.ParamName.Should().Be("pipeline"); + } + + [Fact] + public void Search_should_throw_when_query_is_null() + { + var pipeline = new EmptyPipelineDefinition(); + + var exception = Record.Exception(() => pipeline.Search(null)); + + exception.Should().BeOfType() + .Which.ParamName.Should().Be("searchDefinition"); + } + + [Fact] + public void SearchMeta_should_add_expected_stage() + { + var pipeline = new EmptyPipelineDefinition(); + var builder = new SearchDefinitionBuilder(); + + var result = pipeline.SearchMeta(builder.Text("bar", "foo")); + + var stages = RenderStages(result, BsonDocumentSerializer.Instance); + stages[0].Should().Be("{ $searchMeta: { text: { query: 'foo', path: 'bar' } } }"); + } + + [Fact] + public void SearchMeta_should_add_expected_stage_with_index() + { + var pipeline = new EmptyPipelineDefinition(); + var builder = new SearchDefinitionBuilder(); + + var result = pipeline.SearchMeta(builder.Text("bar", "foo"), indexName: "foo"); + + var stages = RenderStages(result, BsonDocumentSerializer.Instance); + stages[0].Should().Be("{ $searchMeta: { text: { query: 'foo', path: 'bar' }, index: 'foo' } }"); + } + + [Fact] + public void SearchMeta_should_add_expected_stage_with_count() + { + var pipeline = new EmptyPipelineDefinition(); + var builder = new SearchDefinitionBuilder(); + var count = new SearchCountOptions() + { + Type = SearchCountType.Total + }; + + var result = pipeline.SearchMeta(builder.Text("bar", "foo"), count: count); + + var stages = RenderStages(result, BsonDocumentSerializer.Instance); + stages[0].Should().Be("{ $searchMeta: { text: { query: 'foo', path: 'bar' }, count: { type: 'total' } } }"); + } + + [Fact] + public void SearchMeta_should_throw_when_pipeline_is_null() + { + PipelineDefinition pipeline = null; + var builder = new SearchDefinitionBuilder(); + + var exception = Record.Exception(() => pipeline.SearchMeta(builder.Text("bar", "foo"))); + + exception.Should().BeOfType() + .Which.ParamName.Should().Be("pipeline"); + } + + [Fact] + public void SearchMeta_should_throw_when_query_is_null() + { + var pipeline = new EmptyPipelineDefinition(); + + var exception = Record.Exception(() => pipeline.SearchMeta(null)); + + exception.Should().BeOfType() + .Which.ParamName.Should().Be("searchDefinition"); + } + [Fact] public void UnionWith_should_add_expected_stage() { diff --git a/tests/MongoDB.Driver.Tests/PipelineStageDefinitionBuilderTests.cs b/tests/MongoDB.Driver.Tests/PipelineStageDefinitionBuilderTests.cs index f54a2c34902..bb1596dff10 100644 --- a/tests/MongoDB.Driver.Tests/PipelineStageDefinitionBuilderTests.cs +++ b/tests/MongoDB.Driver.Tests/PipelineStageDefinitionBuilderTests.cs @@ -124,7 +124,7 @@ public void ChangeStream_should_return_the_expected_result_when_options_isNull() stage.Document.Should().Be("{ $changeStream : { } }"); } - [SkippableFact] + [Fact] public void GraphLookup_with_many_to_one_parameters_should_return_expected_result() { RequireServer.Check(); @@ -150,7 +150,7 @@ public void GraphLookup_with_many_to_one_parameters_should_return_expected_resul }"); } - [SkippableFact] + [Fact] public void GraphLookup_with_one_to_many_parameters_should_return_expected_result() { RequireServer.Check(); @@ -176,7 +176,7 @@ public void GraphLookup_with_one_to_many_parameters_should_return_expected_resul }"); } - [SkippableFact] + [Fact] public void GraphLookup_with_one_to_one_parameters_should_return_expected_result() { RequireServer.Check(); @@ -202,7 +202,7 @@ public void GraphLookup_with_one_to_one_parameters_should_return_expected_result }"); } - [SkippableFact] + [Fact] public void Lookup_with_let_should_return_the_expected_result() { RequireServer.Check(); @@ -261,7 +261,7 @@ public void Lookup_with_let_should_return_the_expected_result() }"); } - [SkippableFact] + [Fact] public void Lookup_without_optional_let_should_return_the_expected_result() { RequireServer.Check(); @@ -403,7 +403,7 @@ public class StockData public int Instock { get; set; } } - [SkippableFact] + [Fact] public void Lookup_with_entity_generic_params_should_return_the_expected_result() { RequireServer.Check(); @@ -461,7 +461,7 @@ public void Lookup_with_entity_generic_params_should_return_the_expected_result( }"); } - [SkippableFact] + [Fact] public void Lookup_with_empty_required_params_should_throw_expected_exception() { RequireServer.Check(); diff --git a/tests/MongoDB.Driver.Tests/ProjectionDefinitionBuilderTests.cs b/tests/MongoDB.Driver.Tests/ProjectionDefinitionBuilderTests.cs index 2ec73a71f5b..bc13efb3b01 100644 --- a/tests/MongoDB.Driver.Tests/ProjectionDefinitionBuilderTests.cs +++ b/tests/MongoDB.Driver.Tests/ProjectionDefinitionBuilderTests.cs @@ -18,6 +18,8 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver.Linq; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.Tests @@ -85,13 +87,20 @@ public void ElemMatch_from_filter() Assert(subject.Include("a.$"), "{'a.$': 1}"); } - [Fact] - public void ElemMatch_from_filter_Typed() + [Theory] + [ParameterAttributeData] + public void ElemMatch_from_filter_Typed( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { var subject = CreateSubject(); - Assert(subject.Include(x => x.Pets.ElementAt(-1)), "{'pets.$': 1}"); - Assert(subject.Include("Pets.$"), "{'pets.$': 1}"); + var projection = linqProvider == LinqProvider.V2 ? + subject.Include(x => x.Pets.ElementAt(-1)) : + subject.Include(x => x.Pets.FirstMatchingElement()); + Assert(projection, "{ 'pets.$' : 1 }", linqProvider); + + projection = subject.Include("Pets.$"); + Assert(projection, "{ 'pets.$' : 1 }", linqProvider); } [Fact] @@ -187,10 +196,10 @@ public void Slice_Typed_with_limit() Assert(subject.Slice("Pets", 10, 20), "{pets: {$slice: [10, 20]}}"); } - private void Assert(ProjectionDefinition projection, string expectedJson) + private void Assert(ProjectionDefinition projection, string expectedJson, LinqProvider linqProvider = LinqProvider.V3) { var documentSerializer = BsonSerializer.SerializerRegistry.GetSerializer(); - var renderedProjection = projection.Render(documentSerializer, BsonSerializer.SerializerRegistry); + var renderedProjection = projection.Render(documentSerializer, BsonSerializer.SerializerRegistry, linqProvider); renderedProjection.Should().Be(expectedJson); } diff --git a/tests/MongoDB.Driver.Tests/Properties/AssemblyInfo.cs b/tests/MongoDB.Driver.Tests/Properties/AssemblyInfo.cs index 8990b3d975f..d9eef727857 100644 --- a/tests/MongoDB.Driver.Tests/Properties/AssemblyInfo.cs +++ b/tests/MongoDB.Driver.Tests/Properties/AssemblyInfo.cs @@ -14,11 +14,11 @@ */ using System.Runtime.InteropServices; -using MongoDB.Driver.Core.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; [assembly: ComVisible(false)] [assembly: CollectionBehavior(DisableTestParallelization = true)] -[assembly: TestFramework(XunitExtensionsConsts.TimeoutEnforcingXunitFramework, XunitExtensionsConsts.TimeoutEnforcingFrameworkAssembly)] +[assembly: TestFramework(XunitExtensionsConstants.TimeoutEnforcingXunitFramework, XunitExtensionsConstants.TimeoutEnforcingFrameworkAssembly)] diff --git a/tests/MongoDB.Driver.Tests/ReadPreferenceOnStandaloneTests.cs b/tests/MongoDB.Driver.Tests/ReadPreferenceOnStandaloneTests.cs index 9852cd48cc7..14bd9987fca 100644 --- a/tests/MongoDB.Driver.Tests/ReadPreferenceOnStandaloneTests.cs +++ b/tests/MongoDB.Driver.Tests/ReadPreferenceOnStandaloneTests.cs @@ -18,18 +18,24 @@ using System.Threading; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Events; using MongoDB.Driver.Core.Servers; +using MongoDB.Driver.Core.TestHelpers.Logging; using MongoDB.Driver.TestHelpers; using Xunit; +using Xunit.Abstractions; namespace MongoDB.Driver.Tests { - public class ReadPreferenceOnStandaloneTests + public class ReadPreferenceOnStandaloneTests : LoggableTestClass { + public ReadPreferenceOnStandaloneTests(ITestOutputHelper output) : base(output) + { + } + [Theory] [ParameterAttributeData] public void ReadPreference_should_not_be_sent_to_standalone_server( @@ -73,7 +79,7 @@ private DisposableMongoClient CreateDisposableClient(EventCapturer eventCapturer settings.ClusterConfigurator = c => c.Subscribe(eventCapturer); settings.ReadPreference = readPreference; }, - logger: null); + LoggingSettings); } } } diff --git a/tests/MongoDB.Driver.Tests/RegisterObjectSerializerCollection.cs b/tests/MongoDB.Driver.Tests/RegisterObjectSerializerCollection.cs new file mode 100644 index 00000000000..69d2852f291 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/RegisterObjectSerializerCollection.cs @@ -0,0 +1,25 @@ +/* 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 MongoDB.Bson.TestHelpers; +using Xunit; + +namespace MongoDB.Driver.Tests +{ + [CollectionDefinition(RegisterObjectSerializerFixture.CollectionName)] + public sealed class RegisterObjectSerializerCollection : ICollectionFixture + { + } +} diff --git a/tests/MongoDB.Driver.Tests/RetryableWritesTests.cs b/tests/MongoDB.Driver.Tests/RetryableWritesTests.cs index daff790fe58..6472458726e 100644 --- a/tests/MongoDB.Driver.Tests/RetryableWritesTests.cs +++ b/tests/MongoDB.Driver.Tests/RetryableWritesTests.cs @@ -40,7 +40,7 @@ public RetryableWritesTests(ITestOutputHelper output) : } // public methods - [SkippableFact] + [Fact] public void Insert_with_RetryWrites_true_should_work_whether_retryable_writes_are_supported_or_not() { RequireServer.Check(); @@ -54,7 +54,7 @@ public void Insert_with_RetryWrites_true_should_work_whether_retryable_writes_ar } } - [SkippableFact] + [Fact] public void Retryable_write_errorlabel_should_not_be_added_with_retryWrites_false() { if (CoreTestConfiguration.Cluster.Description.Type == ClusterType.Sharded) @@ -96,7 +96,7 @@ public void Retryable_write_errorlabel_should_not_be_added_with_retryWrites_fals } } - [SkippableFact] + [Fact] public void Retryable_write_operation_should_throw_custom_exception_on_servers_using_mmapv1() { RequireSupportForRetryableWrites(); @@ -116,7 +116,7 @@ public void Retryable_write_operation_should_throw_custom_exception_on_servers_u } } - [SkippableFact] + [Fact] public void TxnNumber_should_be_included_with_FindOneAndDelete() { RequireSupportForRetryableWrites(); @@ -135,7 +135,7 @@ public void TxnNumber_should_be_included_with_FindOneAndDelete() } } - [SkippableFact] + [Fact] public void TxnNumber_should_be_included_with_FindOneAndReplace() { RequireSupportForRetryableWrites(); @@ -154,7 +154,7 @@ public void TxnNumber_should_be_included_with_FindOneAndReplace() } } - [SkippableFact] + [Fact] public void TxnNumber_should_be_included_with_FindOneAndUpdate() { RequireSupportForRetryableWrites(); @@ -173,7 +173,7 @@ public void TxnNumber_should_be_included_with_FindOneAndUpdate() } } - [SkippableFact] + [Fact] public void TxnNumber_should_be_included_with_DeleteOne() { RequireSupportForRetryableWrites(); @@ -192,7 +192,7 @@ public void TxnNumber_should_be_included_with_DeleteOne() } } - [SkippableFact] + [Fact] public void TxnNumber_should_be_included_with_InsertOne() { RequireSupportForRetryableWrites(); @@ -211,7 +211,7 @@ public void TxnNumber_should_be_included_with_InsertOne() } } - [SkippableFact] + [Fact] public void TxnNumber_should_be_included_with_ReplaceOne() { RequireSupportForRetryableWrites(); @@ -230,7 +230,7 @@ public void TxnNumber_should_be_included_with_ReplaceOne() } } - [SkippableFact] + [Fact] public void TxnNumber_should_be_included_with_UpdateOne() { RequireSupportForRetryableWrites(); @@ -262,7 +262,7 @@ private DisposableMongoClient GetClient(Action clusterConfigurat clientSettings.ClusterConfigurator = clusterConfigurator; clientSettings.RetryWrites = retryWrites; }, - logger: CreateLogger()); + LoggingSettings); } private DisposableMongoClient GetClient(EventCapturer capturer) diff --git a/tests/MongoDB.Driver.Tests/Samples/AggregationSample.cs b/tests/MongoDB.Driver.Tests/Samples/AggregationSample.cs index d19ea04bd7b..bd732baf0f2 100644 --- a/tests/MongoDB.Driver.Tests/Samples/AggregationSample.cs +++ b/tests/MongoDB.Driver.Tests/Samples/AggregationSample.cs @@ -18,6 +18,8 @@ using FluentAssertions; using MongoDB.Bson.Serialization.Attributes; using MongoDB.Driver.Linq; +using MongoDB.Driver.Tests.Linq.Linq3ImplementationTests; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.Tests.Samples @@ -59,26 +61,40 @@ public bool OneTimeSetup() return true; } - [Fact] - public async Task States_with_pops_over_20000() + [Theory] + [ParameterAttributeData] + public async Task States_with_pops_over_20000( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { - var pipeline = __collection.Aggregate() + var collection = GetCollection(linqProvider); + var pipeline = collection.Aggregate() .Group(x => x.State, g => new { State = g.Key, TotalPopulation = g.Sum(x => x.Population) }) .Match(x => x.TotalPopulation > 20000); - pipeline.ToString().Should().Be("aggregate([" + - "{ \"$group\" : { \"_id\" : \"$state\", \"TotalPopulation\" : { \"$sum\" : \"$pop\" } } }, " + - "{ \"$match\" : { \"TotalPopulation\" : { \"$gt\" : 20000 } } }])"); + var pipelineTranslation = pipeline.ToString(); + var expectedTranslation = linqProvider == LinqProvider.V2 ? + "aggregate([" + + "{ \"$group\" : { \"_id\" : \"$state\", \"TotalPopulation\" : { \"$sum\" : \"$pop\" } } }, " + + "{ \"$match\" : { \"TotalPopulation\" : { \"$gt\" : 20000 } } }])" + : + "aggregate([" + + "{ \"$group\" : { \"_id\" : \"$state\", \"__agg0\" : { \"$sum\" : \"$pop\" } } }, " + + "{ \"$project\" : { \"State\" : \"$_id\", \"TotalPopulation\" : \"$__agg0\", \"_id\" : 0 } }, " + + "{ \"$match\" : { \"TotalPopulation\" : { \"$gt\" : 20000 } } }])"; + pipelineTranslation.Should().Be(expectedTranslation); var result = await pipeline.ToListAsync(); result.Count.Should().Be(1); } - [Fact] - public async Task States_with_pops_over_20000_queryable_method() + [Theory] + [ParameterAttributeData] + public async Task States_with_pops_over_20000_queryable_method( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { - var pipeline = __collection.AsQueryable() + var collection = GetCollection(linqProvider); + var pipeline = collection.AsQueryable() .GroupBy(x => x.State, (k, s) => new { State = k, TotalPopulation = s.Sum(x => x.Population) }) .Where(x => x.TotalPopulation > 20000); @@ -88,32 +104,67 @@ public async Task States_with_pops_over_20000_queryable_method() } #if !MONO - [Fact] - public async Task States_with_pops_over_20000_queryable_syntax() + [Theory] + [ParameterAttributeData] + public void States_with_pops_over_20000_queryable_syntax( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { - var pipeline = from z in __collection.AsQueryable() + var client = DriverTestConfiguration.GetLinqClient(linqProvider); + var database = client.GetDatabase(__collection.CollectionNamespace.DatabaseNamespace.DatabaseName); + var collection = database.GetCollection(__collection.CollectionNamespace.CollectionName); + + var queryable = from z in collection.AsQueryable() group z by z.State into g where g.Sum(x => x.Population) > 20000 select new { State = g.Key, TotalPopulation = g.Sum(x => x.Population) }; - var result = await pipeline.ToListAsync(); + var stages = Linq3TestHelpers.Translate(collection, queryable); + var expectedStages = linqProvider == LinqProvider.V2 ? + new[] + { + "{ $group : { _id : '$state', __agg0 : { $sum : '$pop' } } }", + "{ $match : { __agg0 : { $gt : 20000 } } }", + "{ $project : { State : '$_id', TotalPopulation : '$__agg0', _id : 0 } }" + } + : + new[] + { + "{ $group : { _id : '$state', __agg0 : { $sum : '$pop' } } }", + "{ $match : { $expr : { $gt : ['$__agg0', 20000] } } }", + "{ $project : { State : '$_id', TotalPopulation : '$__agg0', _id : 0 } }" + }; + Linq3TestHelpers.AssertStages(stages, expectedStages); + var result = queryable.ToList(); result.Count.Should().Be(1); } #endif - [Fact] - public async Task Average_city_population_by_state() + [Theory] + [ParameterAttributeData] + public async Task Average_city_population_by_state( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { - var pipeline = __collection.Aggregate() + var collection = GetCollection(linqProvider); + var pipeline = collection.Aggregate() .Group(x => new { State = x.State, City = x.City }, g => new { StateAndCity = g.Key, Population = g.Sum(x => x.Population) }) .Group(x => x.StateAndCity.State, g => new { State = g.Key, AverageCityPopulation = g.Average(x => x.Population) }) .SortBy(x => x.State); - pipeline.ToString().Should().Be("aggregate([" + + var pipelineTranslation = pipeline.ToString(); + var expectedTranslation = linqProvider == LinqProvider.V2 ? + "aggregate([" + "{ \"$group\" : { \"_id\" : { \"State\" : \"$state\", \"City\" : \"$city\" }, \"Population\" : { \"$sum\" : \"$pop\" } } }, " + "{ \"$group\" : { \"_id\" : \"$_id.State\", \"AverageCityPopulation\" : { \"$avg\" : \"$Population\" } } }, " + - "{ \"$sort\" : { \"_id\" : 1 } }])"); + "{ \"$sort\" : { \"_id\" : 1 } }])" + : + "aggregate([" + + "{ \"$group\" : { \"_id\" : { \"State\" : \"$state\", \"City\" : \"$city\" }, \"__agg0\" : { \"$sum\" : \"$pop\" } } }, " + + "{ \"$project\" : { \"StateAndCity\" : \"$_id\", \"Population\" : \"$__agg0\", \"_id\" : 0 } }, " + + "{ \"$group\" : { \"_id\" : \"$StateAndCity.State\", \"__agg0\" : { \"$avg\" : \"$Population\" } } }, " + + "{ \"$project\" : { \"State\" : \"$_id\", \"AverageCityPopulation\" : \"$__agg0\", \"_id\" : 0 } }, " + + "{ \"$sort\" : { \"State\" : 1 } }])"; + pipelineTranslation.Should().Be(expectedTranslation); var result = await pipeline.ToListAsync(); @@ -123,10 +174,13 @@ public async Task Average_city_population_by_state() result[1].State.Should().Be("MA"); } - [Fact] - public async Task Largest_and_smallest_cities_by_state() + [Theory] + [ParameterAttributeData] + public async Task Largest_and_smallest_cities_by_state( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { - var pipeline = __collection.Aggregate() + var collection = GetCollection(linqProvider); + var pipeline = collection.Aggregate() .Group(x => new { State = x.State, City = x.City }, g => new { StateAndCity = g.Key, Population = g.Sum(x => x.Population) }) .SortBy(x => x.Population) .Group(x => x.StateAndCity.State, g => new @@ -145,12 +199,24 @@ public async Task Largest_and_smallest_cities_by_state() }) .SortBy(x => x.State); - pipeline.ToString().Should().Be("aggregate([" + + var pipelineTranslation = pipeline.ToString(); + var expectedTranslation = linqProvider == LinqProvider.V2 ? + "aggregate([" + "{ \"$group\" : { \"_id\" : { \"State\" : \"$state\", \"City\" : \"$city\" }, \"Population\" : { \"$sum\" : \"$pop\" } } }, " + "{ \"$sort\" : { \"Population\" : 1 } }, " + "{ \"$group\" : { \"_id\" : \"$_id.State\", \"BiggestCity\" : { \"$last\" : \"$_id.City\" }, \"BiggestPopulation\" : { \"$last\" : \"$Population\" }, \"SmallestCity\" : { \"$first\" : \"$_id.City\" }, \"SmallestPopulation\" : { \"$first\" : \"$Population\" } } }, " + "{ \"$project\" : { \"State\" : \"$_id\", \"BiggestCity\" : { \"Name\" : \"$BiggestCity\", \"Population\" : \"$BiggestPopulation\" }, \"SmallestCity\" : { \"Name\" : \"$SmallestCity\", \"Population\" : \"$SmallestPopulation\" }, \"_id\" : 0 } }, " + - "{ \"$sort\" : { \"State\" : 1 } }])"); + "{ \"$sort\" : { \"State\" : 1 } }])" + : + "aggregate([" + + "{ \"$group\" : { \"_id\" : { \"State\" : \"$state\", \"City\" : \"$city\" }, \"__agg0\" : { \"$sum\" : \"$pop\" } } }, " + + "{ \"$project\" : { \"StateAndCity\" : \"$_id\", \"Population\" : \"$__agg0\", \"_id\" : 0 } }, " + + "{ \"$sort\" : { \"Population\" : 1 } }, " + + "{ \"$group\" : { \"_id\" : \"$StateAndCity.State\", \"__agg0\" : { \"$last\" : \"$$ROOT\" }, \"__agg1\" : { \"$first\" : \"$$ROOT\" } } }, " + + "{ \"$project\" : { \"State\" : \"$_id\", \"BiggestCity\" : \"$__agg0.StateAndCity.City\", \"BiggestPopulation\" : \"$__agg0.Population\", \"SmallestCity\" : \"$__agg1.StateAndCity.City\", \"SmallestPopulation\" : \"$__agg1.Population\", \"_id\" : 0 } }, " + + "{ \"$project\" : { \"State\" : \"$State\", \"BiggestCity\" : { \"Name\" : \"$BiggestCity\", \"Population\" : \"$BiggestPopulation\" }, \"SmallestCity\" : { \"Name\" : \"$SmallestCity\", \"Population\" : \"$SmallestPopulation\" }, \"_id\" : 0 } }, " + + "{ \"$sort\" : { \"State\" : 1 } }])"; + pipelineTranslation.Should().Be(expectedTranslation); var result = await pipeline.ToListAsync(); @@ -201,6 +267,13 @@ orderby g.Key } #endif + private IMongoCollection GetCollection(LinqProvider linqProvider) + { + var client = DriverTestConfiguration.GetLinqClient(linqProvider); + var database = client.GetDatabase(__collection.Database.DatabaseNamespace.DatabaseName); + return database.GetCollection(__collection.CollectionNamespace.CollectionName); + } + [BsonIgnoreExtraElements] private class ZipEntry { diff --git a/tests/MongoDB.Driver.Tests/Search/AtlasSearchTests.cs b/tests/MongoDB.Driver.Tests/Search/AtlasSearchTests.cs new file mode 100644 index 00000000000..18a4b69a40c --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Search/AtlasSearchTests.cs @@ -0,0 +1,518 @@ +/* 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.Core.Misc; +using MongoDB.Driver.Core.TestHelpers.Logging; +using MongoDB.Driver.GeoJsonObjectModel; +using MongoDB.Driver.Search; +using MongoDB.Driver.TestHelpers; +using MongoDB.TestHelpers.XunitExtensions; +using Xunit; +using Xunit.Abstractions; +using Builders = MongoDB.Driver.Builders; +using GeoBuilders = MongoDB.Driver.Builders; + +namespace MongoDB.Driver.Tests.Search +{ + [Trait("Category", "AtlasSearch")] + public class AtlasSearchTests : LoggableTestClass + { + #region static + + private static readonly GeoJsonPolygon __testPolygon = + new(new(new(new GeoJson2DGeographicCoordinates[] + { + new(-8.6131, 41.14), + new(-8.6131, 41.145), + new(-8.60308, 41.145), + new(-8.60308, 41.14), + new(-8.6131, 41.14), + }))); + + private static readonly GeoWithinBox __testBox = + new(new(new(-8.6131, 41.14)), new(new(-8.60308, 41.145))); + + private static readonly GeoWithinCircle __testCircle = + new(new(new(-8.61308, 41.1413)), 273); + + #endregion + + private readonly DisposableMongoClient _disposableMongoClient; + + public AtlasSearchTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + RequireEnvironment.Check().EnvironmentVariable("ATLAS_SEARCH_TESTS_ENABLED"); + + var atlasSearchUri = Environment.GetEnvironmentVariable("ATLAS_SEARCH"); + Ensure.IsNotNullOrEmpty(atlasSearchUri, nameof(atlasSearchUri)); + + _disposableMongoClient = new(new MongoClient(atlasSearchUri), CreateLogger()); + } + + protected override void DisposeInternal() => _disposableMongoClient.Dispose(); + + [Fact] + public void Autocomplete() + { + var result = SearchSingle(Builders.Search.Autocomplete(x => x.Title, "Declaration of Ind")); + + result.Title.Should().Be("Declaration of Independence"); + } + + [Fact] + public void Compound() + { + var result = SearchSingle(Builders.Search.Compound() + .Must(Builders.Search.Text(x => x.Body, "life"), Builders.Search.Text(x => x.Body, "liberty")) + .MustNot(Builders.Search.Text(x => x.Body, "property")) + .Must(Builders.Search.Text(x => x.Body, "pursuit of happiness"))); + + result.Title.Should().Be("Declaration of Independence"); + } + + [Fact] + public void Count_total() + { + var results = GetTestCollection().Aggregate() + .Search( + Builders.Search.Phrase(x => x.Body, "life, liberty, and the pursuit of happiness"), + count: new SearchCountOptions() + { + Type = SearchCountType.Total + }) + .Project(Builders.Projection.SearchMeta(x => x.MetaResult)) + .Limit(1) + .ToList(); + results.Should().ContainSingle().Which.MetaResult.Count.Total.Should().Be(108); + } + + [Fact] + public void Exists() + { + var result = SearchSingle( + Builders.Search.Compound().Must( + Builders.Search.Text(x => x.Body, "life, liberty, and the pursuit of happiness"), + Builders.Search.Exists(x => x.Title))); + + result.Title.Should().Be("Declaration of Independence"); + } + + [Fact] + public void Filter() + { + var result = SearchSingle( + Builders.Search.Compound().Filter( + Builders.Search.Phrase(x => x.Body, "life, liberty"), + Builders.Search.Wildcard(x => x.Body, "happ*", true))); + + result.Title.Should().Be("Declaration of Independence"); + } + + [Theory] + [InlineData("add")] + [InlineData("constant")] + [InlineData("gauss")] + [InlineData("log")] + [InlineData("log1p")] + [InlineData("multiply")] + [InlineData("path")] + [InlineData("relevance")] + public void FunctionScore(string functionScoreType) + { + var scoreFunction = functionScoreType switch + { + "add" => Builders.SearchScoreFunction.Add(Constant(1), Constant(2)), + "constant" => Constant(1), + "gauss" => Builders.SearchScoreFunction.Gauss(x => x.Score, 100, 1, 0.1, 1), + "log" => Builders.SearchScoreFunction.Log(Constant(1)), + "log1p" => Builders.SearchScoreFunction.Log1p(Constant(1)), + "multiply" => Builders.SearchScoreFunction.Multiply(Constant(1), Constant(2)), + "path" => Builders.SearchScoreFunction.Path(x => x.Score, 1), + "relevance" => Builders.SearchScoreFunction.Relevance(), + _ => throw new ArgumentOutOfRangeException(nameof(functionScoreType), functionScoreType, "Invalid score function") + }; + + var result = SearchSingle(Builders.Search.Phrase( + x => x.Body, + "life, liberty, and the pursuit of happiness", + score: Builders.SearchScore.Function(scoreFunction))); + + result.Title.Should().Be("Declaration of Independence"); + + SearchScoreFunction Constant(double value) => + Builders.SearchScoreFunction.Constant(value); + } + + [Fact] + public void GeoShape() + { + var results = GeoSearch( + GeoBuilders.Search.GeoShape( + x => x.Address.Location, + GeoShapeRelation.Intersects, + __testPolygon)); + + results.Count.Should().Be(25); + results.First().Name.Should().Be("Ribeira Charming Duplex"); + } + + [Theory] + [InlineData("box")] + [InlineData("circle")] + [InlineData("polygon")] + public void GeoWithin(string geometryType) + { + GeoWithinArea geoArea = geometryType switch + { + "box" => __testBox, + "circle" => __testCircle, + "polygon" => new GeoWithinGeometry(__testPolygon), + _ => throw new ArgumentOutOfRangeException(nameof(geometryType), geometryType, "Invalid geometry type") + }; + + var results = GeoSearch(GeoBuilders.Search.GeoWithin(x => x.Address.Location, geoArea)); + + results.Count.Should().Be(25); + results.First().Name.Should().Be("Ribeira Charming Duplex"); + } + + [Fact] + public void MoreLikeThis() + { + var likeThisDocument = new HistoricalDocument + { + Title = "Declaration of Independence", + Body = "We hold these truths to be self-evident that all men are created equal..." + }; + var result = SearchSingle(Builders.Search.MoreLikeThis(likeThisDocument)); + + result.Title.Should().Be("Declaration of Independence"); + } + + [Fact] + public void Must() + { + var result = SearchSingle( + Builders.Search.Compound().Must( + Builders.Search.Phrase(x => x.Body, "life, liberty"), + Builders.Search.Wildcard(x => x.Body, "happ*", true))); + + result.Title.Should().Be("Declaration of Independence"); + } + + [Fact] + public void MustNot() + { + var result = SearchSingle( + Builders.Search.Compound().MustNot( + Builders.Search.Phrase(x => x.Body, "life, liberty"))); + result.Title.Should().Be("US Constitution"); + } + + [Fact] + public void Near() + { + var results = GetGeoTestCollection().Aggregate() + .Search(GeoBuilders.Search.Near(x => x.Address.Location, __testCircle.Center, 1000)) + .Limit(1) + .ToList(); + + results.Should().ContainSingle().Which.Name.Should().Be("Ribeira Charming Duplex"); + } + + [Fact] + public void Phrase() + { + // This test case exercises the indexName and returnStoredSource arguments. The + // remaining test cases omit them. + var coll = GetTestCollection(); + var results = GetTestCollection().Aggregate() + .Search(Builders.Search.Phrase(x => x.Body, "life, liberty, and the pursuit of happiness"), + new SearchHighlightOptions(x => x.Body), + indexName: "default", + returnStoredSource: true) + .Limit(1) + .Project(Builders.Projection + .Include(x => x.Title) + .Include(x => x.Body) + .MetaSearchScore("score") + .MetaSearchHighlights("highlights")) + .ToList(); + + var result = results.Should().ContainSingle().Subject; + result.Title.Should().Be("Declaration of Independence"); + result.Score.Should().NotBe(0); + + var highlightTexts = result.Highlights.Should().ContainSingle().Subject.Texts; + highlightTexts.Should().HaveCount(15); + + foreach (var highlight in highlightTexts) + { + var expectedType = char.IsLetter(highlight.Value[0]) ? HighlightTextType.Hit : HighlightTextType.Text; + highlight.Type.Should().Be(expectedType); + } + + var highlightRangeStr = string.Join(string.Empty, highlightTexts.Skip(1).Select(x => x.Value)); + highlightRangeStr.Should().Be("Life, Liberty and the pursuit of Happiness."); + } + + [Fact] + public void PhraseMultiPath() + { + var result = SearchSingle( + Builders.Search.Phrase( + Builders.SearchPath.Multi(x => x.Title, x => x.Body), + "life, liberty, and the pursuit of happiness")); + + result.Title.Should().Be("Declaration of Independence"); + } + + [Fact] + public void PhraseAnalyzerPath() + { + var result = SearchSingle( + Builders.Search.Phrase( + Builders.SearchPath.Analyzer(x => x.Body, "english"), + "life, liberty, and the pursuit of happiness")); + + result.Title.Should().Be("Declaration of Independence"); + } + + [Fact] + public void PhraseWildcardPath() + { + var result = SearchSingle( + Builders.Search.Phrase( + Builders.SearchPath.Wildcard("b*"), + "life, liberty, and the pursuit of happiness")); + + result.Title.Should().Be("Declaration of Independence"); + } + + [Fact] + public void QueryString() + { + var result = SearchSingle(Builders.Search.QueryString(x => x.Body, "life, liberty, and the pursuit of happiness")); + + result.Title.Should().Be("Declaration of Independence"); + } + + [Fact] + public void Range() + { + var results = GeoSearch( + GeoBuilders.Search.Compound().Must( + GeoBuilders.Search.Range(x => x.Bedrooms, SearchRangeBuilder.Gt(2).Lt(4)), + GeoBuilders.Search.Range(x => x.Beds, SearchRangeBuilder.Gte(14).Lte(14)))); + + results.Should().ContainSingle().Which.Name.Should().Be("House close to station & direct to opera house...."); + } + + [Fact] + public void Search_count_lowerBound() + { + var results = GetTestCollection().Aggregate() + .Search( + Builders.Search.Phrase(x => x.Body, "life, liberty, and the pursuit of happiness"), + count: new SearchCountOptions() + { + Type = SearchCountType.LowerBound, + Threshold = 128 + }) + .Project(Builders.Projection.SearchMeta(x => x.MetaResult)) + .Limit(1) + .ToList(); + results.Should().ContainSingle().Which.MetaResult.Count.LowerBound.Should().Be(108); + } + + [Fact] + public void SearchMeta_count() + { + var result = GetTestCollection().Aggregate() + .SearchMeta( + Builders.Search.Phrase(x => x.Body, "life, liberty, and the pursuit of happiness"), + "default", + new SearchCountOptions() { Type = SearchCountType.Total }) + .Single(); + + result.Should().NotBeNull(); + result.Count.Should().NotBeNull(); + result.Count.Total.Should().Be(108); + } + + [Fact] + public void SearchMeta_facet() + { + var result = GetTestCollection().Aggregate() + .SearchMeta(Builders.Search.Facet( + Builders.Search.Phrase(x => x.Body, "life, liberty, and the pursuit of happiness"), + Builders.SearchFacet.String("string", x => x.Author, 100), + Builders.SearchFacet.Number("number", x => x.Index, 0, 100), + Builders.SearchFacet.Date("date", x => x.Date, DateTime.MinValue, DateTime.MaxValue))) + .Single(); + + result.Should().NotBeNull(); + + var bucket = result.Facet["string"].Buckets.Should().NotBeNull().And.ContainSingle().Subject; + bucket.Id.Should().Be((BsonString)"machine"); + bucket.Count.Should().Be(108); + + bucket = result.Facet["number"].Buckets.Should().NotBeNull().And.ContainSingle().Subject; + bucket.Id.Should().Be((BsonInt32)0); + bucket.Count.Should().Be(0); + + bucket = result.Facet["date"].Buckets.Should().NotBeNull().And.ContainSingle().Subject; + bucket.Id.Should().Be((BsonDateTime)DateTime.MinValue); + bucket.Count.Should().Be(108); + } + + [Fact] + public void Should() + { + var result = SearchSingle( + Builders.Search.Compound().Should( + Builders.Search.Phrase(x => x.Body, "life, liberty"), + Builders.Search.Wildcard(x => x.Body, "happ*", true)) + .MinimumShouldMatch(2)); + result.Title.Should().Be("Declaration of Independence"); + } + + [Theory] + [InlineData("first")] + [InlineData("near")] + [InlineData("or")] + [InlineData("subtract")] + public void Span(string spanType) + { + var spanDefinition = spanType switch + { + "first" => Builders.SearchSpan.First(Term("happiness"), 250), + "near" => Builders.SearchSpan.Near(new[] { Term("life"), Term("liberty"), Term("pursuit"), Term("happiness") }, 3, true), + "or" => Builders.SearchSpan.Or(Term("unalienable"), Term("inalienable")), + "subtract" => Builders.SearchSpan.Subtract(Term("unalienable"), Term("inalienable")), + _ => throw new ArgumentOutOfRangeException(nameof(spanType), spanType, "Invalid span type") + }; + + var result = SearchSingle(Builders.Search.Span(spanDefinition)); + result.Title.Should().Be("Declaration of Independence"); + + SearchSpanDefinition Term(string term) => Builders.SearchSpan.Term(x => x.Body, term); + } + + [Fact] + public void Text() + { + var result = SearchSingle(Builders.Search.Text(x => x.Body, "life, liberty, and the pursuit of happiness")); + + result.Title.Should().Be("Declaration of Independence"); + } + + [Fact] + public void Wildcard() + { + var result = SearchSingle(Builders.Search.Wildcard(x => x.Body, "tranquil*", true)); + + result.Title.Should().Be("US Constitution"); + } + + private List GeoSearch(SearchDefinition searchDefintion) => + GetGeoTestCollection().Aggregate().Search(searchDefintion).ToList(); + + private HistoricalDocument SearchSingle(SearchDefinition searchDefintion) => + GetTestCollection() + .Aggregate() + .Search(searchDefintion) + .Limit(1) + .ToList() + .Single(); + + private IMongoCollection GetTestCollection() => _disposableMongoClient + .GetDatabase("sample_training") + .GetCollection("posts"); + + private IMongoCollection GetGeoTestCollection() => _disposableMongoClient + .GetDatabase("sample_airbnb") + .GetCollection("listingsAndReviews"); + + [BsonIgnoreExtraElements] + public class HistoricalDocument + { + [BsonId] + public ObjectId Id { get; set; } + + [BsonElement("body")] + public string Body { get; set; } + + [BsonElement("author")] + public string Author { get; set; } + + [BsonElement("title")] + public string Title { get; set; } + + [BsonElement("highlights")] + public List Highlights { get; set; } + + [BsonElement("score")] + public double Score { get; set; } + + [BsonElement("date")] + public DateTime Date { get; set; } + + [BsonElement("index")] + public int Index { get; set; } + + [BsonElement("metaResult")] + public SearchMetaResult MetaResult { get; set; } + } + + [BsonIgnoreExtraElements] + public class Address + { + [BsonElement("location")] + public GeoJsonObject Location { get; set; } + + [BsonElement("street")] + public string Street { get; set; } + } + + [BsonIgnoreExtraElements] + public class AirbnbListing + { + [BsonElement("address")] + public Address Address { get; set; } + + [BsonElement("bedrooms")] + public int Bedrooms { get; set; } + + [BsonElement("beds")] + public int Beds { get; set; } + + [BsonElement("description")] + public string Description { get; set; } + + [BsonElement("space")] + public string Space { get; set; } + + [BsonElement("name")] + public string Name { get; set; } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Search/MongoQueryableTests.cs b/tests/MongoDB.Driver.Tests/Search/MongoQueryableTests.cs new file mode 100644 index 00000000000..fc94e4d498b --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Search/MongoQueryableTests.cs @@ -0,0 +1,64 @@ +/* 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.Serialization.Attributes; +using MongoDB.Driver.Linq; +using Xunit; + +namespace MongoDB.Driver.Tests.Search +{ + public class MongoQueryableTests + { + [Fact] + public void Search() + { + var subject = CreateSubject(); + + var query = subject + .Search(Builders.Search.Text(x => x.FirstName, "Alex")); + + query.ToString().Should().EndWith("Aggregate([{ \"$search\" : { \"text\" : { \"query\" : \"Alex\", \"path\" : \"fn\" } } }])"); + } + + [Fact] + public void SearchMeta() + { + var subject = CreateSubject(); + + var query = subject + .SearchMeta(Builders.Search.Text(x => x.FirstName, "Alex")); + + query.ToString().Should().EndWith("Aggregate([{ \"$searchMeta\" : { \"text\" : { \"query\" : \"Alex\", \"path\" : \"fn\" } } }])"); + } + + private IMongoQueryable CreateSubject() + { + var client = DriverTestConfiguration.Linq3Client; + var database = client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName); + var collection = database.GetCollection(DriverTestConfiguration.CollectionNamespace.CollectionName); + return collection.AsQueryable(); + } + + private class Person + { + [BsonElement("fn")] + public string FirstName { get; set; } + + [BsonElement("ln")] + public string LastName { get; set; } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Search/ProjectionDefinitionBuilderTests.cs b/tests/MongoDB.Driver.Tests/Search/ProjectionDefinitionBuilderTests.cs new file mode 100644 index 00000000000..d827ca1220f --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Search/ProjectionDefinitionBuilderTests.cs @@ -0,0 +1,63 @@ +/* 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; +using Xunit; + +namespace MongoDB.Driver.Tests.Search +{ + public class ProjectionDefinitionBuilderTests + { + [Fact] + public void MetaSearchHighlights() + { + var subject = CreateSubject(); + + AssertRendered(subject.MetaSearchHighlights("a"), "{ a: { $meta: 'searchHighlights' } }"); + } + + [Fact] + public void MetaSearchScore() + { + var subject = CreateSubject(); + + AssertRendered(subject.MetaSearchScore("a"), "{ a : { $meta: 'searchScore' } }"); + } + + [Fact] + public void SearchMeta() + { + var subject = CreateSubject(); + + AssertRendered(subject.SearchMeta("a"), "{ a: '$$SEARCH_META' }"); + } + + private void AssertRendered(ProjectionDefinition projection, string expected) => + AssertRendered(projection, BsonDocument.Parse(expected)); + + private void AssertRendered(ProjectionDefinition projection, BsonDocument expected) + { + var documentSerializer = BsonSerializer.SerializerRegistry.GetSerializer(); + var renderedProjection = projection.Render(documentSerializer, BsonSerializer.SerializerRegistry); + + renderedProjection.Should().BeEquivalentTo(expected); + } + + private ProjectionDefinitionBuilder CreateSubject() => + new ProjectionDefinitionBuilder(); + } +} diff --git a/tests/MongoDB.Driver.Tests/Search/SearchDefinitionBuilderTests.cs b/tests/MongoDB.Driver.Tests/Search/SearchDefinitionBuilderTests.cs new file mode 100644 index 00000000000..95d7e9bd377 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Search/SearchDefinitionBuilderTests.cs @@ -0,0 +1,963 @@ +/* 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 FluentAssertions; +using MongoDB.Bson; +using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver.GeoJsonObjectModel; +using MongoDB.Driver.Search; +using Xunit; + +namespace MongoDB.Driver.Tests.Search +{ + public class SearchDefinitionBuilderTests + { + private static readonly GeoWithinBox __testBox = + new GeoWithinBox( + new GeoJsonPoint( + new GeoJson2DGeographicCoordinates(-161.323242, 22.065278)), + new GeoJsonPoint( + new GeoJson2DGeographicCoordinates(-152.446289, 22.512557))); + + private static readonly GeoWithinCircle __testCircle = + new GeoWithinCircle( + new GeoJsonPoint( + new GeoJson2DGeographicCoordinates(-161.323242, 22.512557)), + 7.5); + + private static readonly GeoJsonPolygon __testPolygon = + new GeoJsonPolygon( + new GeoJsonPolygonCoordinates( + new GeoJsonLinearRingCoordinates( + new List() + { + new GeoJson2DGeographicCoordinates(-161.323242, 22.512557), + new GeoJson2DGeographicCoordinates(-152.446289, 22.065278), + new GeoJson2DGeographicCoordinates(-156.09375, 17.811456), + new GeoJson2DGeographicCoordinates(-161.323242, 22.512557) + }))); + + [Fact] + public void Autocomplete() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Autocomplete("x", "foo"), + "{ autocomplete: { query: 'foo', path: 'x' } }"); + AssertRendered( + subject.Autocomplete(new[] { "x", "y" }, "foo"), + "{ autocomplete: { query: 'foo', path: ['x', 'y'] } }"); + AssertRendered( + subject.Autocomplete("x", new[] { "foo", "bar" }), + "{ autocomplete: { query: ['foo', 'bar'], path: 'x' } }"); + AssertRendered( + subject.Autocomplete(new[] { "x", "y" }, new[] { "foo", "bar" }), + "{ autocomplete: { query: ['foo', 'bar'], path: ['x', 'y'] } }"); + + AssertRendered( + subject.Autocomplete("x", "foo", SearchAutocompleteTokenOrder.Any), + "{ autocomplete: { query: 'foo', path: 'x' } }"); + AssertRendered( + subject.Autocomplete("x", "foo", SearchAutocompleteTokenOrder.Sequential), + "{ autocomplete: { query: 'foo', path: 'x', tokenOrder: 'sequential' } }"); + + AssertRendered( + subject.Autocomplete("x", "foo", fuzzy: new SearchFuzzyOptions()), + "{ autocomplete: { query: 'foo', path: 'x', fuzzy: {} } }"); + AssertRendered( + subject.Autocomplete("x", "foo", fuzzy: new SearchFuzzyOptions() + { + MaxEdits = 1, + PrefixLength = 5, + MaxExpansions = 25 + }), + "{ autocomplete: { query: 'foo', path: 'x', fuzzy: { maxEdits: 1, prefixLength: 5, maxExpansions: 25 } } }"); + + var scoreBuilder = new SearchScoreDefinitionBuilder(); + AssertRendered( + subject.Autocomplete("x", "foo", score: scoreBuilder.Constant(1)), + "{ autocomplete: { query: 'foo', path: 'x', score: { constant: { value: 1 } } } }"); + } + + [Fact] + public void Autocomplete_typed() + { + var subject = CreateSubject(); + AssertRendered( + subject.Autocomplete(x => x.FirstName, "foo"), + "{ autocomplete: { query: 'foo', path: 'fn' } }"); + AssertRendered( + subject.Autocomplete("FirstName", "foo"), + "{ autocomplete: { query: 'foo', path: 'fn' } }"); + + AssertRendered( + subject.Autocomplete( + new FieldDefinition[] + { + new ExpressionFieldDefinition(x => x.FirstName), + new ExpressionFieldDefinition(x => x.LastName) + }, + "foo"), + "{ autocomplete: { query: 'foo', path: ['fn', 'ln'] } }"); + AssertRendered( + subject.Autocomplete(new[] { "FirstName", "LastName" }, "foo"), + "{ autocomplete: { query: 'foo', path: ['fn', 'ln'] } }"); + + AssertRendered( + subject.Autocomplete(x => x.FirstName, new[] { "foo", "bar" }), + "{ autocomplete: { query: ['foo', 'bar'], path: 'fn' } }"); + AssertRendered( + subject.Autocomplete("FirstName", new[] { "foo", "bar" }), + "{ autocomplete: { query: ['foo', 'bar'], path: 'fn' } }"); + + AssertRendered( + subject.Autocomplete( + new FieldDefinition[] + { + new ExpressionFieldDefinition(x => x.FirstName), + new ExpressionFieldDefinition(x => x.LastName) + }, + new[] { "foo", "bar" }), + "{ autocomplete: { query: ['foo', 'bar'], path: ['fn', 'ln'] } }"); + AssertRendered( + subject.Autocomplete(new[] { "FirstName", "LastName" }, new[] { "foo", "bar" }), + "{ autocomplete: { query: ['foo', 'bar'], path: ['fn', 'ln'] } }"); + } + + [Fact] + public void Compound() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Compound() + .Must( + subject.Exists("x"), + subject.Exists("y")) + .MustNot( + subject.Exists("foo"), + subject.Exists("bar")) + .Must( + subject.Exists("z")), + "{ compound: { must: [{ exists: { path: 'x' } }, { exists: { path: 'y' } }, { exists: { path: 'z' } }], mustNot: [{ exists: { path: 'foo' } }, { exists: { path: 'bar' } }] } }"); + } + + [Fact] + public void Compound_typed() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Compound() + .Must( + subject.Exists(p => p.Age), + subject.Exists(p => p.FirstName)) + .MustNot( + subject.Exists(p => p.Retired), + subject.Exists(p => p.Birthday)) + .Must( + subject.Exists(p => p.LastName)), + "{ compound: { must: [{ exists: { path: 'age' } }, { exists: { path: 'fn' } }, { exists: { path: 'ln' } }], mustNot: [{ exists: { path: 'ret' } }, { exists: { path: 'dob' } }] } }"); + } + + [Fact] + public void Equals() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Equals("x", true), + "{ equals: { path: 'x', value: true } }"); + AssertRendered( + subject.Equals("x", ObjectId.Empty), + "{ equals: { path: 'x', value: { $oid: '000000000000000000000000' } } }"); + + var scoreBuilder = new SearchScoreDefinitionBuilder(); + AssertRendered( + subject.Equals("x", true, scoreBuilder.Constant(1)), + "{ equals: { path: 'x', value: true, score: { constant: { value: 1 } } } }"); + } + + [Fact] + public void Equals_typed() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Equals(x => x.Retired, true), + "{ equals: { path: 'ret', value: true } }"); + AssertRendered( + subject.Equals("Retired", true), + "{ equals: { path: 'ret', value: true } }"); + + AssertRendered( + subject.Equals(x => x.Id, ObjectId.Empty), + "{ equals: { path: '_id', value: { $oid: '000000000000000000000000' } } }"); + } + + [Fact] + public void Exists() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Exists("x"), + "{ exists: { path: 'x' } }"); + } + + [Fact] + public void Exists_typed() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Exists(x => x.FirstName), + "{ exists: { path: 'fn' } }"); + AssertRendered( + subject.Exists("FirstName"), + "{ exists: { path: 'fn' } }"); + } + + [Fact] + public void Facet() + { + var subject = CreateSubject(); + var facetBuilder = new SearchFacetBuilder(); + + AssertRendered( + subject.Facet( + subject.Phrase("x", "foo"), + facetBuilder.String("string", "y", 100)), + "{ facet: { operator: { phrase: { query: 'foo', path: 'x' } }, facets: { string: { type: 'string', path: 'y', numBuckets: 100 } } } }"); + } + + [Fact] + public void Facet_typed() + { + var subject = CreateSubject(); + var facetBuilder = new SearchFacetBuilder(); + + AssertRendered( + subject.Facet( + subject.Phrase(x => x.LastName, "foo"), + facetBuilder.String("string", x => x.FirstName, 100)), + "{ facet: { operator: { phrase: { query: 'foo', path: 'ln' } }, facets: { string: { type: 'string', path: 'fn', numBuckets: 100 } } } }"); + AssertRendered( + subject.Facet( + subject.Phrase("LastName", "foo"), + facetBuilder.String("string", "FirstName", 100)), + "{ facet: { operator: { phrase: { query: 'foo', path: 'ln' } }, facets: { string: { type: 'string', path: 'fn', numBuckets: 100 } } } }"); + } + + [Fact] + public void Filter() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Compound().Filter( + subject.Exists("x"), + subject.Exists("y")), + "{ compound: { filter: [{ exists: { path: 'x' } }, { exists: { path: 'y' } }] } }"); + } + + [Fact] + public void Filter_typed() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Compound().Filter( + subject.Exists(p => p.Age), + subject.Exists(p => p.Birthday)), + "{ compound: { filter: [{ exists: { path: 'age' } }, { exists: { path: 'dob' } }] } }"); + } + + [Fact] + public void GeoShape() + { + var subject = CreateSubject(); + + AssertRendered( + subject.GeoShape( + "location", + GeoShapeRelation.Disjoint, + __testPolygon), + "{ geoShape: { geometry: { type: 'Polygon', coordinates: [[[-161.323242, 22.512557], [-152.446289, 22.065278], [-156.09375, 17.811456], [-161.323242, 22.512557]]] }, path: 'location', relation: 'disjoint' } }"); + } + + [Fact] + public void GeoShape_typed() + { + var subject = CreateSubject(); + + AssertRendered( + subject.GeoShape( + x => x.Location, + GeoShapeRelation.Disjoint, + __testPolygon), + "{ geoShape: { geometry: { type: 'Polygon', coordinates: [[[-161.323242, 22.512557], [-152.446289, 22.065278], [-156.09375, 17.811456], [-161.323242, 22.512557]]] }, path: 'location', relation: 'disjoint' } }"); + AssertRendered( + subject.GeoShape( + "Location", + GeoShapeRelation.Disjoint, + __testPolygon), + "{ geoShape: { geometry: { type: 'Polygon', coordinates: [[[-161.323242, 22.512557], [-152.446289, 22.065278], [-156.09375, 17.811456], [-161.323242, 22.512557]]] }, path: 'location', relation: 'disjoint' } }"); + } + + [Fact] + public void GeoWithin() + { + var subject = CreateSubject(); + + AssertRendered( + subject.GeoWithin("location", __testPolygon), + "{ geoWithin: { geometry: { type: 'Polygon', coordinates: [[[-161.323242, 22.512557], [-152.446289, 22.065278], [-156.09375, 17.811456], [-161.323242, 22.512557]]] }, path: 'location' } }"); + AssertRendered( + subject.GeoWithin("location", __testBox), + "{ geoWithin: { box: { bottomLeft: { type: 'Point', coordinates: [-161.323242, 22.065278] }, topRight: { type: 'Point', coordinates: [-152.446289, 22.512557] } }, path: 'location' } }"); + AssertRendered( + subject.GeoWithin("location", __testCircle), + "{ geoWithin: { circle: { center: { type: 'Point', coordinates: [-161.323242, 22.512557] }, radius: 7.5 }, path: 'location' } }"); + } + + [Fact] + public void GeoWithin_typed() + { + var subject = CreateSubject(); + + AssertRendered( + subject.GeoWithin(x => x.Location, __testPolygon), + "{ geoWithin: { geometry: { type: 'Polygon', coordinates: [[[-161.323242, 22.512557], [-152.446289, 22.065278], [-156.09375, 17.811456], [-161.323242, 22.512557]]] }, path: 'location' } }"); + AssertRendered( + subject.GeoWithin("Location", __testPolygon), + "{ geoWithin: { geometry: { type: 'Polygon', coordinates: [[[-161.323242, 22.512557], [-152.446289, 22.065278], [-156.09375, 17.811456], [-161.323242, 22.512557]]] }, path: 'location' } }"); + + AssertRendered( + subject.GeoWithin(x => x.Location, __testBox), + "{ geoWithin: { box: { bottomLeft: { type: 'Point', coordinates: [-161.323242, 22.065278] }, topRight: { type: 'Point', coordinates: [-152.446289, 22.512557] } }, path: 'location' } }"); + AssertRendered( + subject.GeoWithin("Location", __testBox), + "{ geoWithin: { box: { bottomLeft: { type: 'Point', coordinates: [-161.323242, 22.065278] }, topRight: { type: 'Point', coordinates: [-152.446289, 22.512557] } }, path: 'location' } }"); + + AssertRendered( + subject.GeoWithin(x => x.Location, __testCircle), + "{ geoWithin: { circle: { center: { type: 'Point', coordinates: [-161.323242, 22.512557] }, radius: 7.5 }, path: 'location' } }"); + AssertRendered( + subject.GeoWithin("Location", __testCircle), + "{ geoWithin: { circle: { center: { type: 'Point', coordinates: [-161.323242, 22.512557] }, radius: 7.5 }, path: 'location' } }"); + } + + [Fact] + public void MoreLikeThis() + { + var subject = CreateSubject(); + + AssertRendered( + subject.MoreLikeThis( + new BsonDocument("x", "foo"), + new BsonDocument("x", "bar")), + "{ moreLikeThis: { like: [{ x: 'foo' }, { x: 'bar' }] } }"); + + AssertRendered( + subject.MoreLikeThis( + new SimplePerson { FirstName = "John", LastName = "Doe" }, + new SimplePerson { FirstName = "Jane", LastName = "Doe" }), + "{ moreLikeThis: { like: [{ fn: 'John', ln: 'Doe' }, { fn: 'Jane', ln: 'Doe' }] } }"); + } + + [Fact] + public void MoreLikeThis_typed() + { + var subject = CreateSubject(); + + AssertRendered( + subject.MoreLikeThis( + new SimplestPerson { FirstName = "John" }, + new SimplestPerson { FirstName = "Jane" }), + "{ moreLikeThis: { like: [{ fn: 'John' }, { fn: 'Jane' }] } }"); + + AssertRendered( + subject.MoreLikeThis( + new SimplePerson { FirstName = "John", LastName = "Doe" }, + new SimplePerson { FirstName = "Jane", LastName = "Doe" }), + "{ moreLikeThis: { like: [{ fn: 'John', ln: 'Doe' }, { fn: 'Jane', ln: 'Doe' }] } }"); + + AssertRendered( + subject.MoreLikeThis( + new BsonDocument + { + { "fn", "John" }, + { "ln", "Doe" }, + }, + new BsonDocument + { + { "fn", "Jane" }, + { "ln", "Doe" }, + }), + "{ moreLikeThis: { like: [{ fn: 'John', ln: 'Doe' }, { fn: 'Jane', ln: 'Doe' }] } }"); + } + + [Fact] + public void Must() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Compound().Must( + subject.Exists(p => p.Age), + subject.Exists(p => p.Birthday)), + "{ compound: { must: [{ exists: { path: 'age' } }, { exists: { path: 'dob' } }] } }"); + } + + [Fact] + public void MustNot() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Compound().MustNot( + subject.Exists(p => p.Age), + subject.Exists(p => p.Birthday)), + "{ compound: { mustNot: [{ exists: { path: 'age' } }, { exists: { path: 'dob' } }] } }"); + } + + [Fact] + public void Near() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Near("x", 5.0, 1.0), + "{ near: { path: 'x', origin: 5.0, pivot: 1.0 } }"); + AssertRendered( + subject.Near("x", 5, 1), + "{ near: { path: 'x', origin: 5, pivot: 1 } }"); + AssertRendered( + subject.Near("x", 5L, 1L), + "{ near: { path: 'x', origin: { $numberLong: '5' }, pivot: { $numberLong: '1' } } }"); + AssertRendered( + subject.Near("x", new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc), 1000L), + "{ near: { path: 'x', origin: { $date: '2000-01-01T00:00:00Z' }, pivot: { $numberLong: '1000' } } }"); + + var scoreBuilder = new SearchScoreDefinitionBuilder(); + AssertRendered( + subject.Near("x", 5.0, 1.0, scoreBuilder.Constant(1)), + "{ near: { path: 'x', origin: 5, pivot: 1, score: { constant: { value: 1 } } } }"); + } + + [Fact] + public void Near_typed() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Near(x => x.Age, 35.0, 5.0), + "{ near: { path: 'age', origin: 35.0, pivot: 5.0 } }"); + AssertRendered( + subject.Near("Age", 35.0, 5.0), + "{ near: { path: 'age', origin: 35.0, pivot: 5.0 } }"); + + AssertRendered( + subject.Near(x => x.Age, 35, 5), + "{ near: { path: 'age', origin: 35, pivot: 5 } }"); + AssertRendered( + subject.Near("Age", 35, 5), + "{ near: { path: 'age', origin: 35, pivot: 5 } }"); + + AssertRendered( + subject.Near(x => x.Age, 35L, 5L), + "{ near: { path: 'age', origin: { $numberLong: '35' }, pivot: { $numberLong: '5' } } }"); + AssertRendered( + subject.Near("Age", 35L, 5L), + "{ near: { path: 'age', origin: { $numberLong: '35' }, pivot: { $numberLong: '5' } } }"); + + AssertRendered( + subject.Near(x => x.Birthday, new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc), 1000L), + "{ near: { path: 'dob', origin: { $date: '2000-01-01T00:00:00Z' }, pivot: { $numberLong: '1000' } } }"); + AssertRendered( + subject.Near("Birthday", new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc), 1000L), + "{ near: { path: 'dob', origin: { $date: '2000-01-01T00:00:00Z' }, pivot: { $numberLong: '1000' } } }"); + } + + [Fact] + public void Phrase() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Phrase("x", "foo"), + "{ phrase: { query: 'foo', path: 'x' } }"); + AssertRendered( + subject.Phrase(new[] { "x", "y" }, "foo"), + "{ phrase: { query: 'foo', path: ['x', 'y'] } }"); + AssertRendered( + subject.Phrase("x", new[] { "foo", "bar" }), + "{ phrase: { query: ['foo', 'bar'], path: 'x' } }"); + AssertRendered( + subject.Phrase(new[] { "x", "y" }, new[] { "foo", "bar" }), + "{ phrase: { query: ['foo', 'bar'], path: ['x', 'y'] } }"); + + AssertRendered( + subject.Phrase("x", "foo", 5), + "{ phrase: { query: 'foo', path: 'x', slop: 5 } }"); + + var scoreBuilder = new SearchScoreDefinitionBuilder(); + AssertRendered( + subject.Phrase("x", "foo", score: scoreBuilder.Constant(1)), + "{ phrase: { query: 'foo', path: 'x', score: { constant: { value: 1 } } } }"); + } + + [Fact] + public void Phrase_typed() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Phrase(x => x.FirstName, "foo"), + "{ phrase: { query: 'foo', path: 'fn' } }"); + AssertRendered( + subject.Phrase("FirstName", "foo"), + "{ phrase: { query: 'foo', path: 'fn' } }"); + + AssertRendered( + subject.Phrase( + new FieldDefinition[] + { + new ExpressionFieldDefinition(x => x.FirstName), + new ExpressionFieldDefinition(x => x.LastName) + }, + "foo"), + "{ phrase: { query: 'foo', path: ['fn', 'ln'] } }"); + AssertRendered( + subject.Phrase(new[] { "FirstName", "LastName" }, "foo"), + "{ phrase: { query: 'foo', path: ['fn', 'ln'] } }"); + + AssertRendered( + subject.Phrase(x => x.FirstName, new[] { "foo", "bar" }), + "{ phrase: { query: ['foo', 'bar'], path: 'fn' } }"); + AssertRendered( + subject.Phrase("FirstName", new[] { "foo", "bar" }), + "{ phrase: { query: ['foo', 'bar'], path: 'fn' } }"); + + AssertRendered( + subject.Phrase( + new FieldDefinition[] + { + new ExpressionFieldDefinition(x => x.FirstName), + new ExpressionFieldDefinition(x => x.LastName) + }, + new[] { "foo", "bar" }), + "{ phrase: { query: ['foo', 'bar'], path: ['fn', 'ln'] } }"); + AssertRendered( + subject.Phrase(new[] { "FirstName", "LastName" }, new[] { "foo", "bar" }), + "{ phrase: { query: ['foo', 'bar'], path: ['fn', 'ln'] } }"); + } + + [Fact] + public void QueryString() + { + var subject = CreateSubject(); + + AssertRendered( + subject.QueryString("x", "foo"), + "{ queryString: { defaultPath: 'x', query: 'foo' } }"); + + var scoreBuilder = new SearchScoreDefinitionBuilder(); + AssertRendered( + subject.QueryString("x", "foo", scoreBuilder.Constant(1)), + "{ queryString: { defaultPath: 'x', query: 'foo', score: { constant: { value: 1 } } } }"); + } + + [Fact] + public void QueryString_typed() + { + var subject = CreateSubject(); + + AssertRendered( + subject.QueryString(x => x.FirstName, "foo"), + "{ queryString: { defaultPath: 'fn', query: 'foo' } }"); + AssertRendered( + subject.QueryString("FirstName", "foo"), + "{ queryString: { defaultPath: 'fn', query: 'foo' } }"); + } + + [Fact] + public void RangeDateTime() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Range( + p => p.Birthday, + SearchRangeBuilder + .Gte(new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc)) + .Lte(new DateTime(2009, 12, 31, 0, 0, 0, DateTimeKind.Utc))), + "{ range: { path: 'dob', gte: { $date: '2000-01-01T00:00:00Z' }, lte: { $date: '2009-12-31T00:00:00Z' } } }"); + } + + [Fact] + public void RangeDouble() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Range(p => p.Age, SearchRangeBuilder.Gt(1.5).Lt(2.5)), + "{ range: { path: 'age', gt: 1.5, lt: 2.5 } }"); + } + + [Fact] + public void RangeInt32() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Range("x", SearchRangeBuilder.Gt(1).Lt(10)), + "{ range: { path: 'x', gt: 1, lt: 10 } }"); + AssertRendered( + subject.Range("x", SearchRangeBuilder.Lt(10).Gt(1)), + "{ range: { path: 'x', gt: 1, lt: 10 } }"); + AssertRendered( + subject.Range("x", SearchRangeBuilder.Gte(1).Lte(10)), + "{ range: { path: 'x', gte: 1, lte: 10 } }"); + AssertRendered( + subject.Range("x", SearchRangeBuilder.Lte(10).Gte(1)), + "{ range: { path: 'x', gte: 1, lte: 10 } }"); + } + + [Fact] + public void RangeInt32_typed() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Range(x => x.Age, SearchRangeBuilder.Gte(18).Lt(65)), + "{ range: { path: 'age', gte: 18, lt: 65 } }"); + AssertRendered( + subject.Range("Age", SearchRangeBuilder.Gte(18).Lt(65)), + "{ range: { path: 'age', gte: 18, lt: 65 } }"); + } + + [Fact] + public void Regex() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Regex("x", "foo"), + "{ regex: { query: 'foo', path: 'x' } }"); + AssertRendered( + subject.Regex(new[] { "x", "y" }, "foo"), + "{ regex: { query: 'foo', path: ['x', 'y'] } }"); + AssertRendered( + subject.Regex("x", new[] { "foo", "bar" }), + "{ regex: { query: ['foo', 'bar'], path: 'x' } }"); + AssertRendered( + subject.Regex(new[] { "x", "y" }, new[] { "foo", "bar" }), + "{ regex: { query: ['foo', 'bar'], path: ['x', 'y'] } }"); + + AssertRendered( + subject.Regex("x", "foo", false), + "{ regex: { query: 'foo', path: 'x' } }"); + AssertRendered( + subject.Regex("x", "foo", true), + "{ regex: { query: 'foo', path: 'x', allowAnalyzedField: true } }"); + + var scoreBuilder = new SearchScoreDefinitionBuilder(); + AssertRendered( + subject.Regex("x", "foo", score: scoreBuilder.Constant(1)), + "{ regex: { query: 'foo', path: 'x', score: { constant: { value: 1 } } } }"); + } + + [Fact] + public void Regex_typed() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Regex(x => x.FirstName, "foo"), + "{ regex: { query: 'foo', path: 'fn' } }"); + AssertRendered( + subject.Regex("FirstName", "foo"), + "{ regex: { query: 'foo', path: 'fn' } }"); + + AssertRendered( + subject.Regex( + new FieldDefinition[] + { + new ExpressionFieldDefinition(x => x.FirstName), + new ExpressionFieldDefinition(x => x.LastName) + }, + "foo"), + "{ regex: { query: 'foo', path: ['fn', 'ln'] } }"); + AssertRendered( + subject.Regex(new[] { "FirstName", "LastName" }, "foo"), + "{ regex: { query: 'foo', path: ['fn', 'ln'] } }"); + + AssertRendered( + subject.Regex(x => x.FirstName, new[] { "foo", "bar" }), + "{ regex: { query: ['foo', 'bar'], path: 'fn' } }"); + AssertRendered( + subject.Regex("FirstName", new[] { "foo", "bar" }), + "{ regex: { query: ['foo', 'bar'], path: 'fn' } }"); + + AssertRendered( + subject.Regex( + new FieldDefinition[] + { + new ExpressionFieldDefinition(x => x.FirstName), + new ExpressionFieldDefinition(x => x.LastName) + }, + new[] { "foo", "bar" }), + "{ regex: { query: ['foo', 'bar'], path: ['fn', 'ln'] } }"); + AssertRendered( + subject.Regex(new[] { "FirstName", "LastName" }, new[] { "foo", "bar" }), + "{ regex: { query: ['foo', 'bar'], path: ['fn', 'ln'] } }"); + } + + [Fact] + public void Should() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Compound() + .Should( + subject.Exists(p => p.Age), + subject.Exists(p => p.Birthday)) + .MinimumShouldMatch(2), + "{ compound: { should: [{ exists: { path: 'age' } }, { exists: { path: 'dob' } }], minimumShouldMatch: 2 } }"); + } + + [Fact] + public void Span() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Span(Builders.SearchSpan + .First(Builders.SearchSpan.Term(p => p.Age, "foo"), 5)), + "{ span: { first: { operator: { term: { query: 'foo', path: 'age' } }, endPositionLte: 5 } } }"); + } + + [Fact] + public void Text() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Text("x", "foo"), + "{ text: { query: 'foo', path: 'x' } }"); + AssertRendered( + subject.Text(new[] { "x", "y" }, "foo"), + "{ text: { query: 'foo', path: ['x', 'y'] } }"); + AssertRendered( + subject.Text("x", new[] { "foo", "bar" }), + "{ text: { query: ['foo', 'bar'], path: 'x' } }"); + AssertRendered( + subject.Text(new[] { "x", "y" }, new[] { "foo", "bar" }), + "{ text: { query: ['foo', 'bar'], path: ['x', 'y'] } }"); + + AssertRendered( + subject.Text("x", "foo", new SearchFuzzyOptions()), + "{ text: { query: 'foo', path: 'x', fuzzy: {} } }"); + AssertRendered( + subject.Text("x", "foo", new SearchFuzzyOptions() + { + MaxEdits = 1, + PrefixLength = 5, + MaxExpansions = 25 + }), + "{ text: { query: 'foo', path: 'x', fuzzy: { maxEdits: 1, prefixLength: 5, maxExpansions: 25 } } }"); + + var scoreBuilder = new SearchScoreDefinitionBuilder(); + AssertRendered( + subject.Text("x", "foo", score: scoreBuilder.Constant(1)), + "{ text: { query: 'foo', path: 'x', score: { constant: { value: 1 } } } }"); + } + + [Fact] + public void Text_typed() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Text(x => x.FirstName, "foo"), + "{ text: { query: 'foo', path: 'fn' } }"); + AssertRendered( + subject.Text("FirstName", "foo"), + "{ text: { query: 'foo', path: 'fn' } }"); + + AssertRendered( + subject.Text( + new FieldDefinition[] + { + new ExpressionFieldDefinition(x => x.FirstName), + new ExpressionFieldDefinition(x => x.LastName) + }, + "foo"), + "{ text: { query: 'foo', path: ['fn', 'ln'] } }"); + AssertRendered( + subject.Text(new[] { "FirstName", "LastName" }, "foo"), + "{ text: { query: 'foo', path: ['fn', 'ln'] } }"); + + AssertRendered( + subject.Text(x => x.FirstName, new[] { "foo", "bar" }), + "{ text: { query: ['foo', 'bar'], path: 'fn' } }"); + AssertRendered( + subject.Text("FirstName", new[] { "foo", "bar" }), + "{ text: { query: ['foo', 'bar'], path: 'fn' } }"); + + AssertRendered( + subject.Text( + new FieldDefinition[] + { + new ExpressionFieldDefinition(x => x.FirstName), + new ExpressionFieldDefinition(x => x.LastName) + }, + new[] { "foo", "bar" }), + "{ text: { query: ['foo', 'bar'], path: ['fn', 'ln'] } }"); + AssertRendered( + subject.Text(new[] { "FirstName", "LastName" }, new[] { "foo", "bar" }), + "{ text: { query: ['foo', 'bar'], path: ['fn', 'ln'] } }"); + } + + [Fact] + public void Wildcard() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Wildcard("x", "foo"), + "{ wildcard: { query: 'foo', path: 'x' } }"); + AssertRendered( + subject.Wildcard(new[] { "x", "y" }, "foo"), + "{ wildcard: { query: 'foo', path: ['x', 'y'] } }"); + AssertRendered( + subject.Wildcard("x", new[] { "foo", "bar" }), + "{ wildcard: { query: ['foo', 'bar'], path: 'x' } }"); + AssertRendered( + subject.Wildcard(new[] { "x", "y" }, new[] { "foo", "bar" }), + "{ wildcard: { query: ['foo', 'bar'], path: ['x', 'y'] } }"); + + AssertRendered( + subject.Wildcard("x", "foo", false), + "{ wildcard: { query: 'foo', path: 'x' } }"); + AssertRendered( + subject.Wildcard("x", "foo", true), + "{ wildcard: { query: 'foo', path: 'x', allowAnalyzedField: true } }"); + + var scoreBuilder = new SearchScoreDefinitionBuilder(); + AssertRendered( + subject.Wildcard("x", "foo", score: scoreBuilder.Constant(1)), + "{ wildcard: { query: 'foo', path: 'x', score: { constant: { value: 1 } } } }"); + } + + [Fact] + public void Wildcard_typed() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Wildcard(x => x.FirstName, "foo"), + "{ wildcard: { query: 'foo', path: 'fn' } }"); + AssertRendered( + subject.Wildcard("FirstName", "foo"), + "{ wildcard: { query: 'foo', path: 'fn' } }"); + + AssertRendered( + subject.Wildcard( + new FieldDefinition[] + { + new ExpressionFieldDefinition(x => x.FirstName), + new ExpressionFieldDefinition(x => x.LastName) + }, + "foo"), + "{ wildcard: { query: 'foo', path: ['fn', 'ln'] } }"); + AssertRendered( + subject.Wildcard(new[] { "FirstName", "LastName" }, "foo"), + "{ wildcard: { query: 'foo', path: ['fn', 'ln'] } }"); + + AssertRendered( + subject.Wildcard(x => x.FirstName, new[] { "foo", "bar" }), + "{ wildcard: { query: ['foo', 'bar'], path: 'fn' } }"); + AssertRendered( + subject.Wildcard("FirstName", new[] { "foo", "bar" }), + "{ wildcard: { query: ['foo', 'bar'], path: 'fn' } }"); + + AssertRendered( + subject.Wildcard( + new FieldDefinition[] + { + new ExpressionFieldDefinition(x => x.FirstName), + new ExpressionFieldDefinition(x => x.LastName) + }, + new[] { "foo", "bar" }), + "{ wildcard: { query: ['foo', 'bar'], path: ['fn', 'ln'] } }"); + AssertRendered( + subject.Wildcard(new[] { "FirstName", "LastName" }, new[] { "foo", "bar" }), + "{ wildcard: { query: ['foo', 'bar'], path: ['fn', 'ln'] } }"); + } + + private void AssertRendered(SearchDefinition query, string expected) => + AssertRendered(query, BsonDocument.Parse(expected)); + + private void AssertRendered(SearchDefinition query, BsonDocument expected) + { + var documentSerializer = BsonSerializer.SerializerRegistry.GetSerializer(); + var renderedQuery = query.Render(documentSerializer, BsonSerializer.SerializerRegistry); + + renderedQuery.Should().BeEquivalentTo(expected); + } + + private SearchDefinitionBuilder CreateSubject() => new SearchDefinitionBuilder(); + + private class Person : SimplePerson + { + [BsonElement("age")] + public int Age { get; set; } + + [BsonElement("dob")] + public DateTime Birthday { get; set; } + + [BsonId] + public ObjectId Id { get; set; } + [BsonElement("location")] + public GeoJsonPoint Location { get; set; } + + [BsonElement("ret")] + public bool Retired { get; set; } + } + + private class SimplePerson + { + [BsonElement("fn")] + public string FirstName { get; set; } + + [BsonElement("ln")] + public string LastName { get; set; } + } + + private class SimplestPerson + { + [BsonElement("fn")] + public string FirstName { get; set; } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Search/SearchFacetBuilderTests.cs b/tests/MongoDB.Driver.Tests/Search/SearchFacetBuilderTests.cs new file mode 100644 index 00000000000..a65b39697e6 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Search/SearchFacetBuilderTests.cs @@ -0,0 +1,144 @@ +/* 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 FluentAssertions; +using MongoDB.Bson; +using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver.Search; +using Xunit; + +namespace MongoDB.Driver.Tests.Search +{ + public class SearchFacetBuilderTests + { + [Fact] + public void Date() + { + var subject = CreateSubject(); + var boundaries = new List() + { + DateTime.MinValue, + DateTime.MaxValue + }; + + AssertRendered( + subject.Date("date", "x", boundaries, "foo"), + "{ type: 'date', path: 'x', boundaries: [{ $date: '0001-01-01T00:00:00Z' }, { $date: '9999-12-31T23:59:59.9999999Z' }], default: 'foo' }"); + AssertRendered( + subject.Date("date", "x", boundaries), + "{ type: 'date', path: 'x', boundaries: [{ $date: '0001-01-01T00:00:00Z' }, { $date: '9999-12-31T23:59:59.9999999Z' }] }"); + } + + [Fact] + public void Date_typed() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Date("date", x => x.Birthday, DateTime.MinValue, DateTime.MaxValue), + "{ type: 'date', path: 'dob', boundaries: [{ $date: '0001-01-01T00:00:00Z' }, { $date: '9999-12-31T23:59:59.9999999Z' }] }"); + AssertRendered( + subject.Date("date", "Birthday", DateTime.MinValue, DateTime.MaxValue), + "{ type: 'date', path: 'dob', boundaries: [{ $date: '0001-01-01T00:00:00Z' }, { $date: '9999-12-31T23:59:59.9999999Z' }] }"); + } + + [Fact] + public void Number() + { + var subject = CreateSubject(); + var boundaries = new List() + { + 0, + 50, + 100 + }; + + AssertRendered( + subject.Number("number", "x", boundaries, "foo"), + "{ type: 'number', path: 'x', boundaries: [0, 50, 100], default: 'foo' }"); + AssertRendered( + subject.Number("number", "x", boundaries), + "{ type: 'number', path: 'x', boundaries: [0, 50, 100] }"); + } + + [Fact] + public void Number_typed() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Number("number", x => x.Age, 0, 18, 65, 120), + "{ type: 'number', path: 'age', boundaries: [0, 18, 65, 120] }"); + AssertRendered( + subject.Number("number", "Age", 0, 18, 65, 120), + "{ type: 'number', path: 'age', boundaries: [0, 18, 65, 120] }"); + } + + [Fact] + public void String() + { + var subject = CreateSubject(); + + AssertRendered( + subject.String("string", "x", 100), + "{ type: 'string', path: 'x', numBuckets: 100 }"); + } + + [Fact] + public void String_typed() + { + var subject = CreateSubject(); + + AssertRendered( + subject.String("string", x => x.FirstName, 100), + "{ type: 'string', path: 'fn', numBuckets: 100 }"); + AssertRendered( + subject.String("string", "FirstName", 100), + "{ type: 'string', path: 'fn', numBuckets: 100 }"); + } + + private void AssertRendered(SearchFacet facet, string expected) => + AssertRendered(facet, BsonDocument.Parse(expected)); + + private void AssertRendered(SearchFacet facet, BsonDocument expected) + { + var documentSerializer = BsonSerializer.SerializerRegistry.GetSerializer(); + var renderedFacet = facet.Render(documentSerializer, BsonSerializer.SerializerRegistry); + + renderedFacet.Should().BeEquivalentTo(expected); + } + + private SearchFacetBuilder CreateSubject() => + new SearchFacetBuilder(); + + private class Person + { + [BsonElement("age")] + public int Age { get; set; } + + [BsonElement("dob")] + public DateTime Birthday { get; set; } + + [BsonElement("fn")] + public string FirstName { get; set; } + + [BsonElement("ln")] + public string LastName { get; set; } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Search/SearchPathDefinitionBuilderTests.cs b/tests/MongoDB.Driver.Tests/Search/SearchPathDefinitionBuilderTests.cs new file mode 100644 index 00000000000..c88c8db8fda --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Search/SearchPathDefinitionBuilderTests.cs @@ -0,0 +1,148 @@ +/* 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.Collections.Generic; +using FluentAssertions; +using MongoDB.Bson; +using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver.Search; +using Xunit; + +namespace MongoDB.Driver.Tests.Search +{ + public class SearchPathDefinitionBuilderTests + { + [Fact] + public void Analyzer() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Analyzer("x", "english"), + "{ value: 'x', multi: 'english' }"); + } + + [Fact] + public void Analyzer_typed() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Analyzer(x => x.FirstName, "english"), + "{ value: 'fn', multi: 'english' }"); + AssertRendered( + subject.Analyzer("FirstName", "english"), + "{ value: 'fn', multi: 'english' }"); + } + + [Fact] + public void Multi() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Multi("x", "y"), + new BsonArray() + { + new BsonString("x"), + new BsonString("y") + }); + AssertRendered( + subject.Multi( + new List>() + { + "x", + "y" + }), + new BsonArray() + { + new BsonString("x"), + new BsonString("y") + }); + } + + [Fact] + public void Multi_typed() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Multi(x => x.FirstName, x => x.LastName), + new BsonArray() + { + new BsonString("fn"), + new BsonString("ln") + }); + AssertRendered( + subject.Multi("FirstName", "LastName"), + new BsonArray() + { + new BsonString("fn"), + new BsonString("ln") + }); + } + + [Fact] + public void Single() + { + var subject = CreateSubject(); + + AssertRendered(subject.Single("x"), new BsonString("x")); + } + + [Fact] + public void Single_typed() + { + var subject = CreateSubject(); + + AssertRendered(subject.Single(x => x.FirstName), new BsonString("fn")); + AssertRendered(subject.Single("FirstName"), new BsonString("fn")); + } + + [Fact] + public void Wildcard() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Wildcard("*"), + "{ wildcard: '*' }"); + } + + private void AssertRendered(SearchPathDefinition path, string expected) => + AssertRendered(path, BsonDocument.Parse(expected)); + + private void AssertRendered(SearchPathDefinition path, BsonValue expected) + { + var documentSerializer = BsonSerializer.SerializerRegistry.GetSerializer(); + var renderedPath = path.Render(documentSerializer, BsonSerializer.SerializerRegistry); + + renderedPath.Should().Be(expected); + } + + private SearchPathDefinitionBuilder CreateSubject() => + new SearchPathDefinitionBuilder(); + + private class Person + { + [BsonElement("fn")] + public string FirstName { get; set; } + + [BsonElement("ln")] + public string LastName { get; set; } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Search/SearchScoreDefinitionBuilderTests.cs b/tests/MongoDB.Driver.Tests/Search/SearchScoreDefinitionBuilderTests.cs new file mode 100644 index 00000000000..7c18f598bb6 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Search/SearchScoreDefinitionBuilderTests.cs @@ -0,0 +1,103 @@ +/* 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; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver.Search; +using Xunit; + +namespace MongoDB.Driver.Tests.Search +{ + public class SearchScoreDefinitionBuilderTests + { + [Fact] + public void Boost() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Boost(1), + "{ boost: { value: 1 } }"); + AssertRendered( + subject.Boost("x"), + "{ boost: { path: 'x' } }"); + AssertRendered( + subject.Boost("x", 1), + "{ boost: { path: 'x', undefined: 1 } }"); + AssertRendered( + subject.Boost(p => p.Age, 1), + "{ boost: { path: 'age', undefined: 1 } }"); + } + + [Fact] + public void Boost_typed() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Boost(x => x.Age), + "{ boost: { path: 'age' } }"); + AssertRendered( + subject.Boost("age"), + "{ boost: { path: 'age' } }"); + } + + [Fact] + public void Constant() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Constant(1), + "{ constant: { value: 1 } }"); + } + + [Fact] + public void Function() + { + var subject = CreateSubject(); + var functionBuilder = new SearchScoreFunctionBuilder(); + + AssertRendered( + subject.Function(functionBuilder.Path(p => p.Age)), + "{ function: { path: 'age' } }"); + AssertRendered( + subject.Function(functionBuilder.Path("age")), + "{ function: { path: 'age' } }"); + } + + private void AssertRendered(SearchScoreDefinition score, string expected) => + AssertRendered(score, BsonDocument.Parse(expected)); + + private void AssertRendered(SearchScoreDefinition score, BsonDocument expected) + { + var documentSerializer = BsonSerializer.SerializerRegistry.GetSerializer(); + var renderedQuery = score.Render(documentSerializer, BsonSerializer.SerializerRegistry); + + renderedQuery.Should().BeEquivalentTo(expected); + } + + private SearchScoreDefinitionBuilder CreateSubject() => + new SearchScoreDefinitionBuilder(); + + private class Person + { + [BsonElement("age")] + public int Age { get; set; } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Search/SearchSpanDefinitionBuilderTests.cs b/tests/MongoDB.Driver.Tests/Search/SearchSpanDefinitionBuilderTests.cs new file mode 100644 index 00000000000..2a020e5bae7 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Search/SearchSpanDefinitionBuilderTests.cs @@ -0,0 +1,173 @@ +/* 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.Collections.Generic; +using FluentAssertions; +using MongoDB.Bson; +using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver.Search; +using Xunit; + +namespace MongoDB.Driver.Tests.Search +{ + public class SearchSpanDefinitionBuilderTests + { + [Fact] + public void First() + { + var subject = CreateSubject(); + + AssertRendered( + subject.First(subject.Term("x", "foo"), 5), + "{ first: { operator: { term: { query: 'foo', path: 'x' } }, endPositionLte: 5 } }"); + } + + [Fact] + public void First_typed() + { + var subject = CreateSubject(); + + AssertRendered( + subject.First(subject.Term(x => x.Biography, "born"), 5), + "{ first: { operator: { term: { query: 'born', path: 'bio' } }, endPositionLte: 5 } }"); + AssertRendered( + subject.First(subject.Term("Biography", "born"), 5), + "{ first: { operator: { term: { query: 'born', path: 'bio' } }, endPositionLte: 5 } }"); + } + + [Fact] + public void Near() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Near( + new List>() + { + subject.Term("x", "foo"), + subject.Term("x", "bar") + }, + 5, + inOrder: true), + "{ near: { clauses: [{ term: { query: 'foo', path: 'x' } }, { term: { query: 'bar', path: 'x' } }], slop: 5, inOrder: true } }"); + } + + [Fact] + public void Near_typed() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Near( + new List>() + { + subject.Term(x => x.Biography, "born"), + subject.Term(x => x.Biography, "school") + }, + 5, + inOrder: true), + "{ near: { clauses: [{ term: { query: 'born', path: 'bio' } }, { term: { query: 'school', path: 'bio' } }], slop: 5, inOrder: true } }"); + AssertRendered( + subject.Near( + new List>() + { + subject.Term("Biography", "born"), + subject.Term("Biography", "school") + }, + 5, + inOrder: true), + "{ near: { clauses: [{ term: { query: 'born', path: 'bio' } }, { term: { query: 'school', path: 'bio' } }], slop: 5, inOrder: true } }"); + } + + [Fact] + public void Or() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Or( + subject.Term("x", "foo"), + subject.Term("x", "bar")), + "{ or: { clauses: [{ term: { query: 'foo', path: 'x' } }, { term: { query: 'bar', path: 'x' } }] } }"); + } + + [Fact] + public void Or_typed() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Or( + subject.Term(x => x.Biography, "engineer"), + subject.Term(x => x.Biography, "developer")), + "{ or: { clauses: [{ term: { query: 'engineer', path: 'bio' } }, { term: { query: 'developer', path: 'bio' } }] } }"); + AssertRendered( + subject.Or( + subject.Term("Biography", "engineer"), + subject.Term("Biography", "developer")), + "{ or: { clauses: [{ term: { query: 'engineer', path: 'bio' } }, { term: { query: 'developer', path: 'bio' } }] } }"); + } + + [Fact] + public void Subtract() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Subtract( + subject.Term("x", "foo"), + subject.Term("x", "bar")), + "{ subtract: { include: { term: { query: 'foo', path: 'x' } }, exclude: { term: { query: 'bar', path: 'x' } } } }"); + } + + [Fact] + public void Subtract_typed() + { + var subject = CreateSubject(); + + AssertRendered( + subject.Subtract( + subject.Term(x => x.Biography, "engineer"), + subject.Term(x => x.Biography, "train")), + "{ subtract: { include: { term: { query: 'engineer', path: 'bio' } }, exclude: { term: { query: 'train', path: 'bio' } } } }"); + AssertRendered( + subject.Subtract( + subject.Term("Biography", "engineer"), + subject.Term("Biography", "train")), + "{ subtract: { include: { term: { query: 'engineer', path: 'bio' } }, exclude: { term: { query: 'train', path: 'bio' } } } }"); + } + + private void AssertRendered(SearchSpanDefinition span, string expected) => + AssertRendered(span, BsonDocument.Parse(expected)); + + private void AssertRendered(SearchSpanDefinition span, BsonDocument expected) + { + var documentSerializer = BsonSerializer.SerializerRegistry.GetSerializer(); + var renderedSpan = span.Render(documentSerializer, BsonSerializer.SerializerRegistry); + + renderedSpan.Should().BeEquivalentTo(expected); + } + + private SearchSpanDefinitionBuilder CreateSubject() => + new SearchSpanDefinitionBuilder(); + + private class Person + { + [BsonElement("bio")] + public string Biography { get; set; } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Specifications/Runner/DisposableJsonDrivenTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/Runner/DisposableJsonDrivenTestRunner.cs index aa4f6179b46..dd7d1b28319 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/Runner/DisposableJsonDrivenTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/Runner/DisposableJsonDrivenTestRunner.cs @@ -34,23 +34,12 @@ public DisposableJsonDrivenTestRunner(ITestOutputHelper testOutputHelper) { } - public override void Dispose() + // protected methods + protected override void DisposeInternal() { - Dispose(disposing: true); - GC.SuppressFinalize(this); - - base.Dispose(); - } - - // private methods - private void Dispose(bool disposing) - { - if (disposing) + foreach (var disposable in _disposables) { - foreach (var disposable in _disposables) - { - disposable.Dispose(); - } + disposable.Dispose(); } } } diff --git a/tests/MongoDB.Driver.Tests/Specifications/Runner/MongoClientJsonDrivenTestRunnerBase.cs b/tests/MongoDB.Driver.Tests/Specifications/Runner/MongoClientJsonDrivenTestRunnerBase.cs index b3dfdb48662..3999f48070d 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/Runner/MongoClientJsonDrivenTestRunnerBase.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/Runner/MongoClientJsonDrivenTestRunnerBase.cs @@ -18,6 +18,7 @@ using System.Linq; using System.Threading; using FluentAssertions; +using Microsoft.Extensions.Logging; using MongoDB.Bson; using MongoDB.Bson.TestHelpers; using MongoDB.Bson.TestHelpers.JsonDrivenTests; @@ -34,8 +35,9 @@ using MongoDB.Driver.Core.TestHelpers.XunitExtensions; using MongoDB.Driver.TestHelpers; using MongoDB.Driver.Tests.JsonDrivenTests; -using Xunit; using Xunit.Abstractions; +using Xunit.Sdk; +using Reflector = MongoDB.Bson.TestHelpers.Reflector; namespace MongoDB.Driver.Tests.Specifications.Runner { @@ -188,14 +190,14 @@ protected virtual void AssertOutcome(BsonDocument test) protected virtual void CheckServerRequirements(BsonDocument document) { - Logger.Debug("Checking server requirements"); + Logger.LogDebug("Checking server requirements"); if (document.TryGetValue("runOn", out var runOn)) { RequireServer.Check().RunOn(runOn.AsBsonArray); } - Logger.Debug("Checked server requirements"); + Logger.LogDebug("Checked server requirements"); } protected virtual void ConfigureClientSettings(MongoClientSettings settings, BsonDocument test) @@ -214,7 +216,7 @@ protected virtual void ConfigureClientSettings(MongoClientSettings settings, Bso protected virtual void CreateCollection(IMongoClient client, string databaseName, string collectionName, BsonDocument test, BsonDocument shared) { - Logger.Debug("Creating collection {0} in {1} db", databaseName, collectionName); + Logger.LogDebug("Creating collection {0} in {1} db", databaseName, collectionName); var database = client.GetDatabase(databaseName).WithWriteConcern(WriteConcern.WMajority); database.CreateCollection(collectionName); @@ -237,7 +239,7 @@ protected virtual JsonDrivenTestFactory CreateJsonDrivenTestFactory(IMongoClient protected virtual void DropCollection(MongoClient client, string databaseName, string collectionName, BsonDocument test, BsonDocument shared) { - Logger.Debug("Dropping collection {0} in {1} db", databaseName, collectionName); + Logger.LogDebug("Dropping collection {0} in {1} db", databaseName, collectionName); var database = client.GetDatabase(databaseName).WithWriteConcern(WriteConcern.WMajority); database.DropCollection(collectionName); @@ -251,7 +253,7 @@ protected virtual void ExecuteOperations(IMongoClient client, Dictionary()) { - Logger.Debug("Executing operation {0}", operation["name"].AsString); + Logger.LogDebug("Executing operation {0}", operation["name"].AsString); ModifyOperationIfNeeded(operation); var receiver = operation["object"].AsString; @@ -282,7 +284,7 @@ protected virtual string GetDatabaseName(BsonDocument definition) protected virtual void InsertData(IMongoClient client, string databaseName, string collectionName, BsonDocument shared) { - Logger.Debug("Inserting data to {0} in {1} db", databaseName, collectionName); + Logger.LogDebug("Inserting data to {0} in {1} db", databaseName, collectionName); if (shared.Contains(DataKey)) { @@ -303,11 +305,11 @@ protected virtual void ModifyOperationIfNeeded(BsonDocument operation) protected virtual void RunTest(BsonDocument shared, BsonDocument test, EventCapturer eventCapturer) { - Logger.Debug("Running test"); + Logger.LogDebug("Running test"); using (var client = CreateDisposableClient(test, eventCapturer)) { - Logger.Debug("Disposable client created with cluster:{0}", client.Cluster.ClusterId); + Logger.LogDebug("Disposable client created with cluster:{0}", client.Cluster.ClusterId); ExecuteOperations(client, objectMap: null, test, eventCapturer); } @@ -452,7 +454,7 @@ protected FailPoint ConfigureFailPoint(BsonDocument test, IMongoClient client) { if (test.TryGetValue(FailPointKey, out var failPoint)) { - Logger.Debug("Configuring failpoint"); + Logger.LogDebug("Configuring failpoint"); ConfigureFailPointCommand(failPoint.AsBsonDocument); @@ -500,7 +502,7 @@ protected DisposableMongoClient CreateDisposableClient(BsonDocument test, EventC settings.ClusterConfigurator = c => c.Subscribe(eventCapturer); } }, - CreateLogger(), + LoggingSettings, useMultipleShardRouters); } @@ -523,7 +525,7 @@ protected virtual EventCapturer InitializeEventCapturer(EventCapturer eventCaptu protected void SetupAndRunTest(JsonDrivenTestCase testCase) { - Logger.Debug("Running {0}", testCase.Name); + Logger.LogDebug("Running {0}", testCase.Name); CheckServerRequirements(testCase.Shared); SetupAndRunTest(testCase.Shared, testCase.Test); diff --git a/tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake/AtlasDataLakeTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake-testing/AtlasDataLakeTestRunner.cs similarity index 95% rename from tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake/AtlasDataLakeTestRunner.cs rename to tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake-testing/AtlasDataLakeTestRunner.cs index 56f4fb422b1..dfe92b4d497 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake/AtlasDataLakeTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake-testing/AtlasDataLakeTestRunner.cs @@ -16,12 +16,12 @@ using System.Collections.Generic; using MongoDB.Bson; using MongoDB.Bson.TestHelpers.JsonDrivenTests; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Tests.Specifications.Runner; using Xunit; using Xunit.Abstractions; -namespace MongoDB.Driver.Tests.Specifications.atlas_data_lake +namespace MongoDB.Driver.Tests.Specifications.atlas_data_lake_testing { [Trait("Category", "AtlasDataLake")] public class AtlasDataLakeTestRunner : MongoClientJsonDrivenTestRunnerBase @@ -35,7 +35,7 @@ public AtlasDataLakeTestRunner(ITestOutputHelper testOutputHelper) { } - [SkippableTheory] + [Theory] [ClassData(typeof(TestCaseFactory))] public void Run(JsonDrivenTestCase testCase) { @@ -82,7 +82,7 @@ protected override string GetDatabaseName(BsonDocument definition) private class TestCaseFactory : JsonDrivenTestCaseFactory { // protected properties - protected override string PathPrefix => "MongoDB.Driver.Tests.Specifications.atlas_data_lake.tests."; + protected override string PathPrefix => "MongoDB.Driver.Tests.Specifications.atlas_data_lake_testing.tests."; // protected methods protected override IEnumerable CreateTestCases(BsonDocument document) diff --git a/tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake/prose-tests/AtlasDataLakeProseTests.cs b/tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake-testing/prose-tests/AtlasDataLakeProseTests.cs similarity index 96% rename from tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake/prose-tests/AtlasDataLakeProseTests.cs rename to tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake-testing/prose-tests/AtlasDataLakeProseTests.cs index 832b88977fc..f9c806259c3 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake/prose-tests/AtlasDataLakeProseTests.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/atlas-data-lake-testing/prose-tests/AtlasDataLakeProseTests.cs @@ -16,19 +16,19 @@ using System.Linq; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core; using MongoDB.Driver.Core.Events; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; using Xunit; -namespace MongoDB.Driver.Tests.Specifications.atlas_data_lake.prose_tests +namespace MongoDB.Driver.Tests.Specifications.atlas_data_lake_testing.prose_tests { [Trait("Category", "AtlasDataLake")] public class AtlasDataLakeProseTests { - [SkippableFact] + [Fact] public void Driver_should_connect_to_AtlasDataLake_without_authentication() { RequireEnvironment.Check().EnvironmentVariable("ATLAS_DATA_LAKE_TESTS_ENABLED"); @@ -39,7 +39,7 @@ public void Driver_should_connect_to_AtlasDataLake_without_authentication() } } - [SkippableFact] + [Fact] public void Driver_should_connect_to_AtlasDataLake_with_SCRAM_SHA_1() { RequireEnvironment.Check().EnvironmentVariable("ATLAS_DATA_LAKE_TESTS_ENABLED"); @@ -59,7 +59,7 @@ public void Driver_should_connect_to_AtlasDataLake_with_SCRAM_SHA_1() } } - [SkippableFact] + [Fact] public void Driver_should_connect_to_AtlasDataLake_with_SCRAM_SHA_256() { RequireEnvironment.Check().EnvironmentVariable("ATLAS_DATA_LAKE_TESTS_ENABLED"); @@ -79,7 +79,7 @@ public void Driver_should_connect_to_AtlasDataLake_with_SCRAM_SHA_256() } } - [SkippableFact] + [Fact] public void KillCursors_should_return_expected_result() { RequireEnvironment.Check().EnvironmentVariable("ATLAS_DATA_LAKE_TESTS_ENABLED"); diff --git a/tests/MongoDB.Driver.Tests/Specifications/auth/AuthTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/auth/AuthTestRunner.cs index 069b3c218a9..d7b2e735127 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/auth/AuthTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/auth/AuthTestRunner.cs @@ -20,7 +20,7 @@ using MongoDB.Bson; using MongoDB.Bson.TestHelpers; using MongoDB.Bson.TestHelpers.JsonDrivenTests; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Authentication; using Xunit; @@ -28,7 +28,7 @@ namespace MongoDB.Driver.Tests.Specifications.auth { public class AuthTestRunner { - [SkippableTheory] + [Theory] [ClassData(typeof(TestCaseFactory))] public void RunTestDefinition(JsonDrivenTestCase testCase) { diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson-corpus/BsonCorpusTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/bson-corpus/BsonCorpusTestRunner.cs similarity index 98% rename from tests/MongoDB.Bson.Tests/Specifications/bson-corpus/BsonCorpusTestRunner.cs rename to tests/MongoDB.Driver.Tests/Specifications/bson-corpus/BsonCorpusTestRunner.cs index ae3e3efd8c0..be2120cbb3e 100644 --- a/tests/MongoDB.Bson.Tests/Specifications/bson-corpus/BsonCorpusTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/bson-corpus/BsonCorpusTestRunner.cs @@ -19,17 +19,18 @@ using System.Linq; using System.Text.RegularExpressions; using FluentAssertions; +using MongoDB.Bson; using MongoDB.Bson.IO; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Bson.TestHelpers.JsonDrivenTests; using Xunit; -namespace MongoDB.Bson.Tests.Specifications.bson_corpus +namespace MongoDB.Driver.Tests.Specifications.bson_corpus { public class BsonCorpusTestRunner { - [SkippableTheory] + [Theory] [ClassData(typeof(TestCaseFactory))] public void RunTestDefinition(JsonDrivenTestCase testCase) { @@ -300,7 +301,7 @@ private class TestCaseFactory : JsonDrivenTestCaseFactory #endregion // protected properties - protected override string PathPrefix => "MongoDB.Bson.Tests.Specifications.bson_corpus.tests."; + protected override string PathPrefix => "MongoDB.Driver.Tests.Specifications.bson_corpus.tests."; // protected methods protected override IEnumerable CreateTestCases(BsonDocument document) diff --git a/tests/MongoDB.Bson.Tests/Specifications/bson/TestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/bson-decimal128/TestRunner.cs similarity index 97% rename from tests/MongoDB.Bson.Tests/Specifications/bson/TestRunner.cs rename to tests/MongoDB.Driver.Tests/Specifications/bson-decimal128/TestRunner.cs index d208c39bd4d..c637063f3c5 100644 --- a/tests/MongoDB.Bson.Tests/Specifications/bson/TestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/bson-decimal128/TestRunner.cs @@ -13,20 +13,19 @@ * limitations under the License. */ -using System; using System.Collections; using System.Collections.Generic; -using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using FluentAssertions; +using MongoDB.Bson; using MongoDB.Bson.IO; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using Xunit; -namespace MongoDB.Bson.Specifications.bson +namespace MongoDB.Driver.Tests.Specifications.bson_decimal128 { public class TestRunner { @@ -160,7 +159,7 @@ private class TestCaseFactory : IEnumerable { public IEnumerator GetEnumerator() { - const string prefix = "MongoDB.Bson.Tests.Specifications.bson.tests."; + const string prefix = "MongoDB.Driver.Tests.Specifications.bson_decimal128.tests."; var executingAssembly = typeof(TestCaseFactory).GetTypeInfo().Assembly; var enumerable = executingAssembly .GetManifestResourceNames() diff --git a/tests/MongoDB.Driver.Tests/Specifications/change-streams/ChangeStreamUnifiedTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/change-streams/ChangeStreamUnifiedTestRunner.cs index e72f3aac74c..2a09759e909 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/change-streams/ChangeStreamUnifiedTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/change-streams/ChangeStreamUnifiedTestRunner.cs @@ -33,11 +33,11 @@ public ChangeStreamUnifiedTestRunner(ITestOutputHelper testOutputHelper) } // public methods - [SkippableTheory] + [Theory] [ClassData(typeof(TestCaseFactory))] public void Run(JsonDrivenTestCase testCase) { - using (var runner = new UnifiedTestRunner()) + using (var runner = new UnifiedTestRunner(loggingService: this)) { runner.Run(testCase); } diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/ClientSideEncryptionTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/ClientSideEncryptionTestRunner.cs index 0fcb11068d0..317c0099367 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/ClientSideEncryptionTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/ClientSideEncryptionTestRunner.cs @@ -15,11 +15,10 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Linq; using MongoDB.Bson; using MongoDB.Bson.TestHelpers.JsonDrivenTests; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Events; using MongoDB.Driver.Encryption; using MongoDB.Driver.TestHelpers; @@ -30,6 +29,7 @@ namespace MongoDB.Driver.Tests.Specifications.client_side_encryption { [Trait("Category", "CSFLE")] + [Trait("Category", "Serverless")] public class ClientSideEncryptionTestRunner : MongoClientJsonDrivenTestRunnerBase { #region static @@ -42,7 +42,7 @@ public ClientSideEncryptionTestRunner(ITestOutputHelper testOutputHelper) { } - [SkippableTheory] + [Theory] [ClassData(typeof(TestCaseFactory))] public void Run(JsonDrivenTestCase testCase) { @@ -266,7 +266,20 @@ private AutoEncryptionOptions ConfigureAutoEncryptionOptions(BsonDocument autoEn case "bypassQueryAnalysis": autoEncryptionOptions = autoEncryptionOptions.With(bypassQueryAnalysis: option.Value.ToBoolean()); break; - + case "extraOptions": + foreach (var extraOption in option.Value.AsBsonDocument.Elements) + { + switch (extraOption.Name) + { + case "mongocryptdBypassSpawn": + extraOptions.Add(extraOption.Name, extraOption.Value.ToBoolean()); + break; + default: + throw new Exception($"Unexpected extra option {extraOption.Name}."); + } + } + autoEncryptionOptions = autoEncryptionOptions.With(extraOptions: extraOptions); + break; default: throw new Exception($"Unexpected auto encryption option {option.Name}."); } @@ -330,11 +343,6 @@ private void ReplaceTypeAssertionWithActual(BsonArray actual, BsonArray expected public class TestCaseFactory : JsonDrivenTestCaseFactory { #region static - private static readonly string[] __ignoredTestNames = - { - // https://jira.mongodb.org/browse/SPEC-1403 - "maxWireVersion.json:operation fails with maxWireVersion < 8" - }; private static readonly string[] __versionedApiIgnoredTestNames = { // https://jira.mongodb.org/browse/SERVER-58293 @@ -348,7 +356,7 @@ public class TestCaseFactory : JsonDrivenTestCaseFactory // protected methods protected override IEnumerable CreateTestCases(BsonDocument document) { - var testCases = base.CreateTestCases(document).Where(test => !__ignoredTestNames.Any(ignoredName => test.Name.EndsWith(ignoredName))); + var testCases = base.CreateTestCases(document); if (CoreTestConfiguration.RequireApiVersion) { testCases = testCases.Where(test => !__versionedApiIgnoredTestNames.Any(ignoredName => test.Name.EndsWith(ignoredName))); diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/ClientSideEncryptionUnifiedTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/ClientSideEncryptionUnifiedTestRunner.cs index 909fb7b54dc..f64d6e74713 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/ClientSideEncryptionUnifiedTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/ClientSideEncryptionUnifiedTestRunner.cs @@ -16,7 +16,7 @@ using System.Collections.Generic; using MongoDB.Bson; using MongoDB.Bson.TestHelpers.JsonDrivenTests; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.TestHelpers.Logging; using MongoDB.Driver.TestHelpers; using MongoDB.Driver.Tests.UnifiedTestOperations; @@ -35,14 +35,14 @@ public ClientSideEncryptionUnifiedTestRunner(ITestOutputHelper testOutputHelper) } // public methods - [SkippableTheory] + [Theory] [ClassData(typeof(TestCaseFactory))] public void Run(JsonDrivenTestCase testCase) { var testCaseNameLower = testCase.Name.ToLower(); if (testCaseNameLower.Contains("kmip") || - testCase.Shared.ToString().ToLower().Contains("kmip")) // also calls kmip kms + testCase.Shared.ToString().ToLower().Contains("kmip")) { // kmip requires configuring kms mock server RequireEnvironment.Check().EnvironmentVariable("KMS_MOCK_SERVERS_ENABLED"); @@ -54,7 +54,7 @@ public void Run(JsonDrivenTestCase testCase) .SkipWhen(() => testCaseNameLower.Contains("gcp") || testCaseNameLower.Contains("rewrap with"), SupportedOperatingSystem.Linux, SupportedTargetFramework.NetStandard20) // gcp is supported starting from netstandard2.1 .SkipWhen(() => testCaseNameLower.Contains("gcp") || testCaseNameLower.Contains("rewrap with"), SupportedOperatingSystem.MacOS, SupportedTargetFramework.NetStandard20); // gcp is supported starting from netstandard2.1 - using (var runner = new UnifiedTestRunner()) + using (var runner = new UnifiedTestRunner(loggingService: this)) { runner.Run(testCase); } diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/EncryptionTestHelper.cs b/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/EncryptionTestHelper.cs index d8a53624081..2362443a24d 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/EncryptionTestHelper.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/EncryptionTestHelper.cs @@ -16,20 +16,26 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Collections.ObjectModel; +using System.Diagnostics; +using System.IO; using System.Linq; using System.Security.Cryptography.X509Certificates; using FluentAssertions; using MongoDB.Bson; +using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Misc; +using MongoDB.Driver.Encryption; namespace MongoDB.Driver.Tests.Specifications.client_side_encryption { public static class EncryptionTestHelper { - private static readonly IReadOnlyDictionary> __kmsProviders; + private static readonly Lazy>> __kmsProviders = new Lazy>>(ConfigureDefaultKmsProviders, isThreadSafe: true); + private const string KmsProviderFilterDelimiter = ";"; + private static readonly string __defaultMongocryptdPath = Environment.GetEnvironmentVariable("MONGODB_BINARIES") ?? ""; + private static readonly Lazy<(bool IsValid, SemanticVersion Version)> __defaultCsfleSetupState = new Lazy<(bool IsValid, SemanticVersion Version)>(IsDefaultCsfleSetupValid, isThreadSafe: true); - static EncryptionTestHelper() + private static IReadOnlyDictionary> ConfigureDefaultKmsProviders() { var kmsProviders = new Dictionary> { @@ -88,7 +94,7 @@ static EncryptionTestHelper() }); } - __kmsProviders = kmsProviders; + return kmsProviders; string GetEnvironmentVariableOrDefaultOrThrowIfNothing(string variableName, string defaultValue = null) => Environment.GetEnvironmentVariable(variableName) ?? @@ -100,12 +106,22 @@ string GetEnvironmentVariableOrDefaultOrThrowIfNothing(string variableName, stri public static void ConfigureDefaultExtraOptions(Dictionary extraOptions) { + if (CoreTestConfiguration.MaxWireVersion < WireVersion.Server42) + { + // CSFLE is supported starting from server 4.2 + return; + } + Ensure.IsNotNull(extraOptions, nameof(extraOptions)); - if (!extraOptions.ContainsKey("mongocryptdSpawnPath")) + if (!extraOptions.TryGetValue("mongocryptdSpawnPath", out object value)) { - var mongocryptdSpawnPath = Environment.GetEnvironmentVariable("MONGODB_BINARIES") ?? ""; - extraOptions.Add("mongocryptdSpawnPath", mongocryptdSpawnPath); + extraOptions.Add("mongocryptdSpawnPath", __defaultMongocryptdPath); + + if (!__defaultCsfleSetupState.Value.IsValid) + { + throw new Exception($"The configured mongocryptd version {__defaultCsfleSetupState.Value.Version} doesn't match the server version {CoreTestConfiguration.ServerVersion}."); + } } var mongocryptdPort = Environment.GetEnvironmentVariable("FLE_MONGOCRYPTD_PORT"); @@ -162,6 +178,32 @@ public static void ConfigureDefaultExtraOptions(Dictionary extra } } + public static string CreateKmsProviderFilter(params string[] kmsProviders) => string.Join(KmsProviderFilterDelimiter, kmsProviders); + + public static BsonDocument CreateMasterKey(string kmsProvider) => kmsProvider switch + { + "local" => null, + "aws" => new BsonDocument + { + { "region", "us-east-1" }, + { "key", "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" } + }, + "azure" => new BsonDocument + { + { "keyName", "key-name-csfle" }, + { "keyVaultEndpoint", "key-vault-csfle.vault.azure.net" } + }, + "gcp" => new BsonDocument + { + { "projectId", "devprod-drivers" }, + { "location", "global" }, + { "keyRing", "key-ring-csfle" }, + { "keyName", "key-name-csfle" } + }, + "kmip" => new BsonDocument(), + _ => throw new ArgumentException($"Incorrect kms provider {kmsProvider}.", nameof(kmsProvider)), + }; + public static Dictionary CreateTlsOptionsIfAllowed( IReadOnlyDictionary> kmsProviders, Func allowClientCertificateFunc) @@ -202,7 +244,8 @@ public static SslSettings CreateTlsOptionsIfAllowed( public static IReadOnlyDictionary> GetKmsProviders(string filter = null) => __kmsProviders - .Where(kms => filter == null || kms.Key == filter) + .Value + .Where(kms => filter == null || filter.Split(new[] { KmsProviderFilterDelimiter }, StringSplitOptions.None).Contains(kms.Key)) .ToDictionary( k => k.Key, v => (IReadOnlyDictionary)v.Value.ToDictionary(ik => ik.Key, iv => iv.Value)); // ensure that inner dictionary is a new instance @@ -268,5 +311,72 @@ public static IReadOnlyDictionary> P bool IsPlaceholder(BsonValue value) => value.IsBsonDocument && value.AsBsonDocument.Contains("$$placeholder"); string GetFromEnvVariables(string kmsProvider, string key) => kmsProvidersFromEnvs[kmsProvider][key].ToString(); } + + // private methods + /// + /// Ensure that used mongocryptd corresponds to used server. + /// + private static (bool IsValid, SemanticVersion MongocryptdVersion) IsDefaultCsfleSetupValid() + { + var cryptSharedLibPath = CoreTestConfiguration.GetCryptSharedLibPath(); + if (cryptSharedLibPath != null) + { + var dummyNamespace = CollectionNamespace.FromFullName("db.dummy"); + var autoEncryptionOptions = new AutoEncryptionOptions( + dummyNamespace, + kmsProviders: GetKmsProviders(filter: "local"), + extraOptions: new Dictionary() { { "cryptSharedLibPath", cryptSharedLibPath } }); + using (var cryptClient = CryptClientCreator.CreateCryptClient(autoEncryptionOptions.ToCryptClientSettings())) + { + if (cryptClient.CryptSharedLibraryVersion != null) + { + // csfle shared library code path + return (IsValid: true, MongocryptdVersion: null); + } + else + { + // we will still try using mongocryptd + } + } + } + + var configuredMongocryptdVersion = GetMongocryptdVersion(__defaultMongocryptdPath); + if (CompareSemanticVersionsAsReleased(configuredMongocryptdVersion, CoreTestConfiguration.ServerVersion) < 0) + { + return (IsValid: false, MongocryptdVersion: configuredMongocryptdVersion); + } + + return (IsValid: true, MongocryptdVersion: configuredMongocryptdVersion); + + SemanticVersion GetMongocryptdVersion(string mongocryptdPath) + { + mongocryptdPath = File.Exists(mongocryptdPath) ? mongocryptdPath : Path.Combine(mongocryptdPath, "mongocryptd"); + using (var process = new Process()) + { + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.Arguments = "--version"; + process.StartInfo.FileName = mongocryptdPath; + process.StartInfo.CreateNoWindow = true; + process.StartInfo.UseShellExecute = false; + + if (!process.Start()) + { + // skip it. This case can happen if no new process resource is started + // (for example, if an existing process is reused) + } + + var output = process.StandardOutput.ReadToEnd(); + var buildInfoBody = output.Substring(output.IndexOf("Build Info") + 12 /*key length*/); + return SemanticVersion.Parse(buildInfoBody); + } + } + + int CompareSemanticVersionsAsReleased(SemanticVersion a, SemanticVersion b) + { + var aReleased = new SemanticVersion(a.Major, a.Minor, a.Patch); + var bReleased = new SemanticVersion(b.Major, b.Minor, b.Patch); + return aReleased.CompareTo(bReleased); + } + } } } diff --git a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/ClientEncryptionProseTests.cs b/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/ClientEncryptionProseTests.cs index 90c153a64e2..499f7e801f7 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/ClientEncryptionProseTests.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/client-side-encryption/prose-tests/ClientEncryptionProseTests.cs @@ -16,18 +16,23 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.IO; using System.Linq; +using System.Net; +using System.Net.Http; using System.Net.Security; using System.Net.Sockets; using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; using System.Threading; +using System.Threading.Tasks; +using Amazon.Runtime; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers; using MongoDB.Bson.TestHelpers.JsonDrivenTests; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core; +using MongoDB.Driver.Core.Authentication.External; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Events; @@ -41,6 +46,8 @@ using MongoDB.Libmongocrypt; using Xunit; using Xunit.Abstractions; +using Xunit.Sdk; +using Reflector = MongoDB.Bson.TestHelpers.Reflector; namespace MongoDB.Driver.Tests.Specifications.client_side_encryption.prose_tests { @@ -78,7 +85,81 @@ public ClientEncryptionProseTests(ITestOutputHelper testOutputHelper) } // public methods - [SkippableTheory] + [Theory] + [ParameterAttributeData] + public void AutomaticDataEncryptionKeysTest( + [Range(1, 4)] int testCase, + [Values(false, true)] bool async) + { + RequireServer.Check().Supports(Feature.Csfle2).ClusterTypes(ClusterType.ReplicaSet, ClusterType.Sharded, ClusterType.LoadBalanced); + + var kmsProvider = "local"; + using (var client = ConfigureClient()) + using (var clientEncryption = ConfigureClientEncryption(client, kmsProviderFilter: kmsProvider)) + { + var encryptedFields = BsonDocument.Parse($@" + {{ + fields: + [ + {{ + path: ""ssn"", + bsonType: ""string"", + keyId: null + }} + ] + }}"); + + DropCollection(__collCollectionNamespace, encryptedFields); + + RunTestCase(testCase); + + void RunTestCase(int testCase) + { + switch (testCase) + { + case 1: // Case 1: Simple Creation and Validation + { + var collection = CreateEncryptedCollection(client, clientEncryption, __collCollectionNamespace, encryptedFields, kmsProvider, async, out _); + + var exception = Record.Exception(() => Insert(collection, async, new BsonDocument("ssn", "123-45-6789"))); + exception.Should().BeOfType>().Which.Message.Should().Contain("Document failed validation"); + } + break; + case 2: // Case 2: Missing ``encryptedFields`` + { + var exception = Record.Exception(() => CreateEncryptedCollection(client, clientEncryption, __collCollectionNamespace, encryptedFields: null, kmsProvider, async, out _)); + + exception + .Should().BeOfType().Which.InnerException + .Should().BeOfType().Which.Message.Should().Contain("There are no encrypted fields defined for the collection.") ; + } + break; + case 3: // Case 3: Invalid ``keyId`` + { + var effectiveEncryptedFields = encryptedFields.DeepClone(); + effectiveEncryptedFields["fields"].AsBsonArray[0].AsBsonDocument["keyId"] = false; + var exception = Record.Exception(() => CreateEncryptedCollection(client, clientEncryption, __collCollectionNamespace, effectiveEncryptedFields.AsBsonDocument, kmsProvider, async, out _)); + exception + .Should().BeOfType().Which.InnerException + .Should().BeOfType().Which.Message.Should().Contain("BSON field 'create.encryptedFields.fields.keyId' is the wrong type 'bool', expected type 'binData'"); + } + break; + case 4: // Case 4: Insert encrypted value + { + var createCollectionOptions = new CreateCollectionOptions { EncryptedFields = encryptedFields }; + var collection = CreateEncryptedCollection(client, clientEncryption, __collCollectionNamespace, createCollectionOptions, kmsProvider, async, out var effectiveEncryptedFields); + var dataKey = effectiveEncryptedFields["fields"].AsBsonArray[0].AsBsonDocument["keyId"].AsGuid; // get generated datakey + var encryptedValue = ExplicitEncrypt(clientEncryption, new EncryptOptions(algorithm: EncryptionAlgorithm.Unindexed, keyId: dataKey), "123-45-6789", async); // use explicit encryption to encrypt data before inserting + Insert(collection, async, new BsonDocument("ssn", encryptedValue)); + } + break; + default: throw new Exception($"Unexpected test case {testCase}."); + } + } + } + } + + [Theory] [ParameterAttributeData] public void BsonSizeLimitAndBatchSizeSplittingTest( [Values(false, true)] bool async) @@ -243,7 +324,76 @@ public void BsonSizeLimitAndBatchSizeSplittingTest( } } - [SkippableTheory] + [Theory] + [ParameterAttributeData] + public void BypassMongocryptdClientWhenSharedLibraryTest( + [Values(false, true)] bool async) + { + RequireServer.Check().Supports(Feature.ClientSideEncryption); + RequireEnvironment.Check().EnvironmentVariable("CRYPT_SHARED_LIB_PATH", isDefined: true, allowEmpty: false); + // socket.Close can hang on non windows OS. Might be related to this issue: https://github.com/dotnet/runtime/issues/47342 + RequirePlatform + .Check() + .SkipWhen(SupportedOperatingSystem.Linux) + .SkipWhen(SupportedOperatingSystem.MacOS); + + const int mongocryptPort = 27030; + var timeout = TimeSpan.FromSeconds(3); + var extraOptions = new Dictionary + { + { "mongocryptdURI", $"mongodb://localhost:{mongocryptPort}" } + }; + + var mongocryptdIpAddress = IPAddress.Parse("127.0.0.1"); + TcpListener tcpListener = null; + try + { + tcpListener = new TcpListener(mongocryptdIpAddress, port: mongocryptPort); + var listenerThread = new Thread(new ParameterizedThreadStart(ThreadStart)) { IsBackground = true }; + + using (var clientEncrypted = ConfigureClientEncrypted(kmsProviderFilter: "local", extraOptions: extraOptions)) + { + var coll = GetCollection(clientEncrypted, __collCollectionNamespace); + + listenerThread.Start(tcpListener); + + _ = Record.Exception(() => Insert(coll, async, new BsonDocument("unencrypted", "test"))); + + if (listenerThread.Join(timeout)) + { + // This exception is never thrown when mognocryptd mongoClient is not spawned which is expected behavior. + // However, if we intentionally break that logic to spawn mongocryptd mongoClient regardless of shared library, + // this exception sometimes won't be thrown. In all such cases the spent time in listenerThread.Join is higher + // or really close to timeout. So it's unclear why Join doesn't throw in that cases, but that logic is unrelated + // to the driver and csfle in particular. We rely on the fact that even if we break this logic, + // we run this test more than once. + throw new Exception($"Listener accepted a tcp call for moncgocryptd during {timeout}."); + } + } + } + finally + { + tcpListener?.Stop(); + } + + void ThreadStart(object param) + { + try + { + var tcpListener = (TcpListener)param; + tcpListener.Start(); + using var client = tcpListener.AcceptTcpClient(); + // Perform a blocking call to accept requests. + // if we're here, then something queries port 27030. + } + catch (SocketException) + { + // listener stopped outside thread + } + } + } + + [Theory] [ParameterAttributeData] public void BypassSpawningMongocryptdViaMongocryptdBypassSpawnTest( [Values(false, true)] bool async) @@ -264,10 +414,6 @@ public void BypassSpawningMongocryptdViaMongocryptdBypassSpawnTest( kmsProviderFilter: "local", extraOptions: extraOptions)) { - var datakeys = GetCollection(client, __keyVaultCollectionNamespace); - var externalKey = JsonFileReader.Instance.Documents["external.external-key.json"]; - Insert(datakeys, async, externalKey); - var coll = GetCollection(clientEncrypted, __collCollectionNamespace); var exception = Record.Exception(() => Insert(coll, async, new BsonDocument("encrypted", "test"))); @@ -275,25 +421,21 @@ public void BypassSpawningMongocryptdViaMongocryptdBypassSpawnTest( } } - [SkippableTheory] + public enum BypassSpawningMongocryptd + { + BypassAutoEncryption, + BypassQueryAnalysis, + SharedLibrary + } + + [Theory] [ParameterAttributeData] public void BypassSpawningMongocryptdTest( - [Values(false, true)] bool bypassAutoEncryption, // true - BypassAutoEncryption, false - BypassQueryAnalysis + [Values(BypassSpawningMongocryptd.BypassQueryAnalysis, BypassSpawningMongocryptd.BypassAutoEncryption, BypassSpawningMongocryptd.SharedLibrary)] BypassSpawningMongocryptd bypassSpawning, [Values(false, true)] bool async) { - RequireServer.Check().Supports(Feature.ClientSideEncryption); - RequireEnvironment.Check().EnvironmentVariable("CRYPT_SHARED_LIB_PATH", isDefined: false); - - var extraOptions = new Dictionary - { - { "mongocryptdSpawnArgs", new [] { "--pidfilepath=bypass-spawning-mongocryptd.pid", "--port=27021" } }, - }; - using (var mongocryptdClient = new DisposableMongoClient(new MongoClient("mongodb://localhost:27021/?serverSelectionTimeoutMS=10000"), CreateLogger())) - using (var clientEncrypted = ConfigureClientEncrypted( - kmsProviderFilter: "local", - bypassAutoEncryption: bypassAutoEncryption, // bypass options are mutually exclusive for this test - bypassQueryAnalysis: !bypassAutoEncryption, - extraOptions: extraOptions)) + using (var clientEncrypted = EnsureEnvironmentAndConfigureTestClientEncrypted()) + using (var mongocryptdClient = new DisposableMongoClient(new MongoClient("mongodb://localhost:27021/?serverSelectionTimeoutMS=1000"), CreateLogger())) { var coll = GetCollection(clientEncrypted, __collCollectionNamespace); Insert(coll, async, new BsonDocument("unencrypted", "test")); @@ -303,11 +445,47 @@ public void BypassSpawningMongocryptdTest( var exception = Record.Exception(() => adminDatabase.RunCommand(legacyHelloCommand)); exception.Should().BeOfType(); - exception.Message.Should().Contain("A timeout occurred after 10000ms selecting a server"); + exception.Message.Should().Contain("A timeout occurred after 1000ms selecting a server").And.Contain("localhost:27021"); + } + + DisposableMongoClient EnsureEnvironmentAndConfigureTestClientEncrypted() + { + var extraOptions = new Dictionary + { + { "mongocryptdSpawnArgs", new [] { "--pidfilepath=bypass-spawning-mongocryptd.pid", "--port=27021" } }, + }; + var kmsProvider = "local"; + switch (bypassSpawning) + { + case BypassSpawningMongocryptd.BypassAutoEncryption: + RequireServer.Check().Supports(Feature.ClientSideEncryption); + RequireEnvironment.Check().EnvironmentVariable("CRYPT_SHARED_LIB_PATH", isDefined: false); + return ConfigureClientEncrypted(kmsProviderFilter: kmsProvider, bypassAutoEncryption: true, extraOptions: extraOptions); + case BypassSpawningMongocryptd.BypassQueryAnalysis: + RequireServer.Check().Supports(Feature.ClientSideEncryption); + RequireEnvironment.Check().EnvironmentVariable("CRYPT_SHARED_LIB_PATH", isDefined: false); + return ConfigureClientEncrypted(kmsProviderFilter: kmsProvider, bypassQueryAnalysis: true, extraOptions: extraOptions); + case BypassSpawningMongocryptd.SharedLibrary: + { + RequireServer.Check().Supports(Feature.Csfle2).ClusterTypes(ClusterType.ReplicaSet, ClusterType.Sharded, ClusterType.LoadBalanced); + RequireEnvironment.Check().EnvironmentVariable("CRYPT_SHARED_LIB_PATH", isDefined: true, allowEmpty: false); + var clientEncryptedSchema = new BsonDocument("db.coll", JsonFileReader.Instance.Documents["external.external-schema.json"]); + var cryptSharedPath = CoreTestConfiguration.GetCryptSharedLibPath(); + Ensure.That(File.Exists(cryptSharedPath), $"Shared library path {cryptSharedPath} is not valid."); + var effectiveExtraOptions = new Dictionary(extraOptions) + { + { "mongocryptdURI", "mongodb://localhost:27021/db?serverSelectionTimeoutMS=1000" }, + { "cryptSharedLibPath", cryptSharedPath }, + { "cryptSharedLibRequired", true } + }; + return ConfigureClientEncrypted(kmsProviderFilter: kmsProvider, schemaMap: clientEncryptedSchema, extraOptions: effectiveExtraOptions); + } + default: throw new Exception($"Invalid bypass mongocryptd {bypassSpawning} option."); + } } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void CorpusTest( [Values(false, true)] bool useLocalSchema, @@ -477,7 +655,7 @@ EncryptOptions CreateEncryptOptions(string algorithm, string identifier, string }; } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void CreateDataKeyAndDoubleEncryptionTest( [Values("local", "aws", "azure", "gcp", "kmip")] string kmsProvider, @@ -553,7 +731,7 @@ public void CreateDataKeyAndDoubleEncryptionTest( } } - [SkippableTheory] + [Theory] // aws [InlineData("aws", null, null, null)] [InlineData("aws", "kms.us-east-1.amazonaws.com", null, null)] @@ -715,7 +893,7 @@ void TestCase(ClientEncryption testCaseClientEncription, BsonDocument masterKey, } } - [SkippableTheory] + [Theory] [MemberData(nameof(DeadlockTest_MemberData))] public void DeadlockTest( string _, @@ -918,7 +1096,7 @@ IEnumerable CasesWithAsync( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void DecryptionEvents( [Range(1, 4)] int testCase, @@ -1019,6 +1197,7 @@ void RunTestCase(IMongoCollection decryptionEventsCollection, int reply["cursor"]["firstBatch"].AsBsonArray.Single()["encrypted"].AsBsonBinaryData.SubType.Should().Be(BsonBinarySubType.Encrypted); } break; + default: throw new Exception($"Unexpected test case {testCase}."); } } @@ -1031,7 +1210,7 @@ BsonDocument Aggregate(IMongoCollection collection, bool async) } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void ExplicitEncryptionTest( [Range(1, 5)] int testCase, @@ -1160,7 +1339,7 @@ void RunTestCase(IMongoCollection explicitCollectionFromEncryptedC } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void ExternalKeyVaultTest( [Values(false, true)] bool withExternalKeyVault, @@ -1211,7 +1390,7 @@ public void ExternalKeyVaultTest( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void KmsTlsOptionsTest( [Values("aws", "azure", "gcp", "kmip")] string kmsProvider, @@ -1267,6 +1446,12 @@ public void KmsTlsOptionsTest( void AssertException(Exception exception) { +#if NET6_0_OR_GREATER + const string invalidCertificateError = "The remote certificate was rejected by the provided RemoteCertificateValidationCallback."; +#else + const string invalidCertificateError = "The remote certificate is invalid according to the validation procedure."; +#endif + var currentOperatingSystem = OperatingSystemHelper.CurrentOperatingSystem; switch (kmsProvider) { @@ -1300,12 +1485,12 @@ void AssertException(Exception exception) case CertificateType.Expired: AssertCertificate(isExpired: true, invalidHost: false); // Expect an error indicating TLS handshake failed due to an expired certificate. - AssertInnerEncryptionException(exception, "The remote certificate is invalid according to the validation procedure"); + AssertInnerEncryptionException(exception, invalidCertificateError); break; case CertificateType.InvalidHostName: AssertCertificate(isExpired: false, invalidHost: true); // Expect an error indicating TLS handshake failed due to an invalid hostname. - AssertInnerEncryptionException(exception, "The remote certificate is invalid according to the validation procedure"); + AssertInnerEncryptionException(exception, invalidCertificateError); break; default: throw new Exception($"Unexpected certificate type {certificateType} for {kmsProvider}."); } @@ -1333,19 +1518,18 @@ void AssertException(Exception exception) break; case CertificateType.TlsWithClientCert: AssertCertificate(isExpired: null, invalidHost: null); - // Expect an error from libmongocrypt with a message containing the string: "HTTP - // status = 404". This implies TLS handshake succeeded. - AssertInnerEncryptionException(exception, "HTTP status=404"); + // Expect an HTTP 404 error from libmongocrypt. This implies TLS handshake succeeded. + AssertInnerEncryptionException(exception, "404"); break; case CertificateType.Expired: AssertCertificate(isExpired: true, invalidHost: false); // Expect an error indicating TLS handshake failed due to an expired certificate. - AssertInnerEncryptionException(exception, "The remote certificate is invalid according to the validation procedure."); + AssertInnerEncryptionException(exception, invalidCertificateError); break; case CertificateType.InvalidHostName: AssertCertificate(isExpired: false, invalidHost: true); // Expect an error indicating TLS handshake failed due to an invalid hostname. - AssertInnerEncryptionException(exception, "The remote certificate is invalid according to the validation procedure."); + AssertInnerEncryptionException(exception, invalidCertificateError); break; default: throw new Exception($"Unexpected certificate type {certificateType} for {kmsProvider}."); } @@ -1372,19 +1556,18 @@ void AssertException(Exception exception) break; case CertificateType.TlsWithClientCert: AssertCertificate(isExpired: null, invalidHost: null); - // Expect an error from libmongocrypt with a message containing the string: "HTTP - // status = 404". This implies TLS handshake succeeded. - AssertInnerEncryptionException(exception, "HTTP status=404"); + // Expect an HTTP 404 error from libmongocrypt. This implies TLS handshake succeeded. + AssertInnerEncryptionException(exception, "404"); break; case CertificateType.Expired: AssertCertificate(isExpired: true, invalidHost: false); // Expect an error indicating TLS handshake failed due to an expired certificate. - AssertInnerEncryptionException(exception, "The remote certificate is invalid according to the validation procedure."); + AssertInnerEncryptionException(exception, invalidCertificateError); break; case CertificateType.InvalidHostName: AssertCertificate(isExpired: false, invalidHost: true); // Expect an error indicating TLS handshake failed due to an invalid hostname. - AssertInnerEncryptionException(exception, "The remote certificate is invalid according to the validation procedure."); + AssertInnerEncryptionException(exception, invalidCertificateError); break; default: throw new Exception($"Unexpected certificate type {certificateType} for {kmsProvider}."); } @@ -1416,12 +1599,12 @@ void AssertException(Exception exception) case CertificateType.Expired: AssertCertificate(isExpired: true, invalidHost: false); // Expect an error indicating TLS handshake failed due to an expired certificate. - AssertInnerEncryptionException(exception, "The remote certificate is invalid according to the validation procedure."); + AssertInnerEncryptionException(exception, invalidCertificateError); break; case CertificateType.InvalidHostName: AssertCertificate(isExpired: false, invalidHost: true); // Expect an error indicating TLS handshake failed due to an invalid hostname. - AssertInnerEncryptionException(exception, "The remote certificate is invalid according to the validation procedure."); + AssertInnerEncryptionException(exception, invalidCertificateError); break; default: throw new Exception($"Unexpected certificate type {certificateType} for {kmsProvider}."); } @@ -1449,7 +1632,7 @@ void AssertTlsWithoutClientCertOnWindows(Exception exception) #endif "The message received was unexpected or badly formatted"); } - catch (Xunit.Sdk.XunitException) // assertation failed + catch (XunitException) // assertation failed { // Sometimes the mock server triggers SocketError.ConnectionReset (10054) on windows instead the expected exception. // It looks like a test env issue, a similar behavior presents in other drivers, so we rely on the same check on different OSs @@ -1522,7 +1705,567 @@ void KmsProviderEndpointConfigurator(string kmsProviderName, Dictionary CreateDataKey(clientEncryption, kmsProvider, datakeyOptions, async)); + if (expectedEnvironment) + { + // all expected env setup MUST be configured + ex.Should().BeNull(); + } + else + { + AssertException(ex); + } + + void AssertException(Exception ex) + { + var currentOperatingSystem = OperatingSystemHelper.CurrentOperatingSystem; + switch (kmsProvider) + { + // AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must not be configured + case "aws": + { + try + { + AssertInnerEncryptionException(ex, "Unable to get IAM security credentials from EC2 Instance Metadata Service."); + } + catch (XunitException) + { + // In rare cases, the thrown error is "CryptException exception: AcceessDeniedException". That means you don't have authorization to perform the requested action. + // It more or less corresponds to the expected behavior here, but it's unclear why the same scenario triggers different exceptions. + // However, it looks harmless to slightly update the test assertion to avoid assertion failures on EG. + AssertInnerEncryptionException(ex, "Error in KMS response. HTTP status=400. Response body=\n{\"__type\":\"AccessDeniedException\"}"); + } + } + break; + case "azure": + { + switch (currentOperatingSystem) + { + case OperatingSystemPlatform.Windows: + case OperatingSystemPlatform.Linux: + { + AssertInnerEncryptionException(ex, "Failed to acquire IMDS access token."); + } + break; + case OperatingSystemPlatform.MacOS: + { + try + { + AssertInnerEncryptionException(ex, "Failed to acquire IMDS access token."); + } + catch (XunitException) + { + AssertInnerEncryptionException(ex, "Failed to acquire IMDS access token."); + } + } + break; + default: throw new Exception($"Unexpected OS: {currentOperatingSystem}"); + } + } + break; + case "gcp": + { + AssertInnerEncryptionException(ex, "Failed to acquire gce metadata credentials."); + } + break; + default: throw new Exception($"Unexpected kms provider: {kmsProvider}."); + } + } + } + + void EnsureEnvironmentConfigured(out BsonDocument customMasterKey) + { + customMasterKey = null; + var requireEnvironmentCheck = RequireEnvironment.Check(); + switch (kmsProvider) + { + case "aws": + { + requireEnvironmentCheck.EnvironmentVariable("AWS_ACCESS_KEY_ID", isDefined: expectedEnvironment); + // mocked env doesn't configure aws_temp credentials with AWS_ACCESS_KEY_ID + requireEnvironmentCheck.EnvironmentVariable("KMS_MOCK_SERVERS_ENABLED", isDefined: !expectedEnvironment); + } + break; + case "azure": + { + if (Environment.GetEnvironmentVariable("CSFLE_AZURE_KMS_TESTS_ENABLED") != null) + { + // azure env + if (!expectedEnvironment) + { + throw new SkipException("Test skipped, because current env should not be Azure."); + } + } + else + { + // It can work everywhere, but limit running these tests here since a single test run can take up to 10 seconds + requireEnvironmentCheck + .EnvironmentVariable("KMS_MOCK_SERVERS_ENABLED", isDefined: true) + .EnvironmentVariable("CSFLE_AZURE_KMS_TESTS_ENABLED", isDefined: expectedEnvironment); + } + + customMasterKey = new BsonDocument + { + { "keyVaultEndpoint", "https://keyvault-drivers-2411.vault.azure.net/keys/" }, + { "keyName", "KEY-NAME" } + }; + } + break; + case "gcp": + { + if (Environment.GetEnvironmentVariable("CSFLE_GCP_KMS_TESTS_ENABLED") != null) + { + // gcp env + if (!expectedEnvironment) + { + throw new SkipException("Test skipped, because current env should not be GCP."); + } + } + else + { + // mocked env + // gcp mocked server fails on non windows env + RequirePlatform + .Check() + .SkipWhen(SupportedOperatingSystem.Linux) + .SkipWhen(SupportedOperatingSystem.MacOS); + + if (expectedEnvironment) + { + requireEnvironmentCheck + .EnvironmentVariable("CSFLE_GCP_KMS_TESTS_ENABLED", isDefined: false) + // mocked env + .EnvironmentVariable("KMS_MOCK_SERVERS_ENABLED", isDefined: true) + .EnvironmentVariable("GCE_METADATA_HOST", isDefined: expectedEnvironment) + // required mock server + .HostReachable((DnsEndPoint)EndPointHelper.Parse(Environment.GetEnvironmentVariable("GCE_METADATA_HOST"))); + } + else + { + requireEnvironmentCheck + .EnvironmentVariable("CSFLE_GCP_KMS_TESTS_ENABLED", isDefined: false) + .EnvironmentVariable("KMS_MOCK_SERVERS_ENABLED", isDefined: false); + } + } + } + break; + default: throw new Exception($"Unexpected kms provider: {kmsProvider}."); + } + } + } + + [Theory] + [ParameterAttributeData] + public async Task OnDemandAzureIMDSCredentialsUnitTest( + [Range(1, 6)] int testCase, + [Values(false, true)] bool async) + { + RequireEnvironment + .Check() + .EnvironmentVariable("KMS_MOCK_SERVERS_ENABLED") + .EnvironmentVariable("AZURE_IMDS_MOCK_ENDPOINT"); + + switch (testCase) + { + case 1: // Case 1: Success + { + var result = await CreateTestCase(request => { }); + result.AccessToken.Should().Be("magic-cookie"); + // < 70 && >= 60 seconds + result.Expiration.Should().BeCloseTo(DateTime.UtcNow + TimeSpan.FromSeconds(65), (int)TimeSpan.FromSeconds(5).TotalMilliseconds); + } + break; + case 2: // Case 2: Empty JSON + { + var exception = await Record.ExceptionAsync(() => CreateTestCase((request) => request.Headers.Add("X-MongoDB-HTTP-TestParams", "case=empty-json"))); + exception.Should().BeOfType().Which.Message.Should().Be("Azure IMDS response must contain access_token."); + } + break; + case 3: // Case 3: Bad JSON + { + var exception = await Record.ExceptionAsync(() => CreateTestCase((request) => request.Headers.Add("X-MongoDB-HTTP-TestParams", "case=bad-json"))); + exception.Should().BeOfType().Which.Message.Should().Be("Azure IMDS response must be in Json format."); + } + break; + case 4: // Case 4: HTTP 404 + { + var exception = await Record.ExceptionAsync(() => CreateTestCase((request) => request.Headers.Add("X-MongoDB-HTTP-TestParams", "case=404"))); + exception + .Should().BeOfType().Which.InnerException + .Should().BeOfType().Which.Message + .Should().Be("Response status code does not indicate success: 404 (Not Found)."); + } + break; + case 5: // Case 5: HTTP 500 + { + var exception = await Record.ExceptionAsync(() => CreateTestCase((request) => request.Headers.Add("X-MongoDB-HTTP-TestParams", "case=500"))); + exception + .Should().BeOfType().Which.InnerException + .Should().BeOfType().Which.Message + .Should().Be("Response status code does not indicate success: 500 (Internal Server Error)."); + } + break; + case 6: // Case 6: Slow Response + { + var exception = await Record.ExceptionAsync(() => CreateTestCase((request) => request.Headers.Add("X-MongoDB-HTTP-TestParams", "case=slow"))); + exception + .Should().BeOfType().Which.InnerException + .Should().BeAssignableTo(); + } + break; + default: throw new Exception($"Unexpected test case: {testCase}."); + } + + async Task CreateTestCase(Action modifyAction) + { + var httpClientWrapperWithModifiedRequest = CreateHttpClientWrapperWithModifiedRequest(modifyAction); + var azureProvider = new AzureAuthenticationCredentialsProvider(httpClientWrapperWithModifiedRequest); + return async + ? await azureProvider.CreateCredentialsFromExternalSourceAsync(default) + : azureProvider.CreateCredentialsFromExternalSource(default); + } + + HttpClientWrapperWithModifiedRequest CreateHttpClientWrapperWithModifiedRequest(Action modifyAction) + { + var imdsMockEndpoint = Environment.GetEnvironmentVariable("AZURE_IMDS_MOCK_ENDPOINT") ?? throw new Exception("AZURE_IMDS_MOCK_ENDPOINT must be configured."); + var httpClientHelper = ExternalCredentialsAuthenticators.Instance.HttpClientWrapper; + var withReplacedEndpoint = (HttpRequestMessage httpRequestMessage) => + { + modifyAction(httpRequestMessage); + var uriBuilder = new UriBuilder(httpRequestMessage.RequestUri); + var mockUri = new Uri($"http://{imdsMockEndpoint}"); + uriBuilder.Scheme = mockUri.Scheme; + uriBuilder.Host = mockUri.Host; + uriBuilder.Port = mockUri.Port; + httpRequestMessage.RequestUri = uriBuilder.Uri; + }; + return new HttpClientWrapperWithModifiedRequest(httpClientHelper, withReplacedEndpoint); + } + } + + [Theory] + [ParameterAttributeData] + public async Task RangeExplicitEncryptionTest( + [Range(1, 8)] int testCase, + // test case rangeType values correspond to keys used in test configuration files + [Values("DecimalNoPrecision", "DecimalPrecision", "DoubleNoPrecision", "DoublePrecision", "Date", "Int", "Long")] string rangeType, + [Values(false, false)] bool async) + { + RequireServer.Check().Supports(Feature.CsfleRangeAlgorithm).ClusterTypes(ClusterType.ReplicaSet, ClusterType.Sharded, ClusterType.LoadBalanced); + if (rangeType == "DecimalNoPrecision") + { + // Tests for ``DecimalNoPrecision`` must only run against a replica set. + // ``DecimalNoPrecision`` queries are expected to take a long time and may exceed the default mongos timeout. + RequireServer.Check().ClusterTypes(ClusterType.ReplicaSet); + } + + var encryptedFields = JsonFileReader.Instance.Documents[$"etc.data.range-encryptedFields-{rangeType}.json"]; + var key1Document = JsonFileReader.Instance.Documents["etc.data.keys.key1-document.json"]; + var key1Id = key1Document["_id"].AsGuid; + var kmsProvider = "local"; + var encryptedKeyWithRangeSupportedType = $"encrypted{rangeType}"; + var value0 = GetValue(0, rangeType); + var value6 = GetValue(6, rangeType); + var value30 = GetValue(30, rangeType); + var value200 = GetValue(200, rangeType); + var value201 = GetValue(201, rangeType); + + var explicitEncryption = CollectionNamespace.FromFullName("db.explicit_encryption"); + var encryptOptions = WithRangeOptions(rangeType, new EncryptOptions(EncryptionAlgorithm.RangePreview, contentionFactor: 0, keyId: key1Id)); + + using (var keyVaultClient = ConfigureClient(clearCollections: true, mainCollectionNamespace: explicitEncryption, encryptedFields: encryptedFields)) + { + var keyVaultCollection = GetCollection(keyVaultClient, __keyVaultCollectionNamespace); + Insert(keyVaultCollection, async, key1Document); + + using (var clientEncryption = ConfigureClientEncryption(keyVaultClient, kmsProviderFilter: kmsProvider)) + using (var encryptedClient = ConfigureClientEncrypted(kmsProviderFilter: kmsProvider, bypassQueryAnalysis: true)) + { + var encrypted0 = ExplicitEncrypt(clientEncryption, encryptOptions, value0, async); + var encrypted6 = ExplicitEncrypt(clientEncryption, encryptOptions, value6, async); + var encrypted30 = ExplicitEncrypt(clientEncryption, encryptOptions, value30, async); + var encrypted200 = ExplicitEncrypt(clientEncryption, encryptOptions, value200, async); + + CreateCollection(encryptedClient, explicitEncryption, encryptedFields: encryptedFields); + var encryptedCollection = GetCollection(encryptedClient, explicitEncryption); + // bulk insert is not supported + Insert( + encryptedCollection, + async, + new BsonDocument { { encryptedKeyWithRangeSupportedType, encrypted0 }, { "_id", 0 } }); + Insert( + encryptedCollection, + async, + new BsonDocument { { encryptedKeyWithRangeSupportedType, encrypted6 }, { "_id", 1 } }); + Insert( + encryptedCollection, + async, + new BsonDocument { { encryptedKeyWithRangeSupportedType, encrypted30 }, { "_id", 2 } }); + Insert( + encryptedCollection, + async, + new BsonDocument { { encryptedKeyWithRangeSupportedType, encrypted200 }, { "_id", 3 } }); + + await RunTestCase(clientEncryption, encryptedCollection, testCase); + } + } + + EncryptOptions WithRangeOptions(string rangeType, EncryptOptions encryptionOptions) + { + var rangeOptions = rangeType switch + { + "DecimalNoPrecision" => new RangeOptions(sparsity: 1), + "DecimalPrecision" => new RangeOptions( + sparsity: 1, + precision: 2, + min: new BsonDecimal128(0), + max: new BsonDecimal128(200)), + "DoubleNoPrecision" => new RangeOptions(sparsity: 1), + "DoublePrecision" => new RangeOptions( + sparsity: 1, + min: new BsonDouble(0), + max: new BsonDouble(200), + precision: 2), + "Date" => new RangeOptions( + sparsity: 1, + min: new BsonDateTime(0), + max: new BsonDateTime(200)), + "Int" => new RangeOptions( + sparsity: 1, + min: new BsonInt32(0), + max: new BsonInt32(200)), + "Long" => new RangeOptions( + sparsity: 1, + min: new BsonInt64(0), + max: new BsonInt64(200)), + _ => throw new Exception($"Unsupported rangeSupportedType {rangeType}.") + }; + + return encryptionOptions.With(rangeOptions: rangeOptions); + } + + + async Task RunTestCase(ClientEncryption clientEncryption, IMongoCollection encryptedCollection, int testCase) + { + switch (testCase) + { + case 1: // can decrypt a payload + { + var insertPayload6 = ExplicitEncrypt(clientEncryption, encryptOptions, value6, async); + var decryptedValue = ExplicitDecrypt(clientEncryption, insertPayload6, async); + decryptedValue.Should().Be(value6); // asserts types too + } + break; + case 2: // can find encrypted range and return the maximum + { + var findPayload = await ExplicitEncryptExpression( + clientEncryption, + encryptOptions.With(queryType: "rangePreview"), + expression: BsonDocument.Parse(@$" + {{ + ""$and"" : + [ + {{ {encryptedKeyWithRangeSupportedType} : {{ ""$gte"" : {value6.ToJson()} }} }}, + {{ {encryptedKeyWithRangeSupportedType} : {{ ""$lte"" : {value200.ToJson()} }} }} + ] + }}"), + async); + + var findResult = Find(encryptedCollection, findPayload, async).ToList().OrderBy((d) => d["_id"]).ToList(); + findResult.Should().HaveCount(3); + + findResult[0][encryptedKeyWithRangeSupportedType].Should().Be(value6); + findResult[1][encryptedKeyWithRangeSupportedType].Should().Be(value30); + findResult[2][encryptedKeyWithRangeSupportedType].Should().Be(value200); + } + break; + case 3: // can find encrypted range and return the minimum + { + var findPayload = await ExplicitEncryptExpression( + clientEncryption, + encryptOptions.With(queryType: "rangePreview"), + expression: BsonDocument.Parse(@$" + {{ + ""$and"" : + [ + {{ {encryptedKeyWithRangeSupportedType} : {{ ""$gte"" : {value0.ToJson()} }} }}, + {{ {encryptedKeyWithRangeSupportedType} : {{ ""$lte"" : {value6.ToJson()} }} }} + ] + }}"), + async); + + var findResult = Find(encryptedCollection, findPayload, async).ToList().OrderBy((d) => d["_id"]).ToList(); + findResult.Should().HaveCount(2); + + findResult[0][encryptedKeyWithRangeSupportedType].Should().Be(value0); + findResult[1][encryptedKeyWithRangeSupportedType].Should().Be(value6); + } + break; + case 4: // can find encrypted range with an open range query + { + var findPayload = await ExplicitEncryptExpression( + clientEncryption, + encryptOptions.With(queryType: "rangePreview"), + expression: BsonDocument.Parse(@$" + {{ + ""$and"" : + [ + {{ {encryptedKeyWithRangeSupportedType} : {{ ""$gt"" : {value30.ToJson()} }} }} + ] + }}"), + async); + + var findResult = Find(encryptedCollection, findPayload, async).ToList().OrderBy((d) => d["_id"]).ToList(); + findResult.Should().HaveCount(1); + + findResult[0][encryptedKeyWithRangeSupportedType].Should().Be(value200); + } + break; + case 5: // can run an aggregation expression inside $expr + { + var findPayload = await ExplicitEncryptExpression( + clientEncryption, + encryptOptions.With(queryType: "rangePreview"), + expression: BsonDocument.Parse(@$" + {{ + ""$and"" : + [ + {{ ""$lt"" : [ ""${encryptedKeyWithRangeSupportedType}"", {value30.ToJson()} ] }} + ] + }}"), + async); + + var findResult = Find(encryptedCollection, BsonDocument.Parse(@$"{{ ""$expr"" : {findPayload} }}"), async).ToList().OrderBy((d) => d["_id"]).ToList(); + findResult.Should().HaveCount(2); + + findResult[0][encryptedKeyWithRangeSupportedType].Should().Be(value0); + findResult[1][encryptedKeyWithRangeSupportedType].Should().Be(value6); + } + break; + case 6: // encrypting a document greater than the maximum errors + { + if (rangeType == "DoubleNoPrecision" || rangeType == "DecimalNoPrecision") + { + throw new SkipException("Skip it based on spec requirement."); + } + + var exception = Record.Exception(() => ExplicitEncrypt(clientEncryption, encryptOptions, value201, async)); + AssertInnerEncryptionException(exception, "Value must be greater than or equal to the minimum value and less than or equal to the maximum value"); + } + break; + case 7: // encrypting a document of a different type errors + { + if (rangeType == "DoubleNoPrecision" || rangeType == "DecimalNoPrecision") + { + throw new SkipException("Skip it based on spec requirement."); + } + + var exception = Record.Exception(() => + Insert( + encryptedCollection, + async, + // If the encrypted field is ``encryptedInt`` insert ``{ "encryptedInt": { "$numberDouble": "6" } }``. + // Otherwise, insert ``{ "encrypted": { "$numberInt": "6" }``. + new BsonDocument(encryptedKeyWithRangeSupportedType, rangeType == "Int" ? GetValue(6, "DoubleNoPrecision") : GetValue(6, "Int")))); + exception.Should().BeOfType>().Which.Message.Should().Contain("Document failed validation"); + } + break; + case 8: // setting precision errors if the type is not a double + { + if (rangeType == "DoubleNoPrecision" || rangeType == "DoublePrecision" || rangeType == "DecimalPrecision" || rangeType == "DecimalNoPrecision") + { + throw new SkipException("Skip it based on spec requirement."); + } + + var exception = Record.Exception(() => + ExplicitEncrypt( + clientEncryption, + encryptOptions.With(rangeOptions: new RangeOptions(sparsity: 1, min: BsonValue.Create(0), max: BsonValue.Create(200), precision: 2)), + value6, + async)); + AssertInnerEncryptionException(exception, "expected 'precision' to be set with double or decimal128 index, but got: INT32 min"); + } + break; + } + } + + BsonValue GetValue(int value, string rangeSupportedType) => rangeSupportedType switch + { + "DecimalNoPrecision" => new BsonDecimal128(value), + "DecimalPrecision" => new BsonDecimal128(value), + "DoubleNoPrecision" => new BsonDouble(value), + "DoublePrecision" => new BsonDouble(value), + "Date" => new BsonDateTime(millisecondsSinceEpoch: value), + "Int" => new BsonInt32(value), + "Long" => new BsonInt64(value), + _ => throw new ArgumentException($"Unsupported {nameof(rangeSupportedType)} {rangeSupportedType}.") + }; + } + + [Theory] + [ParameterAttributeData] + public void RewrapTest( + [Values("local", "aws", "azure", "gcp", "kmip")] string srcProvider, + [Values("local", "aws", "azure", "gcp", "kmip")] string dstProvider, + [Values(false, true)] bool async) + { + RequireServer.Check().Supports(Feature.Csfle2); + + // The test description requires configuring all kmsProviders in setup, but leaving only related to the provided income arguments + // to avoid restrictions on kmip mocking setup for unrelated to kmip tests + var kmsProviderFilter = EncryptionTestHelper.CreateKmsProviderFilter(srcProvider, dstProvider); + RequirePlatform + .Check() + .SkipWhen(() => kmsProviderFilter.Contains("gcp"), SupportedOperatingSystem.Linux, SupportedTargetFramework.NetStandard20) // gcp is supported starting from netstandard2.1 + .SkipWhen(() => kmsProviderFilter.Contains("gcp"), SupportedOperatingSystem.MacOS, SupportedTargetFramework.NetStandard20); + if (kmsProviderFilter.Contains("kmip")) + { + RequireEnvironment.Check().EnvironmentVariable("KMS_MOCK_SERVERS_ENABLED", isDefined: true); + } + + const string value = "test"; + + using (var client1 = ConfigureClient(clearCollections: true)) + using (var clientEncryption1 = ConfigureClientEncryption(client1, kmsProviderFilter: kmsProviderFilter)) + { + var datakeyOptions = CreateDataKeyOptions(srcProvider); + var keyID = CreateDataKey(clientEncryption1, srcProvider, datakeyOptions, async); + var ciphertext = ExplicitEncrypt(clientEncryption1, new EncryptOptions(keyId: keyID, algorithm: EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic), value, async); + + using (var client2 = ConfigureClient(clearCollections: false)) + using (var clientEncryption2 = ConfigureClientEncryption(client2, kmsProviderFilter: kmsProviderFilter)) + { + var rewrapManyDataKeyOptions = CreateRewrapManyDataKeyOptions(dstProvider); + var result = RewrapManyDataKey(clientEncryption2, rewrapManyDataKeyOptions, async); + result.BulkWriteResult.ModifiedCount.Should().Be(1); + + var decrypted = ExplicitDecrypt(clientEncryption1, ciphertext, async); + decrypted.Should().Be(BsonValue.Create(value)); + + decrypted = ExplicitDecrypt(clientEncryption2, ciphertext, async); + decrypted.Should().Be(BsonValue.Create(value)); + } + } + } + + [Theory] [ParameterAttributeData] public void ViewAreProhibitedTest([Values(false, true)] bool async) { @@ -1532,7 +2275,7 @@ public void ViewAreProhibitedTest([Values(false, true)] bool async) using (var client = ConfigureClient(false)) using (var clientEncrypted = ConfigureClientEncrypted(kmsProviderFilter: "local")) { - DropView(viewName); + DropCollection(viewName); client .GetDatabase(viewName.DatabaseNamespace.DatabaseName) .CreateView( @@ -1550,7 +2293,7 @@ public void ViewAreProhibitedTest([Values(false, true)] bool async) } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void UniqueIndexOnKeyAltNames( [Range(1, 2)] int testCase, @@ -1619,7 +2362,7 @@ void RunTestCase(ClientEncryption clientEncryption, Guid existingKey, int testCa } // NOTE: this test is not presented in the prose tests - [SkippableTheory] + [Theory] [ParameterAttributeData] public void UnsupportedPlatformsTests( [Values("gcp")] string kmsProvider, // the rest kms providers are supported on all supported TFs @@ -1688,7 +2431,16 @@ private Exception AssertInnerEncryptionException(Exception ex, Type exType, para } } - e.Should().BeOfType(exType); + if (typeof(OperationCanceledException).IsAssignableFrom(exType)) + { + // handles OperationCanceledException and TaskCanceledException. + // At least in macOS these exceptions can be triggered from the same code path in some cases + e.Should().BeAssignableTo(); + } + else + { + e.Should().BeOfType(exType); + } return e; } @@ -1791,19 +2543,35 @@ private ClientEncryption ConfigureClientEncryption( Action> kmsProviderConfigurator = null, Func allowClientCertificateFunc = null, Action clientEncryptionOptionsConfigurator = null, - string kmsProviderFilter = null) + string kmsProviderFilter = null, + BsonDocument kmsDocument = null) { - var kmsProviders = EncryptionTestHelper - .GetKmsProviders(filter: kmsProviderFilter) - .Select(k => - { - if (kmsProviderConfigurator != null) + Dictionary> kmsProviders; + if (kmsDocument == null) + { + kmsProviders = EncryptionTestHelper + .GetKmsProviders(filter: kmsProviderFilter) + .Select(k => { - kmsProviderConfigurator(k.Key, (Dictionary)k.Value); - } - return k; - }) - .ToDictionary(k => k.Key, k => k.Value); + if (kmsProviderConfigurator != null) + { + kmsProviderConfigurator(k.Key, (Dictionary)k.Value); + } + return k; + }) + .ToDictionary(k => k.Key, k => k.Value); + } + else + { + Ensure.IsNull(kmsProviderFilter, nameof(kmsProviderFilter)); + + kmsProviders = kmsDocument + .Elements + .ToDictionary( + k => k.Name, + k => (IReadOnlyDictionary)k.Value.AsBsonDocument.ToDictionary(ki => ki.Name, ki => (object)ki.Value)); + } + allowClientCertificateFunc = allowClientCertificateFunc ?? ((kmsProviderName) => kmsProviderName == "kmip"); // configure Tls for kmip by default var tlsOptions = EncryptionTestHelper.CreateTlsOptionsIfAllowed(kmsProviders, allowClientCertificateFunc); @@ -1835,6 +2603,27 @@ private void CreateCollection(IMongoClient client, CollectionNamespace collectio }); } + private IMongoCollection CreateEncryptedCollection(IMongoClient client, ClientEncryption clientEncryption, CollectionNamespace collectionNamespace, BsonDocument encryptedFields, string kmsProvider, bool async, out BsonDocument effectiveEncryptedFields) + { + var createCollectionOptions = new CreateCollectionOptions { EncryptedFields = encryptedFields }; + return CreateEncryptedCollection(client, clientEncryption, collectionNamespace, createCollectionOptions, kmsProvider, async, out effectiveEncryptedFields); + } + + private IMongoCollection CreateEncryptedCollection(IMongoClient client, ClientEncryption clientEncryption, CollectionNamespace collectionNamespace, CreateCollectionOptions createCollectionOptions, string kmsProvider, bool async, out BsonDocument effectiveEncryptedFields) + { + var datakeyOptions = CreateDataKeyOptions(kmsProvider); + var database = client.GetDatabase(collectionNamespace.DatabaseNamespace.DatabaseName); + + + var result = async + ? clientEncryption.CreateEncryptedCollectionAsync(database, collectionNamespace.CollectionName, createCollectionOptions, kmsProvider, datakeyOptions, cancellationToken: default).GetAwaiter().GetResult() + : clientEncryption.CreateEncryptedCollection(database, collectionNamespace.CollectionName, createCollectionOptions, kmsProvider, datakeyOptions, cancellationToken: default); + + effectiveEncryptedFields = result.EncryptedFields; + + return client.GetDatabase(collectionNamespace.DatabaseNamespace.DatabaseName).GetCollection(collectionNamespace.CollectionName); + } + private Guid CreateDataKey( ClientEncryption clientEncryption, string kmsProvider, @@ -1857,36 +2646,18 @@ private Guid CreateDataKey( private DataKeyOptions CreateDataKeyOptions(string kmsProvider, BsonDocument customMasterKey = null) { var alternateKeyNames = new[] { $"{kmsProvider}_altname" }; - var masterKey = customMasterKey ?? - kmsProvider switch - { - var kmsName when kmsName == "local" && customMasterKey == null => null, - "aws" => new BsonDocument - { - { "region", "us-east-1" }, - { "key", "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" } - }, - "azure" => new BsonDocument - { - { "keyName", "key-name-csfle" }, - { "keyVaultEndpoint", "key-vault-csfle.vault.azure.net" } - }, - "gcp" => new BsonDocument - { - { "projectId", "devprod-drivers" }, - { "location", "global" }, - { "keyRing", "key-ring-csfle" }, - { "keyName", "key-name-csfle" } - }, - "kmip" => new BsonDocument(), - _ => throw new ArgumentException($"Incorrect kms provider {kmsProvider} or provided custom master key {customMasterKey}.", nameof(kmsProvider)), - }; - + var masterKey = customMasterKey ?? EncryptionTestHelper.CreateMasterKey(kmsProvider); return new DataKeyOptions( alternateKeyNames: alternateKeyNames, masterKey: masterKey); } + private RewrapManyDataKeyOptions CreateRewrapManyDataKeyOptions(string kmsProvider, BsonDocument customMasterKey = null) + { + var masterKey = customMasterKey ?? EncryptionTestHelper.CreateMasterKey(kmsProvider); + return new RewrapManyDataKeyOptions(kmsProvider, masterKey: masterKey); + } + private DisposableMongoClient CreateMongoClient( CollectionNamespace keyVaultNamespace = null, BsonDocument schemaMapDocument = null, @@ -1913,7 +2684,7 @@ private DisposableMongoClient CreateMongoClient( writeConcern, readConcern); - return DriverTestConfiguration.CreateDisposableClient(mongoClientSettings, logger: CreateLogger()); + return DriverTestConfiguration.CreateDisposableClient(mongoClientSettings); } private MongoClientSettings CreateMongoClientSettings( @@ -1998,12 +2769,14 @@ private MongoClientSettings CreateMongoClientSettings( mongoClientSettings.AutoEncryptionOptions = autoEncryptionOptions; } + mongoClientSettings.LoggingSettings = LoggingSettings; + return mongoClientSettings; } - private void DropView(CollectionNamespace viewNamespace) + private void DropCollection(CollectionNamespace collectionNamespace, BsonDocument encryptedFields = null) { - var operation = new DropCollectionOperation(viewNamespace, CoreTestConfiguration.MessageEncoderSettings); + var operation = DropCollectionOperation.CreateEncryptedDropCollectionOperationIfConfigured(collectionNamespace, encryptedFields, CoreTestConfiguration.MessageEncoderSettings, configureDropCollectionConfigurator: null); using (var session = CoreTestConfiguration.StartSession(_cluster)) using (var binding = new WritableServerBinding(_cluster, session.Fork())) using (var bindingHandle = new ReadWriteBindingHandle(binding)) @@ -2065,6 +2838,15 @@ private BsonBinaryData ExplicitEncrypt( return encryptedValue; } + private async Task ExplicitEncryptExpression( + ClientEncryption clientEncryption, + EncryptOptions encryptOptions, + BsonDocument expression, + bool async) => + async + ? await clientEncryption.EncryptExpressionAsync(expression, encryptOptions) + : clientEncryption.EncryptExpression(expression, encryptOptions); + private IAsyncCursor Find( IMongoCollection collection, BsonDocument filter, @@ -2149,6 +2931,24 @@ private void Insert( } } + private RewrapManyDataKeyResult RewrapManyDataKey( + ClientEncryption clientEncryption, + RewrapManyDataKeyOptions rewrapManyDataKeyOptions, + bool async, + string filter = "{}") => + async + ? clientEncryption + .RewrapManyDataKeyAsync( + filter, + rewrapManyDataKeyOptions, + CancellationToken.None) + .GetAwaiter() + .GetResult() + : clientEncryption.RewrapManyDataKey( + filter, + rewrapManyDataKeyOptions, + CancellationToken.None); + // nested types public enum CertificateType { @@ -2235,6 +3035,26 @@ private void RemoveIgnoredElements(BsonDocument document) } } } + + private class HttpClientWrapperWithModifiedRequest : IHttpClientWrapper + { + private readonly IHttpClientWrapper _httpClientWrapper; + private readonly Action _modifyAction; + + public HttpClientWrapperWithModifiedRequest( + IHttpClientWrapper httpClientWrapper, + Action modifyAction) + { + _httpClientWrapper = Ensure.IsNotNull(httpClientWrapper, nameof(httpClientWrapper)); + _modifyAction = Ensure.IsNotNull(modifyAction, nameof(modifyAction)); + } + + public Task GetHttpContentAsync(HttpRequestMessage request, string exceptionMessage, CancellationToken cancellationToken) + { + _modifyAction(request); + return _httpClientWrapper.GetHttpContentAsync(request, exceptionMessage, cancellationToken); + } + } } public static class ClientEncryptionOptionsReflector diff --git a/tests/MongoDB.Driver.Tests/Specifications/collection-management/CollectionManagementUnifiedTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/collection-management/CollectionManagementUnifiedTestRunner.cs index c8e058c6814..15dd24afb06 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/collection-management/CollectionManagementUnifiedTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/collection-management/CollectionManagementUnifiedTestRunner.cs @@ -32,11 +32,11 @@ public CollectionManagementUnifiedTestRunner(ITestOutputHelper testOutputHelper) } // public methods - [SkippableTheory] + [Theory] [ClassData(typeof(TestCaseFactory))] public void Run(JsonDrivenTestCase testCase) { - using (var runner = new UnifiedTestRunner()) + using (var runner = new UnifiedTestRunner(loggingService: this)) { runner.Run(testCase); } diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/BulkWriteTest.cs b/tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/BulkWriteTest.cs similarity index 98% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/BulkWriteTest.cs rename to tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/BulkWriteTest.cs index d1d20b14861..41c0f72ed8b 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/BulkWriteTest.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/BulkWriteTest.cs @@ -17,7 +17,7 @@ using System.Collections.Generic; using MongoDB.Bson; -namespace MongoDB.Driver.Tests.Specifications.command_monitoring +namespace MongoDB.Driver.Tests.Specifications.command_logging_and_monitoring { public class BulkWriteTest : CrudOperationTestBase { diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/CommandLoggingUnifiedTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/CommandLoggingUnifiedTestRunner.cs new file mode 100644 index 00000000000..727b5581e81 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/CommandLoggingUnifiedTestRunner.cs @@ -0,0 +1,67 @@ +/* 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.Collections.Generic; +using MongoDB.Bson; +using MongoDB.Bson.TestHelpers.JsonDrivenTests; +using MongoDB.Driver.Core.TestHelpers.Logging; +using MongoDB.Driver.Tests.UnifiedTestOperations; +using Xunit; +using Xunit.Abstractions; + +namespace MongoDB.Driver.Tests.Specifications.command_logging_and_monitoring +{ + public class CommandLoggingUnifiedTestRunner : LoggableTestClass + { + public CommandLoggingUnifiedTestRunner(ITestOutputHelper testOutputHelper) + : base(testOutputHelper, true) + { + } + + [Theory] + [ClassData(typeof(TestCaseFactory))] + public void Run(JsonDrivenTestCase testCase) + { + using (var runner = new UnifiedTestRunner(loggingService: this)) + { + runner.Run(testCase); + } + } + + public class TestCaseFactory : JsonDrivenTestCaseFactory + { + protected override string PathPrefix => "MongoDB.Driver.Tests.Specifications.command_logging_and_monitoring.tests.logging."; + + protected override IEnumerable CreateTestCases(BsonDocument document) + { + foreach (var testCase in base.CreateTestCases(document)) + { + // .NET driver has a fallback logic to get a server connectionId based on an additional getLastError call which is not expected by the spec. + if (testCase.Name.Contains("pre-42-server-connection-id")) + { + continue; + } + + foreach (var async in new[] { false, true }) + { + var name = $"{testCase.Name}:async={async}"; + var test = testCase.Test.DeepClone().AsBsonDocument.Add("async", async); + yield return new JsonDrivenTestCase(name, testCase.Shared, test); + } + } + } + } + } +} diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/CommandMonitoringTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/CommandMonitoringTestRunner.cs similarity index 98% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/CommandMonitoringTestRunner.cs rename to tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/CommandMonitoringTestRunner.cs index 25610b31682..aec4c0aaac5 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/CommandMonitoringTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/CommandMonitoringTestRunner.cs @@ -25,8 +25,9 @@ using MongoDB.Driver.Core.Events; using MongoDB.Driver.Core.Misc; using Xunit; +using Xunit.Sdk; -namespace MongoDB.Driver.Tests.Specifications.command_monitoring +namespace MongoDB.Driver.Tests.Specifications.command_logging_and_monitoring { public class CommandMonitoringTestRunner { @@ -97,7 +98,7 @@ public bool OneTimeSetup() return true; } - [SkippableTheory] + [Theory] [ClassData(typeof(TestCaseFactory))] public void RunTestDefinition(JsonDrivenTestCase testCase) { @@ -373,7 +374,7 @@ private BsonDocument MassageReply(string commandName, BsonDocument reply, BsonDo private class TestCaseFactory : JsonDrivenTestCaseFactory { // protected properties - protected override string PathPrefix => "MongoDB.Driver.Tests.Specifications.command_monitoring.tests.legacy"; + protected override string PathPrefix => "MongoDB.Driver.Tests.Specifications.command_logging_and_monitoring.tests.legacy"; // protected methods protected override IEnumerable CreateTestCases(BsonDocument document) diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/CommandMonitoringUnifiedTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/CommandMonitoringUnifiedTestRunner.cs similarity index 80% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/CommandMonitoringUnifiedTestRunner.cs rename to tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/CommandMonitoringUnifiedTestRunner.cs index a84c02cc7b3..c45f5b0feb6 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/CommandMonitoringUnifiedTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/CommandMonitoringUnifiedTestRunner.cs @@ -17,18 +17,26 @@ using System.Linq; using MongoDB.Bson; using MongoDB.Bson.TestHelpers.JsonDrivenTests; +using MongoDB.Driver.Core.TestHelpers.Logging; using MongoDB.Driver.Tests.UnifiedTestOperations; using Xunit; +using Xunit.Abstractions; -namespace MongoDB.Driver.Tests.Specifications.command_monitoring +namespace MongoDB.Driver.Tests.Specifications.command_logging_and_monitoring { - public class CommandMonitoringUnifiedTestRunner + public class CommandMonitoringUnifiedTestRunner : LoggableTestClass { - [SkippableTheory] + // public constructors + public CommandMonitoringUnifiedTestRunner(ITestOutputHelper testOutputHelper) + : base(testOutputHelper) + { + } + + [Theory] [ClassData(typeof(TestCaseFactory))] public void Run(JsonDrivenTestCase testCase) { - using (var runner = new UnifiedTestRunner()) + using (var runner = new UnifiedTestRunner(loggingService: this)) { runner.Run(testCase); } @@ -49,7 +57,7 @@ public class TestCaseFactory : JsonDrivenTestCaseFactory #endregion // protected properties - protected override string PathPrefix => "MongoDB.Driver.Tests.Specifications.command_monitoring.tests.unified."; + protected override string PathPrefix => "MongoDB.Driver.Tests.Specifications.command_logging_and_monitoring.tests.unified."; // protected methods protected override IEnumerable CreateTestCases(BsonDocument document) diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/CountTest.cs b/tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/CountTest.cs similarity index 95% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/CountTest.cs rename to tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/CountTest.cs index 4756b3d28bb..15b8c15119c 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/CountTest.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/CountTest.cs @@ -18,7 +18,7 @@ using FluentAssertions; using MongoDB.Bson; -namespace MongoDB.Driver.Tests.Specifications.command_monitoring +namespace MongoDB.Driver.Tests.Specifications.command_logging_and_monitoring { public class CountTest : CrudOperationTestBase { diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/CrudOperationTestBase.cs b/tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/CrudOperationTestBase.cs similarity index 96% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/CrudOperationTestBase.cs rename to tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/CrudOperationTestBase.cs index 586b066286c..ae53e2b4637 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/CrudOperationTestBase.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/CrudOperationTestBase.cs @@ -20,7 +20,7 @@ using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Misc; -namespace MongoDB.Driver.Tests.Specifications.command_monitoring +namespace MongoDB.Driver.Tests.Specifications.command_logging_and_monitoring { public abstract class CrudOperationTestBase : ICrudOperationTest { diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/DeleteManyTest.cs b/tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/DeleteManyTest.cs similarity index 95% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/DeleteManyTest.cs rename to tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/DeleteManyTest.cs index 6a5f21a6717..4d740a8587c 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/DeleteManyTest.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/DeleteManyTest.cs @@ -18,7 +18,7 @@ using FluentAssertions; using MongoDB.Bson; -namespace MongoDB.Driver.Tests.Specifications.command_monitoring +namespace MongoDB.Driver.Tests.Specifications.command_logging_and_monitoring { public class DeleteManyTest : CrudOperationTestBase { diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/DeleteOneTest.cs b/tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/DeleteOneTest.cs similarity index 95% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/DeleteOneTest.cs rename to tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/DeleteOneTest.cs index 3652c898130..33ace692b69 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/DeleteOneTest.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/DeleteOneTest.cs @@ -18,7 +18,7 @@ using FluentAssertions; using MongoDB.Bson; -namespace MongoDB.Driver.Tests.Specifications.command_monitoring +namespace MongoDB.Driver.Tests.Specifications.command_logging_and_monitoring { public class DeleteOneTest : CrudOperationTestBase { diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/FindTest.cs b/tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/FindTest.cs similarity index 97% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/FindTest.cs rename to tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/FindTest.cs index a51b9f30447..572a4f3cb45 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/FindTest.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/FindTest.cs @@ -17,7 +17,7 @@ using System.Threading.Tasks; using MongoDB.Bson; -namespace MongoDB.Driver.Tests.Specifications.command_monitoring +namespace MongoDB.Driver.Tests.Specifications.command_logging_and_monitoring { public class FindTest : CrudOperationTestBase { diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/ICrudOperationTest.cs b/tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/ICrudOperationTest.cs similarity index 93% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/ICrudOperationTest.cs rename to tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/ICrudOperationTest.cs index 5c61f3766db..64d86740626 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/ICrudOperationTest.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/ICrudOperationTest.cs @@ -20,7 +20,7 @@ using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Misc; -namespace MongoDB.Driver.Tests.Specifications.command_monitoring +namespace MongoDB.Driver.Tests.Specifications.command_logging_and_monitoring { public interface ICrudOperationTest { diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/InsertManyTest.cs b/tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/InsertManyTest.cs similarity index 97% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/InsertManyTest.cs rename to tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/InsertManyTest.cs index 90e3149dade..537752fd854 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/InsertManyTest.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/InsertManyTest.cs @@ -18,7 +18,7 @@ using System.Linq; using MongoDB.Bson; -namespace MongoDB.Driver.Tests.Specifications.command_monitoring +namespace MongoDB.Driver.Tests.Specifications.command_logging_and_monitoring { public class InsertManyTest : CrudOperationTestBase { diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/InsertOneTest.cs b/tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/InsertOneTest.cs similarity index 95% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/InsertOneTest.cs rename to tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/InsertOneTest.cs index c2b7a4d206a..e95a3e785e6 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/InsertOneTest.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/InsertOneTest.cs @@ -16,7 +16,7 @@ using System.Threading.Tasks; using MongoDB.Bson; -namespace MongoDB.Driver.Tests.Specifications.command_monitoring +namespace MongoDB.Driver.Tests.Specifications.command_logging_and_monitoring { public class InsertOneTest : CrudOperationTestBase { diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/UpdateManyTest.cs b/tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/UpdateManyTest.cs similarity index 96% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/UpdateManyTest.cs rename to tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/UpdateManyTest.cs index 52342049eec..05b0e64c7fc 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/UpdateManyTest.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/UpdateManyTest.cs @@ -18,7 +18,7 @@ using FluentAssertions; using MongoDB.Bson; -namespace MongoDB.Driver.Tests.Specifications.command_monitoring +namespace MongoDB.Driver.Tests.Specifications.command_logging_and_monitoring { public class UpdateManyTest : CrudOperationTestBase { diff --git a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/UpdateOneTest.cs b/tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/UpdateOneTest.cs similarity index 96% rename from tests/MongoDB.Driver.Tests/Specifications/command-monitoring/UpdateOneTest.cs rename to tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/UpdateOneTest.cs index 22fdd22c70d..df1f8799c03 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/command-monitoring/UpdateOneTest.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/command-logging-and-monitoring/UpdateOneTest.cs @@ -18,7 +18,7 @@ using FluentAssertions; using MongoDB.Bson; -namespace MongoDB.Driver.Tests.Specifications.command_monitoring +namespace MongoDB.Driver.Tests.Specifications.command_logging_and_monitoring { public class UpdateOneTest : CrudOperationTestBase { diff --git a/tests/MongoDB.Driver.Tests/Specifications/connection-monitoring-and-pooling/CmapUnifiedTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/connection-monitoring-and-pooling/CmapUnifiedTestRunner.cs new file mode 100644 index 00000000000..c1b58fc038e --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Specifications/connection-monitoring-and-pooling/CmapUnifiedTestRunner.cs @@ -0,0 +1,98 @@ +/* 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.Collections.Generic; +using MongoDB.Bson; +using MongoDB.Bson.TestHelpers.JsonDrivenTests; +using MongoDB.Driver.Core.Logging; +using MongoDB.Driver.Core.TestHelpers.Logging; +using MongoDB.Driver.Tests.UnifiedTestOperations; +using Xunit; +using Xunit.Abstractions; + +namespace MongoDB.Driver.Tests.Specifications.command_logging_and_monitoring +{ + public class CmapUnifiedTestRunner : LoggableTestClass + { + private static readonly HashSet __ignoredLogsMessages = new HashSet( + new[] + { + "Connection pool opening", + "Connection adding", + "Connection added", + "Connection adding", + "Connection checking in", + "Connection removing", + "Connection removed", + "Connection pool closing", + "Connection pool clearing", + "Connection opening", + "Connection failed", + "Connection opening failed", + "Connection closing", + "Sending", + "Sending failed", + "Sent", + "Receiving", + "Receiving failed", + "Received" + }); + + private static string __connectionCategoryName = LogCategoryHelper.GetCategoryName(); + + public CmapUnifiedTestRunner(ITestOutputHelper testOutputHelper) + : base(testOutputHelper, true) + { + } + + [Theory] + [ClassData(typeof(TestCaseFactory))] + public void Run(JsonDrivenTestCase testCase) + { + using (var runner = new UnifiedTestRunner(loggingService: this, loggingFilter: IsLogValid)) + { + runner.Run(testCase); + } + } + + public class TestCaseFactory : JsonDrivenTestCaseFactory + { + protected override string PathPrefix => "MongoDB.Driver.Tests.Specifications.connection_monitoring_and_pooling.tests.logging."; + + protected override IEnumerable CreateTestCases(BsonDocument document) + { + foreach (var testCase in base.CreateTestCases(document)) + { + // skip tests for obsolete pool parameters + if (testCase.Name.Contains("waitQueueMultiple")) + { + continue; + } + + foreach (var async in new[] { false, true }) + { + var name = $"{testCase.Name}:async={async}"; + var test = testCase.Test.DeepClone().AsBsonDocument.Add("async", async); + yield return new JsonDrivenTestCase(name, testCase.Shared, test); + } + } + } + } + + private static bool IsLogValid(LogEntry logEntry) => + logEntry.Category != __connectionCategoryName || + !__ignoredLogsMessages.Contains(logEntry.GetParameter(StructuredLogTemplateProviders.Message)); + } +} diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/ConnectionMonitoringAndPoolingTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/connection-monitoring-and-pooling/ConnectionMonitoringAndPoolingTestRunner.cs similarity index 96% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/ConnectionMonitoringAndPoolingTestRunner.cs rename to tests/MongoDB.Driver.Tests/Specifications/connection-monitoring-and-pooling/ConnectionMonitoringAndPoolingTestRunner.cs index 52c0d68511e..c53e51df346 100644 --- a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-monitoring-and-pooling/ConnectionMonitoringAndPoolingTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/connection-monitoring-and-pooling/ConnectionMonitoringAndPoolingTestRunner.cs @@ -33,7 +33,7 @@ using MongoDB.Driver.Core.ConnectionPools; using MongoDB.Driver.Core.Connections; using MongoDB.Driver.Core.Events; -using MongoDB.Driver.Core.Helpers; +using MongoDB.Driver.Core.Logging; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; using MongoDB.Driver.Core.TestHelpers; @@ -41,7 +41,7 @@ using Moq; using Xunit; -namespace MongoDB.Driver.Specifications.connection_monitoring_and_pooling +namespace MongoDB.Driver.Tests.Specifications.connection_monitoring_and_pooling { [Trait("Category", "Pool")] public class ConnectionMonitoringAndPoolingTestRunner @@ -125,7 +125,7 @@ public sealed class FailPoint #endregion - [SkippableTheory] + [Theory] [ClassData(typeof(TestCaseFactory))] public void RunTestDefinition(JsonDrivenTestCase testCase) { @@ -209,7 +209,7 @@ private void AssertEvent(object actualEvent, BsonDocument expectedEvent) } else { - actualEvent.ConnectionId().LocalValue.Should().Be(expectedConnectionId); + actualEvent.ConnectionId().LongLocalValue.Should().Be(expectedConnectionId); } } @@ -617,7 +617,7 @@ private void ParseSettings( private void ResetConnectionId() { - IdGeneratorReflector.__lastId(0); + LongIdGeneratorReflector.__lastId(0); } private (IConnectionPool, FailPoint, ICluster, Func) SetupConnectionData(BsonDocument test, EventCapturer eventCapturer, bool isUnit) @@ -636,7 +636,7 @@ private void ResetConnectionId() var connectionFactory = new Mock(); var connectionExceptionHandler = new Mock(); - + connectionFactory.Setup(f => f.ConnectionSettings).Returns(() => new ConnectionSettings()); connectionFactory .Setup(c => c.CreateConnection(serverId, endPoint)) .Returns(() => @@ -650,8 +650,8 @@ private void ResetConnectionId() endPoint, connectionPoolSettings, connectionFactory.Object, - eventCapturer, - connectionExceptionHandler.Object); + connectionExceptionHandler.Object, + eventCapturer.ToEventLogger()); connectionPool.Initialize(); } @@ -690,9 +690,9 @@ private void ResetConnectionId() { eventCapturer.WaitForOrThrowIfTimeout(events => events.Any(e => e is ConnectionCreatedEvent), TimeSpan.FromMilliseconds(500)); - var connectionIdsToIgnore = new HashSet(eventCapturer.Events + var connectionIdsToIgnore = new HashSet(eventCapturer.Events .OfType() - .Select(c => c.ConnectionId.LocalValue) + .Select(c => c.ConnectionId.LongLocalValue) .ToList()); eventsFilter = o => @@ -703,7 +703,7 @@ or ConnectionCreatedEvent or ConnectionFailedEvent) { var connectionId = o.ConnectionId(); - return !connectionIdsToIgnore.Contains(connectionId.LocalValue) && + return !connectionIdsToIgnore.Contains(connectionId.LongLocalValue) && EndPointHelper.Equals(connectionId.ServerId.EndPoint, server.EndPoint); } @@ -743,6 +743,7 @@ private IConnectionPool SetupConnectionPoolMock(BsonDocument test, IEventSubscri var connectionFactory = new Mock(); var exceptionHandler = new Mock(); + connectionFactory.Setup(f => f.ConnectionSettings).Returns(() => new ConnectionSettings()); connectionFactory .Setup(c => c.CreateConnection(serverId, endPoint)) .Returns(() => @@ -755,8 +756,8 @@ private IConnectionPool SetupConnectionPoolMock(BsonDocument test, IEventSubscri endPoint, connectionPoolSettings, connectionFactory.Object, - eventSubscriber, - exceptionHandler.Object); + exceptionHandler.Object, + eventSubscriber.ToEventLogger()); return connectionPool; } @@ -808,7 +809,7 @@ private void WaitForThread(BsonDocument operation, ConcurrentDictionary "MongoDB.Driver.Core.Tests.Specifications.connection_monitoring_and_pooling.tests."; + protected override string PathPrefix => "MongoDB.Driver.Tests.Specifications.connection_monitoring_and_pooling.tests.cmap_format."; protected override IEnumerable CreateTestCases(BsonDocument document) { @@ -842,9 +843,9 @@ public static ServerId ServerId(this object @event) } } - internal static class IdGeneratorReflector + internal static class LongIdGeneratorReflector { - public static void __lastId(int value) => Reflector.SetStaticFieldValue(typeof(IdGenerator), nameof(__lastId), value); + public static void __lastId(int value) => Reflector.SetStaticFieldValue(typeof(LongIdGenerator), nameof(__lastId), value); } internal static class IServerReflector diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/ConnectionStringTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/connection-string/ConnectionStringTestRunner.cs similarity index 98% rename from tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/ConnectionStringTestRunner.cs rename to tests/MongoDB.Driver.Tests/Specifications/connection-string/ConnectionStringTestRunner.cs index 90b8705d03d..f16531d4e29 100644 --- a/tests/MongoDB.Driver.Core.Tests/Specifications/connection-string/ConnectionStringTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/connection-string/ConnectionStringTestRunner.cs @@ -20,15 +20,16 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.TestHelpers.JsonDrivenTests; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Compression; using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers.Logging; using Xunit; using Xunit.Abstractions; +using Xunit.Sdk; -namespace MongoDB.Driver.Specifications.connection_string +namespace MongoDB.Driver.Tests.Specifications.connection_string { public class ConnectionStringTestRunner : LoggableTestClass { @@ -39,7 +40,7 @@ public ConnectionStringTestRunner(ITestOutputHelper testOutputHelper) } // public methods - [SkippableTheory] + [Theory] [ClassData(typeof(TestCaseFactory))] public void RunTestDefinition(JsonDrivenTestCase testCase) { @@ -411,8 +412,8 @@ private class TestCaseFactory : JsonDrivenTestCaseFactory protected override string[] PathPrefixes => new[] { - "MongoDB.Driver.Core.Tests.Specifications.connection_string.tests.", - "MongoDB.Driver.Core.Tests.Specifications.uri_options.tests." + "MongoDB.Driver.Tests.Specifications.connection_string.tests.", + "MongoDB.Driver.Tests.Specifications.uri_options.tests." }; protected override IEnumerable CreateTestCases(BsonDocument document) diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/CrudTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/crud/CrudTestRunner.cs index 016c2639a98..07803975379 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/crud/CrudTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/crud/CrudTestRunner.cs @@ -28,6 +28,7 @@ using MongoDB.Driver.TestHelpers; using Xunit; using Xunit.Abstractions; +using Xunit.Sdk; namespace MongoDB.Driver.Tests.Specifications.crud { @@ -58,7 +59,7 @@ public CrudTestRunner(ITestOutputHelper testOutputHelper) } // public methods - [SkippableTheory] + [Theory] [ClassData(typeof(TestCaseFactory))] public void Run(JsonDrivenTestCase testCase) { @@ -145,7 +146,7 @@ private DisposableMongoClient CreateDisposableClient(EventCapturer eventCapturer c.ConfigureServer(ss => ss.With(heartbeatInterval: Timeout.InfiniteTimeSpan)); }; }, - CreateLogger()); + LoggingSettings); } private void ExecuteOperation(IMongoClient client, IMongoDatabase database, IMongoCollection collection, BsonDocument operation, BsonDocument outcome, bool async) diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/CrudUnifiedTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/crud/CrudUnifiedTestRunner.cs index 204b3fd43ba..945dfaa96e1 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/crud/CrudUnifiedTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/crud/CrudUnifiedTestRunner.cs @@ -34,11 +34,11 @@ public CrudUnifiedTestRunner(ITestOutputHelper testOutputHelper) } // public methods - [SkippableTheory] + [Theory] [ClassData(typeof(TestCaseFactory))] public void Run(JsonDrivenTestCase testCase) { - using (var runner = new UnifiedTestRunner(loggerFactory: LoggerFactory)) + using (var runner = new UnifiedTestRunner(loggingService: this)) { runner.Run(testCase); } diff --git a/tests/MongoDB.Driver.Tests/Specifications/crud/prose-tests/CrudProseTests.cs b/tests/MongoDB.Driver.Tests/Specifications/crud/prose-tests/CrudProseTests.cs index 6daab505b62..12cdea327a9 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/crud/prose-tests/CrudProseTests.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/crud/prose-tests/CrudProseTests.cs @@ -41,7 +41,7 @@ public CrudProseTests(ITestOutputHelper output) : } // public methods - [SkippableFact] + [Fact] public void WriteConcernError_details_should_expose_writeConcernError_errInfo() { var failPointFeature = CoreTestConfiguration.Cluster.Description.Type == ClusterType.Sharded @@ -88,7 +88,7 @@ public void WriteConcernError_details_should_expose_writeConcernError_errInfo() } } - [SkippableFact] + [Fact] public void WriteError_details_should_expose_writeErrors_errInfo() { RequireServer.Check().VersionGreaterThanOrEqualTo(new SemanticVersion(5, 0, 0, "")); @@ -167,7 +167,7 @@ private DisposableMongoClient CreateDisposableClient(EventCapturer eventCapturer settings.HeartbeatInterval = TimeSpan.FromMilliseconds(5); settings.ClusterConfigurator = c => c.Subscribe(eventCapturer); }, - logger: CreateLogger()); + LoggingSettings); } } } diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/InitialDnsSeedlistDiscoveryTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/initial-dns-seedlist-discovery/InitialDnsSeedlistDiscoveryTestRunner.cs similarity index 96% rename from tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/InitialDnsSeedlistDiscoveryTestRunner.cs rename to tests/MongoDB.Driver.Tests/Specifications/initial-dns-seedlist-discovery/InitialDnsSeedlistDiscoveryTestRunner.cs index 196bb7bcee9..f5e2fdac6d5 100644 --- a/tests/MongoDB.Driver.Core.Tests/Specifications/initial-dns-seedlist-discovery/InitialDnsSeedlistDiscoveryTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/initial-dns-seedlist-discovery/InitialDnsSeedlistDiscoveryTestRunner.cs @@ -23,12 +23,12 @@ using MongoDB.Bson; using MongoDB.Driver.Core.Configuration; using Xunit; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using System.Collections; using System.Threading.Tasks; using Xunit.Abstractions; -namespace MongoDB.Driver.Specifications.initial_dns_seedlist_discovery +namespace MongoDB.Driver.Tests.Specifications.initial_dns_seedlist_discovery { [Trait("Category", "ConnectionString")] [Trait("Category", "SupportLoadBalancing")] @@ -166,7 +166,7 @@ private class TestCaseFactory : IEnumerable { public IEnumerator GetEnumerator() { - const string prefix = "MongoDB.Driver.Core.Tests.Specifications.initial_dns_seedlist_discovery.tests."; + const string prefix = "MongoDB.Driver.Tests.Specifications.initial_dns_seedlist_discovery.tests."; var executingAssembly = typeof(TestCaseFactory).GetTypeInfo().Assembly; return executingAssembly .GetManifestResourceNames() diff --git a/tests/MongoDB.Driver.Tests/Specifications/load-balancers/LoadBalancersTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/load-balancers/LoadBalancersTestRunner.cs index e534cae661b..d89d727f6b4 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/load-balancers/LoadBalancersTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/load-balancers/LoadBalancersTestRunner.cs @@ -17,7 +17,7 @@ using System.Collections.Generic; using MongoDB.Bson; using MongoDB.Bson.TestHelpers.JsonDrivenTests; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.TestHelpers.Logging; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; using MongoDB.Driver.TestHelpers; @@ -37,7 +37,7 @@ public LoadBalancersTestRunner(ITestOutputHelper output) : } // public methods - [SkippableTheory] + [Theory] [ClassData(typeof(TestCaseFactory))] public void Run(JsonDrivenTestCase testCase) { @@ -66,7 +66,7 @@ public void Run(JsonDrivenTestCase testCase) .SkipWhen(SupportedOperatingSystem.MacOS); #endif - using (var runner = new UnifiedTestRunner()) + using (var runner = new UnifiedTestRunner(loggingService: this)) { runner.Run(testCase); } diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/read-write-concern/ConnectionStringTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/read-write-concern/ConnectionStringTestRunner.cs similarity index 95% rename from tests/MongoDB.Driver.Core.Tests/Specifications/read-write-concern/ConnectionStringTestRunner.cs rename to tests/MongoDB.Driver.Tests/Specifications/read-write-concern/ConnectionStringTestRunner.cs index 22f5b6fdecd..02aa0de397e 100644 --- a/tests/MongoDB.Driver.Core.Tests/Specifications/read-write-concern/ConnectionStringTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/read-write-concern/ConnectionStringTestRunner.cs @@ -16,12 +16,12 @@ using System; using FluentAssertions; using MongoDB.Bson; +using MongoDB.Bson.TestHelpers.JsonDrivenTests; using MongoDB.Driver.Core.Configuration; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; -using MongoDB.Bson.TestHelpers.XunitExtensions; -using MongoDB.Bson.TestHelpers.JsonDrivenTests; -namespace MongoDB.Driver.Specifications.read_write_concern.tests +namespace MongoDB.Driver.Tests.Specifications.read_write_concern.tests { public class ConnectionStringTestRunner { @@ -109,7 +109,7 @@ private BsonDocument MassageWriteConcernDocument(BsonDocument writeConcern) private class TestCaseFactory : JsonDrivenTestCaseFactory { - protected override string PathPrefix => "MongoDB.Driver.Core.Tests.Specifications.read_write_concern.tests.connection_string."; + protected override string PathPrefix => "MongoDB.Driver.Tests.Specifications.read_write_concern.tests.connection_string."; } } } diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/read-write-concern/DocumentTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/read-write-concern/DocumentTestRunner.cs similarity index 96% rename from tests/MongoDB.Driver.Core.Tests/Specifications/read-write-concern/DocumentTestRunner.cs rename to tests/MongoDB.Driver.Tests/Specifications/read-write-concern/DocumentTestRunner.cs index 7b287e59df5..4550986dd32 100644 --- a/tests/MongoDB.Driver.Core.Tests/Specifications/read-write-concern/DocumentTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/read-write-concern/DocumentTestRunner.cs @@ -17,10 +17,10 @@ using FluentAssertions; using MongoDB.Bson; using Xunit; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Bson.TestHelpers.JsonDrivenTests; -namespace MongoDB.Driver.Specifications.read_write_concern.tests +namespace MongoDB.Driver.Tests.Specifications.read_write_concern.tests { public class DocumentTestRunner { @@ -135,7 +135,7 @@ private BsonDocument MassageWriteConcernDocument(BsonDocument writeConcern) private class TestCaseFactory : JsonDrivenTestCaseFactory { - protected override string PathPrefix => "MongoDB.Driver.Core.Tests.Specifications.read_write_concern.tests.document."; + protected override string PathPrefix => "MongoDB.Driver.Tests.Specifications.read_write_concern.tests.document."; } } } diff --git a/tests/MongoDB.Driver.Tests/Specifications/read-write-concern/OperationTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/read-write-concern/OperationTestRunner.cs index d73f424faa4..5b5c0d5132b 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/read-write-concern/OperationTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/read-write-concern/OperationTestRunner.cs @@ -33,7 +33,7 @@ public OperationTestRunner(ITestOutputHelper testOutputHelper) } // public methods - [SkippableTheory] + [Theory] [ClassData(typeof(TestCaseFactory))] public void Run(JsonDrivenTestCase testCase) { @@ -44,7 +44,7 @@ public void Run(JsonDrivenTestCase testCase) public class TestCaseFactory : JsonDrivenTestCaseFactory { // protected properties - protected override string PathPrefix => "MongoDB.Driver.Tests.Specifications.read_write_concern.tests."; + protected override string PathPrefix => "MongoDB.Driver.Tests.Specifications.read_write_concern.tests.operation."; // protected methods protected override IEnumerable CreateTestCases(BsonDocument document) diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/RetryableReadsProseTests.cs b/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/RetryableReadsProseTests.cs index da0d5641d4b..79f34dbafca 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/RetryableReadsProseTests.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/RetryableReadsProseTests.cs @@ -20,7 +20,7 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters.ServerSelectors; @@ -35,7 +35,7 @@ namespace MongoDB.Driver.Tests.Specifications.retryable_reads { public class RetryableReadsProseTests { - [SkippableTheory] + [Theory] [ParameterAttributeData] public async Task PoolClearedError_read_retryablity_test([Values(true, false)] bool async) { diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/RetryableReadsTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/RetryableReadsTestRunner.cs index ec99c1648a9..808939b16da 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/RetryableReadsTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/RetryableReadsTestRunner.cs @@ -18,6 +18,7 @@ using System.Linq; using System.Threading; using FluentAssertions; +using Microsoft.Extensions.Logging; using MongoDB.Bson; using MongoDB.Bson.TestHelpers.JsonDrivenTests; using MongoDB.Driver.Core; @@ -33,6 +34,7 @@ using MongoDB.Driver.Tests.JsonDrivenTests; using Xunit; using Xunit.Abstractions; +using Xunit.Sdk; namespace MongoDB.Driver.Tests.Specifications.retryable_reads { @@ -65,7 +67,7 @@ public RetryableReadsTestRunner(ITestOutputHelper testOutputHelper) } // public methods - [SkippableTheory] + [Theory] [ClassData(typeof(TestCaseFactory))] public void Run(JsonDrivenTestCase testCase) { @@ -139,12 +141,12 @@ private void Run(BsonDocument shared, BsonDocument test) AssertOutcome(test); } - Logger.Debug("Finished"); + Logger.LogDebug("Finished"); } private void DropCollection() { - Logger.Debug("Dropping collection {0}", _collectionName); + Logger.LogDebug("Dropping collection {0}", _collectionName); var client = DriverTestConfiguration.Client; var database = client.GetDatabase(_databaseName).WithWriteConcern(WriteConcern.WMajority); @@ -153,7 +155,7 @@ private void DropCollection() private void CreateCollection() { - Logger.Debug("Creating collection {0}", _collectionName); + Logger.LogDebug("Creating collection {0}", _collectionName); var client = DriverTestConfiguration.Client; var database = client.GetDatabase(_databaseName).WithWriteConcern(WriteConcern.WMajority); @@ -169,7 +171,7 @@ private void InsertData(BsonDocument shared) if (shared.Contains("bucket_name")) { - Logger.Debug("Inserting gridfs data"); + Logger.LogDebug("Inserting gridfs data"); InsertGridFsData(shared); return; @@ -183,7 +185,7 @@ private void InsertData(BsonDocument shared) var database = client.GetDatabase(_databaseName); var collection = database.GetCollection(_collectionName).WithWriteConcern(WriteConcern.WMajority); - Logger.Debug("Inserting documents {0}", documents?.Count); + Logger.LogDebug("Inserting documents {0}", documents?.Count); collection.InsertMany(documents); } @@ -215,7 +217,7 @@ private DisposableMongoClient CreateDisposableClient(BsonDocument test, EventCap ConfigureClientSettings(settings, test); settings.ClusterConfigurator = c => c.Subscribe(eventCapturer); }, - CreateLogger()); + LoggingSettings); } private void ConfigureClientSettings(MongoClientSettings settings, BsonDocument test) @@ -250,7 +252,7 @@ private ReadPreference ReadPreferenceFromBsonValue(BsonValue value) private IClientSessionHandle StartSession(IMongoClient client, BsonDocument test, string sessionKey) { - Logger.Debug("Starting session {0}", sessionKey); + Logger.LogDebug("Starting session {0}", sessionKey); var options = CreateSessionOptions(test, sessionKey); return client.StartSession(options); @@ -295,7 +297,7 @@ private FailPoint ConfigureFailPoint(BsonDocument test) var session = NoCoreSession.NewHandle(); var command = failPoint.AsBsonDocument; - Logger.Debug("Configuring failpoint"); + Logger.LogDebug("Configuring failpoint"); return FailPoint.Configure(cluster, session, command); } @@ -312,7 +314,7 @@ private void ExecuteOperations(IMongoClient client, Dictionary o var name = operation["name"].AsString; var jsonDrivenTest = factory.CreateTest(receiver, name); - Logger.Debug("Executing {0}", name); + Logger.LogDebug("Executing {0}", name); jsonDrivenTest.Arrange(operation); if (test["async"].AsBoolean) @@ -329,7 +331,7 @@ private void ExecuteOperations(IMongoClient client, Dictionary o private void ValidateOperations(BsonDocument test) { - Logger.Debug("Validating operations"); + Logger.LogDebug("Validating operations"); foreach (var operation in test["operations"].AsBsonArray.Cast()) { @@ -342,7 +344,7 @@ private void ValidateOperations(BsonDocument test) private void AssertEvents(EventCapturer actualEvents, BsonDocument test, Dictionary sessionIdMap) { - Logger.Debug("Asserting events"); + Logger.LogDebug("Asserting events"); if (test.Contains("expectations")) { @@ -384,7 +386,7 @@ private void AssertEvent(object actualEvent, BsonDocument expectedEvent) private void AssertOutcome(BsonDocument test) { - Logger.Debug("Asserting outcome"); + Logger.LogDebug("Asserting outcome"); if (test.Contains("outcome")) { diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/RetryableReadsUnifiedTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/RetryableReadsUnifiedTestRunner.cs index c942193e7dd..eb8f75ab1c5 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/RetryableReadsUnifiedTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/RetryableReadsUnifiedTestRunner.cs @@ -14,6 +14,7 @@ */ using System.Collections.Generic; +using System.Linq; using MongoDB.Bson; using MongoDB.Bson.TestHelpers.JsonDrivenTests; using MongoDB.Driver.Core.TestHelpers.Logging; @@ -27,6 +28,13 @@ namespace MongoDB.Driver.Tests.Specifications.retryable_reads [Trait("Category", "Serverless")] public sealed class RetryableReadsUnifiedTestRunner : LoggableTestClass { + private readonly static string[] __operationsToSkip = + new[] + { + "findOne", + "listIndexNames" + }; + // public constructors public RetryableReadsUnifiedTestRunner(ITestOutputHelper testOutputHelper) : base(testOutputHelper) @@ -34,11 +42,11 @@ public RetryableReadsUnifiedTestRunner(ITestOutputHelper testOutputHelper) } // public methods - [SkippableTheory] + [Theory] [ClassData(typeof(TestCaseFactory))] public void Run(JsonDrivenTestCase testCase) { - using (var runner = new UnifiedTestRunner()) + using (var runner = new UnifiedTestRunner(loggingService: this)) { runner.Run(testCase); } @@ -55,6 +63,11 @@ protected override IEnumerable CreateTestCases(BsonDocument { foreach (var testCase in base.CreateTestCases(document)) { + if (__operationsToSkip.Any(testCase.Name.Contains)) + { + continue; + } + foreach (var async in new[] { false, true }) { var name = $"{testCase.Name}:async={async}"; diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/unified/handshakeError.json b/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/unified/handshakeError.json deleted file mode 100644 index 2cf1d173f81..00000000000 --- a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/unified/handshakeError.json +++ /dev/null @@ -1,257 +0,0 @@ -{ - "description": "retryable reads handshake failures", - "schemaVersion": "1.3", - "runOnRequirements": [ - { - "minServerVersion": "4.2", - "topologies": [ - "replicaset", - "sharded", - "load-balanced" - ], - "auth": true - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": false, - "observeEvents": [ - "commandStartedEvent", - "connectionCheckOutStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "retryable-handshake-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll" - } - } - ], - "initialData": [ - { - "collectionName": "coll", - "databaseName": "retryable-handshake-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "tests": [ - { - "description": "find succeeds after retryable handshake network error", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "saslContinue", - "ping" - ], - "closeConnection": true - } - } - } - }, - { - "name": "runCommand", - "object": "database0", - "arguments": { - "commandName": "ping", - "command": { - "ping": 1 - } - }, - "expectError": { - "isError": true - } - }, - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": { - "_id": 2 - } - }, - "expectResult": [ - { - "_id": 2, - "x": 22 - } - ] - } - ], - "expectEvents": [ - { - "client": "client0", - "eventType": "cmap", - "events": [ - { - "connectionCheckOutStartedEvent": {} - }, - { - "connectionCheckOutStartedEvent": {} - }, - { - "connectionCheckOutStartedEvent": {} - }, - { - "connectionCheckOutStartedEvent": {} - } - ] - }, - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "ping": 1 - }, - "databaseName": "retryable-handshake-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "find": "coll", - "filter": { - "_id": 2 - } - }, - "databaseName": "retryable-handshake-tests" - } - } - ] - } - ] - }, - { - "description": "find succeeds after retryable handshake network error (ShutdownInProgress)", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "saslContinue", - "ping" - ], - "errorCode": 91 - } - } - } - }, - { - "name": "runCommand", - "object": "database0", - "arguments": { - "commandName": "ping", - "command": { - "ping": 1 - } - }, - "expectError": { - "isError": true - } - }, - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": { - "_id": 2 - } - }, - "expectResult": [ - { - "_id": 2, - "x": 22 - } - ] - } - ], - "expectEvents": [ - { - "client": "client0", - "eventType": "cmap", - "events": [ - { - "connectionCheckOutStartedEvent": {} - }, - { - "connectionCheckOutStartedEvent": {} - }, - { - "connectionCheckOutStartedEvent": {} - }, - { - "connectionCheckOutStartedEvent": {} - } - ] - }, - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "ping": 1 - }, - "databaseName": "retryable-handshake-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "find": "coll", - "filter": { - "_id": 2 - } - }, - "databaseName": "retryable-handshake-tests" - } - } - ] - } - ] - } - ] -} diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/unified/handshakeError.yml b/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/unified/handshakeError.yml deleted file mode 100644 index 90e6947dd10..00000000000 --- a/tests/MongoDB.Driver.Tests/Specifications/retryable-reads/tests/unified/handshakeError.yml +++ /dev/null @@ -1,129 +0,0 @@ -description: "retryable reads handshake failures" - -schemaVersion: "1.3" - -runOnRequirements: - - minServerVersion: "4.2" - topologies: [replicaset, sharded, load-balanced] - auth: true - -createEntities: - - client: - id: &client0 client0 - useMultipleMongoses: false - observeEvents: [commandStartedEvent, connectionCheckOutStartedEvent] - - database: - id: &database0 database0 - client: *client0 - databaseName: &databaseName retryable-handshake-tests - - collection: - id: &collection0 collection0 - database: *database0 - collectionName: &collectionName coll - -initialData: - - collectionName: *collectionName - databaseName: *databaseName - documents: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - -tests: - - description: "find succeeds after retryable handshake network error" - operations: - - name: failPoint # fail the next connection establishment - object: testRunner - arguments: - client: *client0 - failPoint: - configureFailPoint: failCommand - mode: { times: 2 } - data: - # use saslContinue here to avoid SDAM errors - # this failPoint itself will create a usable connection in the connection pool - # so we run a ping (that also fails) in order to discard the connection - # before testing our read operation "find" - failCommands: [saslContinue, ping] - closeConnection: true - - - name: runCommand - object: *database0 - arguments: - commandName: ping - command: { ping: 1 } - expectError: - isError: true - - - name: find - object: *collection0 - arguments: - filter: { _id: 2 } - expectResult: [{ _id: 2, x: 22 }] - - expectEvents: - - client: *client0 - eventType: cmap - events: - - { connectionCheckOutStartedEvent: {} } - - { connectionCheckOutStartedEvent: {} } - - { connectionCheckOutStartedEvent: {} } - - { connectionCheckOutStartedEvent: {} } - - client: *client0 - events: - - commandStartedEvent: - command: - ping: 1 - databaseName: *databaseName - - commandStartedEvent: - command: - find: *collectionName - filter: { _id: 2 } - databaseName: *databaseName - - - description: "find succeeds after retryable handshake network error (ShutdownInProgress)" - operations: - - name: failPoint # fail the next connection establishment - object: testRunner - arguments: - client: *client0 - failPoint: - configureFailPoint: failCommand - mode: { times: 2 } - data: - failCommands: [saslContinue, ping] - errorCode: 91 # ShutdownInProgress - - - name: runCommand - object: *database0 - arguments: - commandName: ping - command: { ping: 1 } - expectError: - isError: true - - - name: find - object: *collection0 - arguments: - filter: { _id: 2 } - expectResult: [{ _id: 2, x: 22 }] - - expectEvents: - - client: *client0 - eventType: cmap - events: - - { connectionCheckOutStartedEvent: {} } - - { connectionCheckOutStartedEvent: {} } - - { connectionCheckOutStartedEvent: {} } - - { connectionCheckOutStartedEvent: {} } - - client: *client0 - events: - - commandStartedEvent: - command: - ping: 1 - databaseName: *databaseName - - commandStartedEvent: - command: - find: *collectionName - filter: { _id: 2 } - databaseName: *databaseName diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/RetryableWriteTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/RetryableWriteTestRunner.cs index d1d1369d0fd..718e0de3a03 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/RetryableWriteTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/RetryableWriteTestRunner.cs @@ -19,12 +19,14 @@ using System.IO; using System.Linq; using System.Reflection; +using Microsoft.Extensions.Logging; using MongoDB.Bson; using MongoDB.Driver.Core.TestHelpers.Logging; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; using MongoDB.Driver.TestHelpers; using Xunit; using Xunit.Abstractions; +using Xunit.Sdk; namespace MongoDB.Driver.Tests.Specifications.retryable_writes { @@ -42,7 +44,7 @@ public RetryableWriteTestRunner(ITestOutputHelper testOutputHelper) } // public methods - [SkippableTheory] + [Theory] [ClassData(typeof(TestCaseFactory))] public void RunTestDefinition(TestCase testCase) { @@ -88,7 +90,7 @@ private void InitializeCollection(BsonDocument definition) var database = client.GetDatabase(_databaseName); var collection = database.GetCollection(_collectionName); - Logger.Debug("Dropping colleciton"); + Logger.LogDebug("Dropping colleciton"); database.DropCollection(collection.CollectionNamespace.CollectionName); if (definition.TryGetValue("data", out var data) && @@ -101,7 +103,7 @@ data is BsonArray dataArray && private void RunTest(BsonDocument test, bool async) { - Logger.Debug("Running test"); + Logger.LogDebug("Running test"); var useMultipleShardRouters = test.GetValue("useMultipleMongoses", false).AsBoolean; RequireServer.Check().MultipleMongosesIfSharded(required: useMultipleShardRouters); @@ -137,7 +139,7 @@ private DisposableMongoClient CreateDisposableClient(BsonDocument test, bool use settings.HeartbeatInterval = TimeSpan.FromMilliseconds(5); // the default value for spec tests ParseClientOptions(settings, clientOptions); }, - CreateLogger(), + LoggingSettings, useMultipleShardRouters); } diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/RetryableWritesUnifiedTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/RetryableWritesUnifiedTestRunner.cs index d1836f5727d..1559b9c6fab 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/RetryableWritesUnifiedTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/RetryableWritesUnifiedTestRunner.cs @@ -34,11 +34,11 @@ public RetryableWritesUnifiedTestRunner(ITestOutputHelper testOutputHelper) } // public methods - [SkippableTheory] + [Theory] [ClassData(typeof(TestCaseFactory))] public void Run(JsonDrivenTestCase testCase) { - using (var runner = new UnifiedTestRunner()) + using (var runner = new UnifiedTestRunner(loggingService: this)) { runner.Run(testCase); } diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/prose-tests/CommandConstructionTests.cs b/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/prose-tests/CommandConstructionTests.cs index 6350bfc1a2e..083373fd105 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/prose-tests/CommandConstructionTests.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/prose-tests/CommandConstructionTests.cs @@ -18,8 +18,9 @@ using System.Linq; using System.Threading; using FluentAssertions; +using Microsoft.Extensions.Logging; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Events; @@ -45,7 +46,7 @@ public CommandConstructionTests(ITestOutputHelper testOutputHelper) } // public methods - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Unacknowledged_writes_should_not_have_transaction_id( [Values("delete", "insert", "update")] string operation, @@ -108,7 +109,7 @@ public void Unacknowledged_writes_should_not_have_transaction_id( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Unsupported_single_statement_writes_should_not_have_transaction_id( [Values("deleteMany", "updateMany")] string operation, @@ -158,7 +159,7 @@ public void Unsupported_single_statement_writes_should_not_have_transaction_id( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Unsupported_multi_statement_writes_should_not_have_transaction_id( [Values("deleteMany", "updateMany")] string operation, @@ -203,7 +204,7 @@ public void Unsupported_multi_statement_writes_should_not_have_transaction_id( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Aggregate_with_write_stage_should_not_have_transaction_id( [Values("$out", "$merge")] string outStage, @@ -256,7 +257,7 @@ public void Aggregate_with_write_stage_should_not_have_transaction_id( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Supported_single_statement_writes_should_have_transaction_id( [Values("insertOne", "updateOne", "replaceOne", "deleteOne", "findOneAndDelete", "findOneAndReplace", "findOneAndUpdate")] string operation, @@ -369,7 +370,7 @@ public void Supported_single_statement_writes_should_have_transaction_id( } } - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Supported_multi_statement_writes_should_have_transaction_id( [Values("insertMany", "bulkWrite")] string operation, @@ -443,7 +444,7 @@ private DisposableMongoClient CreateDisposableClient(EventCapturer eventCapturer settings.ClusterConfigurator = c => c.Subscribe(eventCapturer); settings.RetryWrites = true; }, - logger: LoggerFactory.CreateLogger()); + LoggingSettings); } private EventCapturer CreateEventCapturer() @@ -469,13 +470,13 @@ private void DropAndCreateCollection() var client = DriverTestConfiguration.Client; var database = client.GetDatabase(_databaseName); - Logger.Debug("Dropping collection {0}", _collectionName); + Logger.LogDebug("Dropping collection {0}", _collectionName); database.DropCollection(_collectionName); - Logger.Debug("Creating collection {0}", _collectionName); + Logger.LogDebug("Creating collection {0}", _collectionName); database.CreateCollection(_collectionName); - Logger.Debug("Created collection {0}", _collectionName); + Logger.LogDebug("Created collection {0}", _collectionName); } private void DropCollection() @@ -483,7 +484,7 @@ private void DropCollection() var client = DriverTestConfiguration.Client; var database = client.GetDatabase(_databaseName); - Logger.Debug("Dropping collection {0}", _collectionName); + Logger.LogDebug("Dropping collection {0}", _collectionName); database.DropCollection(_collectionName); } diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/prose-tests/MMapV1Tests.cs b/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/prose-tests/MMapV1Tests.cs index b756b6a2847..e6da3104d68 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/prose-tests/MMapV1Tests.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/prose-tests/MMapV1Tests.cs @@ -16,17 +16,23 @@ using System; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters; +using MongoDB.Driver.Core.TestHelpers.Logging; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; using MongoDB.Driver.TestHelpers; using Xunit; +using Xunit.Abstractions; namespace MongoDB.Driver.Tests.Specifications.retryable_writes.prose_tests { - public class MMapV1Tests + public class MMapV1Tests : LoggableTestClass { - [SkippableTheory] + public MMapV1Tests(ITestOutputHelper output) : base(output) + { + } + + [Theory] [ParameterAttributeData] public void Write_operation_should_throw_when_retry_writes_is_true_and_storage_engine_is_MMMAPv1( [Values(false, true)] bool async) @@ -60,7 +66,7 @@ public void Write_operation_should_throw_when_retry_writes_is_true_and_storage_e // private methods private DisposableMongoClient CreateDisposableMongoClient() { - return DriverTestConfiguration.CreateDisposableClient(s => s.RetryWrites = true, logger: null); + return DriverTestConfiguration.CreateDisposableClient(s => s.RetryWrites = true, LoggingSettings); } } } diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/prose-tests/PoolClearRetryability.cs b/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/prose-tests/PoolClearRetryability.cs index f58f3d8efed..b6a10731301 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/prose-tests/PoolClearRetryability.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/prose-tests/PoolClearRetryability.cs @@ -20,7 +20,7 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters; @@ -36,7 +36,7 @@ namespace MongoDB.Driver.Tests.Specifications.retryable_writes.prose_tests { public class PoolClearRetryability { - [SkippableTheory] + [Theory] [ParameterAttributeData] public async Task PoolClearedError_write_retryablity_test([Values(false, true)] bool async) { diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/unified/handshakeError.json b/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/unified/handshakeError.json deleted file mode 100644 index 6d6b4ac4914..00000000000 --- a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/unified/handshakeError.json +++ /dev/null @@ -1,279 +0,0 @@ -{ - "description": "retryable writes handshake failures", - "schemaVersion": "1.3", - "runOnRequirements": [ - { - "minServerVersion": "4.2", - "topologies": [ - "replicaset", - "sharded", - "load-balanced" - ], - "auth": true - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": false, - "observeEvents": [ - "commandStartedEvent", - "connectionCheckOutStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "retryable-handshake-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll" - } - } - ], - "initialData": [ - { - "collectionName": "coll", - "databaseName": "retryable-handshake-tests", - "documents": [ - { - "_id": 1, - "x": 11 - } - ] - } - ], - "tests": [ - { - "description": "InsertOne succeeds after retryable handshake error", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "saslContinue", - "ping" - ], - "closeConnection": true - } - } - } - }, - { - "name": "runCommand", - "object": "database0", - "arguments": { - "commandName": "ping", - "command": { - "ping": 1 - } - }, - "expectError": { - "isError": true - } - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 2, - "x": 22 - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "eventType": "cmap", - "events": [ - { - "connectionCheckOutStartedEvent": {} - }, - { - "connectionCheckOutStartedEvent": {} - }, - { - "connectionCheckOutStartedEvent": {} - }, - { - "connectionCheckOutStartedEvent": {} - } - ] - }, - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "ping": 1 - }, - "databaseName": "retryable-handshake-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "insert": "coll", - "documents": [ - { - "_id": 2, - "x": 22 - } - ] - }, - "commandName": "insert", - "databaseName": "retryable-handshake-tests" - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll", - "databaseName": "retryable-handshake-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ] - }, - { - "description": "InsertOne succeeds after retryable handshake error ShutdownInProgress", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "saslContinue", - "ping" - ], - "errorCode": 91 - } - } - } - }, - { - "name": "runCommand", - "object": "database0", - "arguments": { - "commandName": "ping", - "command": { - "ping": 1 - } - }, - "expectError": { - "isError": true - } - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 2, - "x": 22 - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "eventType": "cmap", - "events": [ - { - "connectionCheckOutStartedEvent": {} - }, - { - "connectionCheckOutStartedEvent": {} - }, - { - "connectionCheckOutStartedEvent": {} - }, - { - "connectionCheckOutStartedEvent": {} - } - ] - }, - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "ping": 1 - }, - "databaseName": "retryable-handshake-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "insert": "coll", - "documents": [ - { - "_id": 2, - "x": 22 - } - ] - }, - "commandName": "insert", - "databaseName": "retryable-handshake-tests" - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll", - "databaseName": "retryable-handshake-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ] - } - ] -} diff --git a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/unified/handshakeError.yml b/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/unified/handshakeError.yml deleted file mode 100644 index bac3a7dba32..00000000000 --- a/tests/MongoDB.Driver.Tests/Specifications/retryable-writes/tests/unified/handshakeError.yml +++ /dev/null @@ -1,137 +0,0 @@ -description: "retryable writes handshake failures" - -schemaVersion: "1.3" - -runOnRequirements: - - minServerVersion: "4.2" - topologies: [replicaset, sharded, load-balanced] - auth: true - -createEntities: - - client: - id: &client0 client0 - useMultipleMongoses: false - observeEvents: [commandStartedEvent, connectionCheckOutStartedEvent] - - database: - id: &database0 database0 - client: *client0 - databaseName: &databaseName retryable-handshake-tests - - collection: - id: &collection0 collection0 - database: *database0 - collectionName: &collectionName coll - -initialData: - - collectionName: *collectionName - databaseName: *databaseName - documents: - - { _id: 1, x: 11 } - -tests: - - description: "InsertOne succeeds after retryable handshake error" - operations: - - name: failPoint # fail the next connection establishment - object: testRunner - arguments: - client: *client0 - failPoint: - configureFailPoint: failCommand - mode: { times: 2 } - data: - failCommands: [saslContinue, ping] - closeConnection: true - - - name: runCommand - object: *database0 - arguments: - commandName: ping - command: { ping: 1 } - expectError: - isError: true - - - name: insertOne - object: *collection0 - arguments: - document: { _id: 2, x: 22 } - - expectEvents: - - client: *client0 - eventType: cmap - events: - - { connectionCheckOutStartedEvent: {} } - - { connectionCheckOutStartedEvent: {} } - - { connectionCheckOutStartedEvent: {} } - - { connectionCheckOutStartedEvent: {} } - - client: *client0 - events: - - commandStartedEvent: - command: - ping: 1 - databaseName: *databaseName - - commandStartedEvent: - command: - insert: *collectionName - documents: [{ _id: 2, x: 22 }] - commandName: insert - databaseName: *databaseName - - outcome: - - collectionName: *collectionName - databaseName: *databaseName - documents: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } # The write was still applied - - - description: "InsertOne succeeds after retryable handshake error ShutdownInProgress" - operations: - - name: failPoint # fail the next connection establishment - object: testRunner - arguments: - client: *client0 - failPoint: - configureFailPoint: failCommand - mode: { times: 2 } - data: - failCommands: [saslContinue, ping] - errorCode: 91 # ShutdownInProgress - - - name: runCommand - object: *database0 - arguments: - commandName: ping - command: { ping: 1 } - expectError: - isError: true - - - name: insertOne - object: *collection0 - arguments: - document: { _id: 2, x: 22 } - - expectEvents: - - client: *client0 - eventType: cmap - events: - - { connectionCheckOutStartedEvent: {} } - - { connectionCheckOutStartedEvent: {} } - - { connectionCheckOutStartedEvent: {} } - - { connectionCheckOutStartedEvent: {} } - - client: *client0 - events: - - commandStartedEvent: - command: - ping: 1 - databaseName: *databaseName - - commandStartedEvent: - command: - insert: *collectionName - documents: [{ _id: 2, x: 22 }] - commandName: insert - databaseName: *databaseName - - outcome: - - collectionName: *collectionName - databaseName: *databaseName - documents: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } # The write was still applied diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/MonitoringTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/MonitoringTestRunner.cs similarity index 96% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/MonitoringTestRunner.cs rename to tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/MonitoringTestRunner.cs index 2af30c189f2..f6c31e69432 100644 --- a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/MonitoringTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/MonitoringTestRunner.cs @@ -21,25 +21,31 @@ using MongoDB.Bson; using MongoDB.Bson.TestHelpers; using MongoDB.Bson.TestHelpers.JsonDrivenTests; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.Connections; using MongoDB.Driver.Core.Events; -using MongoDB.Driver.Core.Helpers; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; +using MongoDB.Driver.Core.TestHelpers; +using MongoDB.Driver.Core.TestHelpers.Logging; using Xunit; +using Xunit.Abstractions; -namespace MongoDB.Driver.Specifications.sdam_monitoring +namespace MongoDB.Driver.Tests.Specifications.server_discovery_and_monitoring { - public class MonitoringTestRunner + public class MonitoringTestRunner : LoggableTestClass { private ICluster _cluster; private EventCapturer _eventSubscriber; private MockClusterableServerFactory _serverFactory; + public MonitoringTestRunner(ITestOutputHelper output) : base(output) + { + } + [Theory] [ClassData(typeof(TestCaseFactory))] public void RunTestDefinition(JsonDrivenTestCase testCase) @@ -351,15 +357,15 @@ private ICluster BuildCluster(BsonDocument definition) _eventSubscriber.Capture(e => true); _eventSubscriber.Capture(e => true); _eventSubscriber.Capture(e => true); - _serverFactory = new MockClusterableServerFactory(_eventSubscriber); - return new ClusterFactory(settings, _serverFactory, _eventSubscriber) + _serverFactory = new MockClusterableServerFactory(LoggerFactory, _eventSubscriber); + return new ClusterFactory(settings, _serverFactory, _eventSubscriber, LoggerFactory) .CreateCluster(); } // nested types private class TestCaseFactory : JsonDrivenTestCaseFactory { - protected override string PathPrefix => "MongoDB.Driver.Core.Tests.Specifications.server_discovery_and_monitoring.tests.monitoring."; + protected override string PathPrefix => "MongoDB.Driver.Tests.Specifications.server_discovery_and_monitoring.tests.monitoring."; protected override IEnumerable CreateTestCases(BsonDocument document) { diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/ServerDiscoveryAndMonitoringIntegrationTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/ServerDiscoveryAndMonitoringIntegrationTestRunner.cs index b6e6e35431f..e4005697921 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/ServerDiscoveryAndMonitoringIntegrationTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/ServerDiscoveryAndMonitoringIntegrationTestRunner.cs @@ -58,7 +58,7 @@ public async Task ConfigureFailPointAsync(IServer server, ICoreSessionHandle ses AddDisposable(failPoint); } - [SkippableTheory] + [Theory] [ClassData(typeof(TestCaseFactory))] public void Run(JsonDrivenTestCase testCase) { diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/ServerDiscoveryAndMonitoringProseTests.cs b/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/ServerDiscoveryAndMonitoringProseTests.cs index f54b6e571ba..97bdc6ce21d 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/ServerDiscoveryAndMonitoringProseTests.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/ServerDiscoveryAndMonitoringProseTests.cs @@ -37,7 +37,7 @@ namespace MongoDB.Driver.Tests.Specifications.server_discovery_and_monitoring { public class ServerDiscoveryAndMonitoringProseTests { - [SkippableFact] + [Fact] public void Heartbeat_should_work_as_expected() { var heartbeatSuceededTimestamps = new ConcurrentQueue(); @@ -76,7 +76,7 @@ public void Heartbeat_should_work_as_expected() } } - [SkippableFact] + [Fact] public void Monitor_sleep_at_least_minHeartbeatFreqencyMS_between_checks() { var minVersion = new SemanticVersion(4, 9, 0, ""); @@ -120,7 +120,7 @@ public void Monitor_sleep_at_least_minHeartbeatFreqencyMS_between_checks() sw.ElapsedMilliseconds.Should().BeInRange(2000, 3500); } - [SkippableFact] + [Fact] public void RoundTimeTrip_test() { RequireServer.Check().Supports(Feature.StreamingHello); @@ -172,7 +172,7 @@ public void RoundTimeTrip_test() } } - [SkippableFact] + [Fact] public void ConnectionPool_cleared_on_failed_hello() { var minVersion = new SemanticVersion(4, 9, 0, ""); @@ -249,14 +249,6 @@ private DisposableMongoClient CreateClient(MongoClientSettings mongoClientSettin } } - internal static class ServerReflector - { - public static IServerMonitor _monitor(this IServer server) - { - return (IServerMonitor)Reflector.GetFieldValue(server, nameof(_monitor)); - } - } - internal static class ServerMonitorReflector { public static IRoundTripTimeMonitor _roundTripTimeMonitor(this IServerMonitor serverMonitor) diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/ServerDiscoveryAndMonitoringTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/ServerDiscoveryAndMonitoringTestRunner.cs similarity index 95% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/ServerDiscoveryAndMonitoringTestRunner.cs rename to tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/ServerDiscoveryAndMonitoringTestRunner.cs index c1eed031dd5..e2978e23b50 100644 --- a/tests/MongoDB.Driver.Core.Tests/Specifications/server-discovery-and-monitoring/ServerDiscoveryAndMonitoringTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/ServerDiscoveryAndMonitoringTestRunner.cs @@ -15,7 +15,6 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Threading; using FluentAssertions; @@ -28,21 +27,26 @@ using MongoDB.Driver.Core.ConnectionPools; using MongoDB.Driver.Core.Connections; using MongoDB.Driver.Core.Events; -using MongoDB.Driver.Core.Helpers; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; using MongoDB.Driver.Core.TestHelpers; +using MongoDB.Driver.Core.TestHelpers.Logging; using Moq; using Xunit; +using Xunit.Abstractions; -namespace MongoDB.Driver.Specifications.server_discovery_and_monitoring +namespace MongoDB.Driver.Tests.Specifications.server_discovery_and_monitoring { - public class ServerDiscoveryAndMonitoringTestRunner + public class ServerDiscoveryAndMonitoringTestRunner : LoggableTestClass { private ICluster _cluster; private IEventSubscriber _eventSubscriber; private MockClusterableServerFactory _serverFactory; + public ServerDiscoveryAndMonitoringTestRunner(ITestOutputHelper output) : base(output) + { + } + [Theory] [ClassData(typeof(TestCaseFactory))] public void RunTestDefinition(JsonDrivenTestCase testCase) @@ -585,9 +589,9 @@ private ICluster BuildCluster(BsonDocument definition) } // Passing in an eventCapturer results in Server being used instead of a Mock - _serverFactory = new MockClusterableServerFactory(new EventCapturer()); + _serverFactory = new MockClusterableServerFactory(LoggerFactory, new EventCapturer()); _eventSubscriber = new Mock().Object; - return new ClusterFactory(settings, _serverFactory, _eventSubscriber) + return new ClusterFactory(settings, _serverFactory, _eventSubscriber, LoggerFactory) .CreateCluster(); } @@ -596,11 +600,14 @@ private class TestCaseFactory : JsonDrivenTestCaseFactory // private constants private readonly string[] MonitoringPrefixes = { - "MongoDB.Driver.Core.Tests.Specifications.server_discovery_and_monitoring.tests.monitoring.", - "MongoDB.Driver.Core.Tests.Specifications.server_discovery_and_monitoring.tests.legacy_hello.monitoring." + "MongoDB.Driver.Tests.Specifications.server_discovery_and_monitoring.tests.monitoring.", + "MongoDB.Driver.Tests.Specifications.server_discovery_and_monitoring.tests.legacy_hello.monitoring." }; - protected override string PathPrefix => "MongoDB.Driver.Core.Tests.Specifications.server_discovery_and_monitoring.tests."; + // Integration tests are run by ServerDiscoveryAndMonitoringIntegrationTestRunner in MongoDB.Driver.Tests + private const string IntegrationTestPrefix = "MongoDB.Driver.Tests.Specifications.server_discovery_and_monitoring.tests.integration."; + + protected override string PathPrefix => "MongoDB.Driver.Tests.Specifications.server_discovery_and_monitoring.tests."; protected override IEnumerable CreateTestCases(BsonDocument document) { @@ -610,10 +617,9 @@ protected override IEnumerable CreateTestCases(BsonDocument protected override bool ShouldReadJsonDocument(string path) { - var loadBalancerTestDirectory = $"{Path.PathSeparator}load-balanced{Path.PathSeparator}"; return base.ShouldReadJsonDocument(path) && !MonitoringPrefixes.Any(prefix => path.StartsWith(prefix)) && - !path.Contains(loadBalancerTestDirectory); // load balancer support not yet implemented + !path.StartsWith(IntegrationTestPrefix); } } } @@ -645,6 +651,11 @@ public static IConnectionPool _connectionPool(this Server server) return (IConnectionPool)Reflector.GetFieldValue(server, nameof(_connectionPool)); } + public static IServerMonitor _monitor(this IServer server) + { + return (IServerMonitor)Reflector.GetFieldValue(server, nameof(_monitor)); + } + public static void HandleBeforeHandshakeCompletesException(this Server server, Exception ex) { Reflector.Invoke(server, nameof(HandleBeforeHandshakeCompletesException), ex); diff --git a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/prose-tests/ServerDiscoveryProseTests.cs b/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/prose-tests/ServerDiscoveryProseTests.cs index 7a29846633c..05f7a3dda79 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/prose-tests/ServerDiscoveryProseTests.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/server-discovery-and-monitoring/prose-tests/ServerDiscoveryProseTests.cs @@ -19,8 +19,9 @@ using System.Threading; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Clusters; +using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; using MongoDB.Driver.Core.TestHelpers.Logging; @@ -40,7 +41,7 @@ public ServerDiscoveryProseTests(ITestOutputHelper testOutputHelper) } // public methods - [SkippableTheory] + [Theory] [ParameterAttributeData] public void Topology_secondary_discovery_with_directConnection_false_should_work_as_expected([Values(false, true, null)] bool? directConnection) { @@ -65,7 +66,10 @@ public void Topology_secondary_discovery_with_directConnection_false_should_work var dnsEndpoint = (DnsEndPoint)secondary.EndPoint; var replicaSetName = secondary.ReplicaSetConfig.Name; - using (var client = new DisposableMongoClient(new MongoClient(CreateConnectionString(dnsEndpoint, directConnection, replicaSetName)), CreateLogger())) + var settings = MongoClientSettings.FromConnectionString(CreateConnectionString(dnsEndpoint, directConnection, replicaSetName)); + settings.LoggingSettings = LoggingSettings; + + using (var client = new DisposableMongoClient(new MongoClient(settings), CreateLogger())) { var database = client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName); var collection = database.GetCollection(DriverTestConfiguration.CollectionNamespace.CollectionName); diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/InWindowTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/server-selection/InWindowTestRunner.cs similarity index 93% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/InWindowTestRunner.cs rename to tests/MongoDB.Driver.Tests/Specifications/server-selection/InWindowTestRunner.cs index 9e7787555a5..1d8c9aa581b 100644 --- a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/InWindowTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/server-selection/InWindowTestRunner.cs @@ -26,12 +26,14 @@ using MongoDB.Driver.Core.Clusters.ServerSelectors; using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.Servers; +using MongoDB.Driver.Core.TestHelpers.Logging; using Moq; using Xunit; +using Xunit.Abstractions; -namespace MongoDB.Driver.Specifications.server_selection +namespace MongoDB.Driver.Tests.Specifications.server_selection { - public sealed class InWindowTestRunner + public sealed class InWindowTestRunner : LoggableTestClass { private sealed class OperationsCount { @@ -57,6 +59,10 @@ private sealed class TestData public Outcome outcome { get; set; } } + public InWindowTestRunner(ITestOutputHelper output) : base(output) + { + } + [Theory] [ClassData(typeof(TestCaseFactory))] public void RunTestDefinition(JsonDrivenTestCase testCase) @@ -121,7 +127,7 @@ private MultiServerCluster CreateAndSetupCluster(ClusterDescription clusterDescr return server.Object; }); - var result = new MultiServerCluster(clusterSettings, mockServerFactory.Object, new EventCapturer()); + var result = new MultiServerCluster(clusterSettings, mockServerFactory.Object, new EventCapturer(), LoggerFactory); result.Initialize(); return result; } @@ -131,7 +137,7 @@ private class TestCaseFactory : JsonDrivenTestCaseFactory { protected override string[] PathPrefixes => new[] { - "MongoDB.Driver.Core.Tests.Specifications.server_selection.tests.in_window.", + "MongoDB.Driver.Tests.Specifications.server_selection.tests.in_window.", }; protected override IEnumerable CreateTestCases(BsonDocument document) diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/RttTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/server-selection/RttTestRunner.cs similarity index 95% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/RttTestRunner.cs rename to tests/MongoDB.Driver.Tests/Specifications/server-selection/RttTestRunner.cs index c05dd8bc693..574bf4bd60e 100644 --- a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/RttTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/server-selection/RttTestRunner.cs @@ -24,7 +24,7 @@ using MongoDB.Driver.Core.Misc; using Xunit; -namespace MongoDB.Driver.Specifications.server_selection +namespace MongoDB.Driver.Tests.Specifications.server_selection { public class RttTestRunner { @@ -51,7 +51,7 @@ private class TestCaseFactory : IEnumerable { public IEnumerator GetEnumerator() { - const string prefix = "MongoDB.Driver.Core.Tests.Specifications.server_selection.tests.rtt."; + const string prefix = "MongoDB.Driver.Tests.Specifications.server_selection.tests.rtt."; var executingAssembly = typeof(TestCaseFactory).GetTypeInfo().Assembly; var enumerable = executingAssembly .GetManifestResourceNames() diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/ServerSelectionTestHelpers.cs b/tests/MongoDB.Driver.Tests/Specifications/server-selection/ServerSelectionTestHelpers.cs similarity index 99% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/ServerSelectionTestHelpers.cs rename to tests/MongoDB.Driver.Tests/Specifications/server-selection/ServerSelectionTestHelpers.cs index 4fc6a7b6b02..e619b322779 100644 --- a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/ServerSelectionTestHelpers.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/server-selection/ServerSelectionTestHelpers.cs @@ -22,7 +22,7 @@ using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; -namespace MongoDB.Driver.Specifications.server_selection +namespace MongoDB.Driver.Tests.Specifications.server_selection { internal static class ServerSelectionTestHelper { diff --git a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/ServerSelectionTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/server-selection/ServerSelectionTestRunner.cs similarity index 96% rename from tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/ServerSelectionTestRunner.cs rename to tests/MongoDB.Driver.Tests/Specifications/server-selection/ServerSelectionTestRunner.cs index 572c70ab015..4e1e1904c80 100644 --- a/tests/MongoDB.Driver.Core.Tests/Specifications/server-selection/ServerSelectionTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/server-selection/ServerSelectionTestRunner.cs @@ -25,7 +25,7 @@ using MongoDB.Driver.Core.Servers; using Xunit; -namespace MongoDB.Driver.Specifications.server_selection +namespace MongoDB.Driver.Tests.Specifications.server_selection { public class ServerSelectionTestRunner { @@ -150,8 +150,8 @@ private class TestCaseFactory : JsonDrivenTestCaseFactory { protected override string[] PathPrefixes => new[] { - "MongoDB.Driver.Core.Tests.Specifications.server_selection.tests.server_selection.", - "MongoDB.Driver.Core.Tests.Specifications.max_staleness.tests." + "MongoDB.Driver.Tests.Specifications.server_selection.tests.server_selection.", + "MongoDB.Driver.Tests.Specifications.max_staleness.tests." }; protected override IEnumerable CreateTestCases(BsonDocument document) diff --git a/tests/MongoDB.Driver.Tests/Specifications/sessions/SessionsProseTests.cs b/tests/MongoDB.Driver.Tests/Specifications/sessions/SessionsProseTests.cs index f38c2c9c1e9..33627757869 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/sessions/SessionsProseTests.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/sessions/SessionsProseTests.cs @@ -20,18 +20,24 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.TestHelpers; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core; using MongoDB.Driver.Core.Events; +using MongoDB.Driver.Core.TestHelpers.Logging; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; using Xunit; +using Xunit.Abstractions; namespace MongoDB.Driver.Tests.Specifications.sessions { [Trait("Category", "Serverless")] - public class SessionsProseTests + public class SessionsProseTests : LoggableTestClass { - [SkippableFact] + public SessionsProseTests(ITestOutputHelper output) : base(output) + { + } + + [Fact] public void Snapshot_and_causal_consistent_session_is_not_allowed() { RequireServer.Check(); @@ -48,7 +54,7 @@ public void Snapshot_and_causal_consistent_session_is_not_allowed() exception.Should().BeOfType(); } - [SkippableTheory] + [Theory] [ParameterAttributeData] public async Task Ensure_server_session_are_allocated_only_on_connection_checkout([Values(true, false)]bool async) { @@ -62,7 +68,7 @@ public async Task Ensure_server_session_are_allocated_only_on_connection_checkou settings.MaxConnectionPoolSize = 1; settings.ClusterConfigurator = c => c.Subscribe(eventCapturer); }, - logger: null); + LoggingSettings); var database = client.GetDatabase("test"); @@ -183,7 +189,7 @@ await ThreadingUtilities.ExecuteTasksOnNewThreads(operationsCount, async i => singleSessionUsed.Should().BeTrue("At least one iteration should use single session"); } - [SkippableFact] + [Fact] public async Task Ensure_server_session_are_allocated_only_on_connection_checkout_deterministic() { var eventCapturer = new EventCapturer() @@ -196,7 +202,7 @@ public async Task Ensure_server_session_are_allocated_only_on_connection_checkou settings.MaxConnectionPoolSize = 1; settings.ClusterConfigurator = c => c.Subscribe(eventCapturer); }, - logger: null); + LoggingSettings); var database = client.GetDatabase("test"); database.DropCollection("inventory"); diff --git a/tests/MongoDB.Driver.Tests/Specifications/sessions/SessionsTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/sessions/SessionsTestRunner.cs index a9b15477bee..cbebc6a67d7 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/sessions/SessionsTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/sessions/SessionsTestRunner.cs @@ -34,7 +34,7 @@ public SessionsTestRunner(ITestOutputHelper testOutputHelper) { } - [SkippableTheory] + [Theory] [ClassData(typeof(TestCaseFactory))] public void RunTestDefinition(JsonDrivenTestCase testCase) { diff --git a/tests/MongoDB.Driver.Tests/Specifications/sessions/SessionsUnifiedTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/sessions/SessionsUnifiedTestRunner.cs index bbdf090b4fa..2b235945789 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/sessions/SessionsUnifiedTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/sessions/SessionsUnifiedTestRunner.cs @@ -33,11 +33,11 @@ public SessionsUnifiedTestRunner(ITestOutputHelper testOutputHelper) } // public methods - [SkippableTheory] + [Theory] [ClassData(typeof(TestCaseFactory))] public void Run(JsonDrivenTestCase testCase) { - using (var runner = new UnifiedTestRunner()) + using (var runner = new UnifiedTestRunner(loggingService: this)) { runner.Run(testCase); } diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/TransactionsConvenientApiTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/TransactionsConvenientApiTestRunner.cs index cbc2a4b3204..befc9945a57 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/TransactionsConvenientApiTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/transactions-convenient-api/TransactionsConvenientApiTestRunner.cs @@ -31,7 +31,7 @@ public TransactionsConvenientApiTestRunner(ITestOutputHelper testOutputHelper) { } - [SkippableTheory] + [Theory] [ClassData(typeof(TestCaseFactory))] public void Run(JsonDrivenTestCase testCase) { diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/TransactionTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/transactions/TransactionTestRunner.cs index e14c2b66ad8..b7c94d1ebfa 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/transactions/TransactionTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/transactions/TransactionTestRunner.cs @@ -19,6 +19,7 @@ using System.Threading; using System.Threading.Tasks; using FluentAssertions; +using Microsoft.Extensions.Logging; using MongoDB.Bson; using MongoDB.Bson.TestHelpers.JsonDrivenTests; using MongoDB.Driver; @@ -37,6 +38,7 @@ using MongoDB.Driver.Tests.JsonDrivenTests; using Xunit; using Xunit.Abstractions; +using Xunit.Sdk; namespace MongoDB.Driver.Tests.Specifications.transactions { @@ -96,33 +98,22 @@ public async Task ConfigureFailPointAsync(IServer server, ICoreSessionHandle ses _disposables.Add(failPoint); } - public override void Dispose() - { - Dispose(disposing: true); - GC.SuppressFinalize(this); - - base.Dispose(); - } - - [SkippableTheory] + [Theory] [ClassData(typeof(TestCaseFactory))] public void Run(JsonDrivenTestCase testCase) { Run(testCase.Shared, testCase.Test); } - // private methods - private void Dispose(bool disposing) + protected override void DisposeInternal() { - if (disposing) + foreach (var disposable in _disposables) { - foreach (var disposable in _disposables) - { - disposable.Dispose(); - } + disposable.Dispose(); } } + // private methods private void Run(BsonDocument shared, BsonDocument test) { if (test.Contains("skipReason")) @@ -217,7 +208,7 @@ private void KillAllSessions() private void DropCollection() { - Logger.Debug("Dropping collection {0}", _collectionName); + Logger.LogDebug("Dropping collection {0}", _collectionName); var client = DriverTestConfiguration.Client; var database = client.GetDatabase(_databaseName).WithWriteConcern(WriteConcern.WMajority); @@ -226,7 +217,7 @@ private void DropCollection() private void CreateCollection() { - Logger.Debug("Creating collection {0}", _collectionName); + Logger.LogDebug("Creating collection {0}", _collectionName); var client = DriverTestConfiguration.Client; var database = client.GetDatabase(_databaseName).WithWriteConcern(WriteConcern.WMajority); @@ -235,7 +226,7 @@ private void CreateCollection() private void InsertData(BsonDocument shared) { - Logger.Debug("Inserting data"); + Logger.LogDebug("Inserting data"); if (shared.Contains("data")) { @@ -272,7 +263,7 @@ private DisposableMongoClient CreateDisposableClient(BsonDocument test, EventCap ConfigureClientSettings(settings, test); settings.ClusterConfigurator = c => c.Subscribe(eventCapturer); }, - CreateLogger(), + LoggingSettings, useMultipleShardRouters); } @@ -332,7 +323,7 @@ private ReadPreference ReadPreferenceFromBsonValue(BsonValue value) private IClientSessionHandle StartSession(IMongoClient client, BsonDocument test, string sessionKey) { - Logger.Debug("Starting session"); + Logger.LogDebug("Starting session"); var options = ParseSessionOptions(test, sessionKey); return client.StartSession(options); @@ -414,7 +405,7 @@ private void ExecuteOperations(IMongoClient client, Dictionary o var name = operation["name"].AsString; var jsonDrivenTest = factory.CreateTest(receiver, name); - Logger.Debug("Execution operation {0}", name); + Logger.LogDebug("Execution operation {0}", name); jsonDrivenTest.Arrange(operation); if (test["async"].AsBoolean) @@ -431,7 +422,7 @@ private void ExecuteOperations(IMongoClient client, Dictionary o private void AssertEvents(EventCapturer actualEvents, BsonDocument test, Dictionary sessionIdMap) { - Logger.Debug("Asserting events"); + Logger.LogDebug("Asserting events"); if (test.Contains("expectations")) { @@ -473,7 +464,7 @@ private void AssertEvent(object actualEvent, BsonDocument expectedEvent) private void AssertOutcome(BsonDocument test) { - Logger.Debug("Asserting outcome"); + Logger.LogDebug("Asserting outcome"); if (test.Contains("outcome")) { diff --git a/tests/MongoDB.Driver.Tests/Specifications/transactions/TransactionUnifiedTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/transactions/TransactionUnifiedTestRunner.cs index 7f1fdb045de..a09003f0407 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/transactions/TransactionUnifiedTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/transactions/TransactionUnifiedTestRunner.cs @@ -34,16 +34,11 @@ public TransactionUnifiedTestRunner(ITestOutputHelper testOutputHelper) } // public methods - [SkippableTheory] + [Theory] [ClassData(typeof(TestCaseFactory))] public void Run(JsonDrivenTestCase testCase) { - if (testCase.Name.Contains("mongos-unpin.json")) - { - throw new SkipException("Load balancer support not yet implemented."); - } - - using (var runner = new UnifiedTestRunner()) + using (var runner = new UnifiedTestRunner(loggingService: this)) { runner.Run(testCase); } diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/UnifiedTestFormatValidFailTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/UnifiedTestFormatValidFailTestRunner.cs index 391edcf7e49..06990b94579 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/UnifiedTestFormatValidFailTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/UnifiedTestFormatValidFailTestRunner.cs @@ -17,7 +17,7 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.TestHelpers.JsonDrivenTests; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.TestHelpers.Logging; using MongoDB.Driver.Tests.UnifiedTestOperations; using Xunit; @@ -34,7 +34,7 @@ public UnifiedTestFormatValidFailTestRunner(ITestOutputHelper testOutputHelper) } // public methods - [SkippableTheory] + [Theory] [ClassData(typeof(TestCaseFactory))] public void Run(JsonDrivenTestCase testCase) { @@ -44,7 +44,7 @@ public void Run(JsonDrivenTestCase testCase) RequireEnvironment.Check().EnvironmentVariable("KMS_MOCK_SERVERS_ENABLED"); } - using (var runner = new UnifiedTestRunner(loggerFactory: LoggerFactory)) + using (var runner = new UnifiedTestRunner(loggingService: this)) { var exception = Record.Exception(() => runner.Run(testCase)); exception.Should().NotBeNull(); diff --git a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/UnifiedTestFormatValidPassTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/UnifiedTestFormatValidPassTestRunner.cs index e368d6dd9be..662040c06c2 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/UnifiedTestFormatValidPassTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/unified-test-format/UnifiedTestFormatValidPassTestRunner.cs @@ -17,7 +17,7 @@ using System.Linq; using MongoDB.Bson; using MongoDB.Bson.TestHelpers.JsonDrivenTests; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.TestHelpers.Logging; using MongoDB.Driver.Tests.UnifiedTestOperations; using Xunit; @@ -34,7 +34,7 @@ public UnifiedTestFormatValidPassTestRunner(ITestOutputHelper testOutputHelper) } // public methods - [SkippableTheory] + [Theory] [ClassData(typeof(TestCaseFactory))] public void Run(JsonDrivenTestCase testCase) { @@ -44,7 +44,7 @@ public void Run(JsonDrivenTestCase testCase) RequireEnvironment.Check().EnvironmentVariable("KMS_MOCK_SERVERS_ENABLED"); } - using (var runner = new UnifiedTestRunner(loggerFactory: LoggerFactory)) + using (var runner = new UnifiedTestRunner(loggingService: this)) { runner.Run(testCase); } diff --git a/tests/MongoDB.Bson.Tests/Specifications/uuid/prose-tests/ExplicitDecodingTests.cs b/tests/MongoDB.Driver.Tests/Specifications/uuid/prose-tests/ExplicitDecodingTests.cs similarity index 98% rename from tests/MongoDB.Bson.Tests/Specifications/uuid/prose-tests/ExplicitDecodingTests.cs rename to tests/MongoDB.Driver.Tests/Specifications/uuid/prose-tests/ExplicitDecodingTests.cs index c5f93e4bfec..0f36dbb40d0 100644 --- a/tests/MongoDB.Bson.Tests/Specifications/uuid/prose-tests/ExplicitDecodingTests.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/uuid/prose-tests/ExplicitDecodingTests.cs @@ -15,10 +15,11 @@ using System; using FluentAssertions; +using MongoDB.Bson; using MongoDB.Bson.TestHelpers; using Xunit; -namespace MongoDB.Bson.Tests.Specifications.uuid.prose_tests +namespace MongoDB.Driver.Tests.Specifications.uuid.prose_tests { public class ExplicitDecodingTests { diff --git a/tests/MongoDB.Bson.Tests/Specifications/uuid/prose-tests/ExplicitEncodingTests.cs b/tests/MongoDB.Driver.Tests/Specifications/uuid/prose-tests/ExplicitEncodingTests.cs similarity index 97% rename from tests/MongoDB.Bson.Tests/Specifications/uuid/prose-tests/ExplicitEncodingTests.cs rename to tests/MongoDB.Driver.Tests/Specifications/uuid/prose-tests/ExplicitEncodingTests.cs index 47eb443e4cc..ac3785d3cfc 100644 --- a/tests/MongoDB.Bson.Tests/Specifications/uuid/prose-tests/ExplicitEncodingTests.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/uuid/prose-tests/ExplicitEncodingTests.cs @@ -15,10 +15,11 @@ using System; using FluentAssertions; +using MongoDB.Bson; using MongoDB.Bson.TestHelpers; using Xunit; -namespace MongoDB.Bson.Tests.Specifications.uuid.prose_tests +namespace MongoDB.Driver.Tests.Specifications.uuid.prose_tests { public class ExplicitEncodingTests { diff --git a/tests/MongoDB.Driver.Tests/Specifications/versioned-api/VersionedApiUnifiedTestRunner.cs b/tests/MongoDB.Driver.Tests/Specifications/versioned-api/VersionedApiUnifiedTestRunner.cs index 1f3d72f590b..3a449ab2288 100644 --- a/tests/MongoDB.Driver.Tests/Specifications/versioned-api/VersionedApiUnifiedTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/Specifications/versioned-api/VersionedApiUnifiedTestRunner.cs @@ -34,11 +34,11 @@ public VersionedApiUnifiedTestRunner(ITestOutputHelper testOutputHelper) } // public methods - [SkippableTheory] + [Theory] [ClassData(typeof(TestCaseFactory))] public void Run(JsonDrivenTestCase testCase) { - using (var runner = new UnifiedTestRunner()) + using (var runner = new UnifiedTestRunner(loggingService: this)) { runner.Run(testCase); } diff --git a/tests/MongoDB.Driver.Tests/SslSettingsTests.cs b/tests/MongoDB.Driver.Tests/SslSettingsTests.cs index 4cd2f6c2229..2a1c1c80d8f 100644 --- a/tests/MongoDB.Driver.Tests/SslSettingsTests.cs +++ b/tests/MongoDB.Driver.Tests/SslSettingsTests.cs @@ -184,7 +184,7 @@ public void TestServerCertificateValidationCallback() private string GetTestCertificateFileName() { - var codeBase = typeof(SslSettingsTests).GetTypeInfo().Assembly.CodeBase; + var codeBase = typeof(SslSettingsTests).GetTypeInfo().Assembly.Location; var codeBaseUrl = new Uri(codeBase); var codeBasePath = Uri.UnescapeDataString(codeBaseUrl.AbsolutePath); var codeBaseDirectory = Path.GetDirectoryName(codeBasePath); diff --git a/tests/MongoDB.Driver.Tests/TargetFrameworkTests.cs b/tests/MongoDB.Driver.Tests/TargetFrameworkTests.cs index 9aa5068696a..818c6f57885 100644 --- a/tests/MongoDB.Driver.Tests/TargetFrameworkTests.cs +++ b/tests/MongoDB.Driver.Tests/TargetFrameworkTests.cs @@ -23,7 +23,7 @@ public class TargetFrameworkTests [Fact] public void TargetFramework_should_be_valid() { - var actualFramework = MongoDB.Driver.TargetFramework.Moniker; + var actualFramework = TargetFramework.Moniker; var expectedFramework = GetExpectedTargetFramework(); actualFramework.Should().Be(expectedFramework); } @@ -33,7 +33,7 @@ private string GetExpectedTargetFramework() { #if NETCOREAPP2_1 return "netstandard20"; -#elif NETCOREAPP3_1 +#elif NETCOREAPP3_1_OR_GREATER return "netstandard21"; #elif NET472 return "net472"; diff --git a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/Matchers/UnifiedErrorMatcher.cs b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/Matchers/UnifiedErrorMatcher.cs index 7e30a51322d..d9dc77a4d57 100644 --- a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/Matchers/UnifiedErrorMatcher.cs +++ b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/Matchers/UnifiedErrorMatcher.cs @@ -18,7 +18,7 @@ using System.Linq; using FluentAssertions; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; namespace MongoDB.Driver.Tests.UnifiedTestOperations.Matchers { diff --git a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/Matchers/UnifiedEventMatcher.cs b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/Matchers/UnifiedEventMatcher.cs index 28963e3a3c4..3b178d900f4 100644 --- a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/Matchers/UnifiedEventMatcher.cs +++ b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/Matchers/UnifiedEventMatcher.cs @@ -16,10 +16,11 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Xml.Linq; using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.IO; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Connections; using MongoDB.Driver.Core.Events; using Xunit.Sdk; @@ -79,169 +80,166 @@ public void AssertEventsMatch(List actualEvents, BsonArray expectedEvent } } - // private methods - private void AssertEvents(List actualEvents, BsonArray expectedEventsDocuments, bool ignoreExtraEvents) + public void AssertEventsMatch(object actualEvent, BsonDocument expectedEventDocument) { - if (ignoreExtraEvents) - { - actualEvents.Count.Should().BeGreaterOrEqualTo(expectedEventsDocuments.Count); - } - else + if (expectedEventDocument.ElementCount != 1) { - actualEvents.Should().HaveSameCount(expectedEventsDocuments); + throw new FormatException("Expected event document model must contain a single element."); } + var expectedEventType = expectedEventDocument.GetElement(0).Name; + var expectedEventValue = expectedEventDocument[0].AsBsonDocument; - for (int i = 0; i < expectedEventsDocuments.Count; i++) + switch (expectedEventType) { - var actualEvent = actualEvents[i]; - var expectedEventDocument = expectedEventsDocuments[i].AsBsonDocument; - if (expectedEventDocument.ElementCount != 1) - { - throw new FormatException("Expected event document model must contain a single element."); - } - var expectedEventType = expectedEventDocument.GetElement(0).Name; - var expectedEventValue = expectedEventDocument[0].AsBsonDocument; - - switch (expectedEventType) - { - case "commandStartedEvent": - var commandStartedEvent = actualEvent.Should().BeOfType().Subject; - foreach (var element in expectedEventValue) + case "commandStartedEvent": + var commandStartedEvent = actualEvent.Should().BeOfType().Subject; + foreach (var element in expectedEventValue) + { + switch (element.Name) { - switch (element.Name) - { - case "command": - _valueMatcher.AssertValuesMatch(commandStartedEvent.Command, element.Value); - break; - case "commandName": - commandStartedEvent.CommandName.Should().Be(element.Value.AsString); - break; - case "databaseName": - commandStartedEvent.DatabaseNamespace.DatabaseName.Should().Be(element.Value.AsString); - break; - case "hasServiceId": - commandStartedEvent.ServiceId.Should().Match(s => s.HasValue == element.Value.ToBoolean()); - break; - case "hasServerConnectionId": - AssertHasServerConnectionId(commandStartedEvent.ConnectionId, element.Value.ToBoolean()); - break; - default: - throw new FormatException($"Unexpected commandStartedEvent field: '{element.Name}'."); - } + case "command": + _valueMatcher.AssertValuesMatch(commandStartedEvent.Command, element.Value); + break; + case "commandName": + commandStartedEvent.CommandName.Should().Be(element.Value.AsString); + break; + case "databaseName": + commandStartedEvent.DatabaseNamespace.DatabaseName.Should().Be(element.Value.AsString); + break; + case "hasServiceId": + commandStartedEvent.ServiceId.Should().Match(s => s.HasValue == element.Value.ToBoolean()); + break; + case "hasServerConnectionId": + AssertHasServerConnectionId(commandStartedEvent.ConnectionId, element.Value.ToBoolean()); + break; + default: + throw new FormatException($"Unexpected commandStartedEvent field: '{element.Name}'."); } - break; - case "commandSucceededEvent": - var commandSucceededEvent = actualEvent.Should().BeOfType().Subject; - foreach (var element in expectedEventValue) + } + break; + case "commandSucceededEvent": + var commandSucceededEvent = actualEvent.Should().BeOfType().Subject; + foreach (var element in expectedEventValue) + { + switch (element.Name) { - switch (element.Name) - { - case "reply": - _valueMatcher.AssertValuesMatch(commandSucceededEvent.Reply, element.Value); - break; - case "commandName": - commandSucceededEvent.CommandName.Should().Be(element.Value.AsString); - break; - case "hasServiceId": - commandSucceededEvent.ServiceId.Should().Match(s => s.HasValue == element.Value.ToBoolean()); - break; - case "hasServerConnectionId": - AssertHasServerConnectionId(commandSucceededEvent.ConnectionId, element.Value.ToBoolean()); - break; - default: - throw new FormatException($"Unexpected commandStartedEvent field: '{element.Name}'."); - } + case "reply": + _valueMatcher.AssertValuesMatch(commandSucceededEvent.Reply, element.Value); + break; + case "commandName": + commandSucceededEvent.CommandName.Should().Be(element.Value.AsString); + break; + case "hasServiceId": + commandSucceededEvent.ServiceId.Should().Match(s => s.HasValue == element.Value.ToBoolean()); + break; + case "hasServerConnectionId": + AssertHasServerConnectionId(commandSucceededEvent.ConnectionId, element.Value.ToBoolean()); + break; + default: + throw new FormatException($"Unexpected commandStartedEvent field: '{element.Name}'."); } - break; - case "commandFailedEvent": - var commandFailedEvent = actualEvent.Should().BeOfType().Subject; + } + break; + case "commandFailedEvent": + var commandFailedEvent = actualEvent.Should().BeOfType().Subject; + foreach (var element in expectedEventValue) + { + switch (element.Name) + { + case "commandName": + commandFailedEvent.CommandName.Should().Be(element.Value.AsString); + break; + case "hasServiceId": + commandFailedEvent.ServiceId.Should().Match(s => s.HasValue == element.Value.ToBoolean()); + break; + case "hasServerConnectionId": + AssertHasServerConnectionId(commandFailedEvent.ConnectionId, element.Value.ToBoolean()); + break; + default: + throw new FormatException($"Unexpected commandStartedEvent field: '{element.Name}'."); + } + } + break; + case "connectionReadyEvent": + actualEvent.Should().BeOfType(); + expectedEventValue.ElementCount.Should().Be(0); // empty document + break; + case "connectionCheckOutStartedEvent": + actualEvent.Should().BeOfType(); + expectedEventValue.ElementCount.Should().Be(0); // empty document + break; + case "connectionCheckedOutEvent": + actualEvent.Should().BeOfType(); + expectedEventValue.ElementCount.Should().Be(0); // empty document + break; + case "connectionCheckedInEvent": + actualEvent.Should().BeOfType(); + expectedEventValue.ElementCount.Should().Be(0); // empty document + break; + case "connectionClosedEvent": + { + var connectionClosedEvent = actualEvent.Should().BeOfType().Subject; foreach (var element in expectedEventValue) { switch (element.Name) { - case "commandName": - commandFailedEvent.CommandName.Should().Be(element.Value.AsString); - break; - case "hasServiceId": - commandFailedEvent.ServiceId.Should().Match(s => s.HasValue == element.Value.ToBoolean()); - break; - case "hasServerConnectionId": - AssertHasServerConnectionId(commandFailedEvent.ConnectionId, element.Value.ToBoolean()); + case "reason": + //connectionClosedEvent.Reason.Should().Be(reason); // TODO: should be implemented in the scope of CSHARP-3219 break; default: - throw new FormatException($"Unexpected commandStartedEvent field: '{element.Name}'."); - } - } - break; - case "connectionReadyEvent": - actualEvent.Should().BeOfType(); - expectedEventValue.ElementCount.Should().Be(0); // empty document - break; - case "connectionCheckOutStartedEvent": - actualEvent.Should().BeOfType(); - expectedEventValue.ElementCount.Should().Be(0); // empty document - break; - case "connectionCheckedOutEvent": - actualEvent.Should().BeOfType(); - expectedEventValue.ElementCount.Should().Be(0); // empty document - break; - case "connectionCheckedInEvent": - actualEvent.Should().BeOfType(); - expectedEventValue.ElementCount.Should().Be(0); // empty document - break; - case "connectionClosedEvent": - { - var connectionClosedEvent = actualEvent.Should().BeOfType().Subject; - foreach (var element in expectedEventValue) - { - switch (element.Name) - { - case "reason": - //connectionClosedEvent.Reason.Should().Be(reason); // TODO: should be implemented in the scope of CSHARP-3219 - break; - default: - throw new FormatException($"Unexpected {expectedEventType} field: '{element.Name}'."); - } - } - } - break; - case "connectionCreatedEvent": - actualEvent.Should().BeOfType(); - expectedEventValue.ElementCount.Should().Be(0); // empty document - break; - case "connectionCheckOutFailedEvent": - { - var connectionCheckOutFailedEvent = actualEvent.Should().BeOfType().Subject; - foreach (var element in expectedEventValue) - { - switch (element.Name) - { - case "reason": - connectionCheckOutFailedEvent.Reason.ToString().ToLower().Should().Be(element.Value.ToString().ToLower()); - break; - default: - throw new FormatException($"Unexpected {expectedEventType} field: '{element.Name}'."); - } + throw new FormatException($"Unexpected {expectedEventType} field: '{element.Name}'."); } } - break; - case "poolClearedEvent": - var poolClearedEvent = actualEvent.Should().BeOfType().Subject; + } + break; + case "connectionCreatedEvent": + actualEvent.Should().BeOfType(); + expectedEventValue.ElementCount.Should().Be(0); // empty document + break; + case "connectionCheckOutFailedEvent": + { + var connectionCheckOutFailedEvent = actualEvent.Should().BeOfType().Subject; foreach (var element in expectedEventValue) { switch (element.Name) { - case "hasServiceId": - poolClearedEvent.ServiceId.Should().Match(s => s.HasValue == element.Value.ToBoolean()); + case "reason": + connectionCheckOutFailedEvent.Reason.ToString().ToLower().Should().Be(element.Value.ToString().ToLower()); break; default: throw new FormatException($"Unexpected {expectedEventType} field: '{element.Name}'."); } } - break; - default: - throw new FormatException($"Unrecognized event type: '{expectedEventType}'."); - } + } + break; + case "poolClearedEvent": + var poolClearedEvent = actualEvent.Should().BeOfType().Subject; + foreach (var element in expectedEventValue) + { + switch (element.Name) + { + case "hasServiceId": + poolClearedEvent.ServiceId.Should().Match(s => s.HasValue == element.Value.ToBoolean()); + break; + default: + throw new FormatException($"Unexpected {expectedEventType} field: '{element.Name}'."); + } + } + break; + case "poolReadyEvent": + actualEvent.Should().BeOfType(); + expectedEventValue.ElementCount.Should().Be(0); // empty document + break; + case "serverDescriptionChangedEvent": + var serverDescriptionChangedEvent = actualEvent.Should().BeOfType().Subject; + if (expectedEventValue.Elements.Any()) + { + throw new FormatException($"Unexpected {expectedEventType} fields."); + } + break; + + default: + throw new FormatException($"Unrecognized event type: '{expectedEventType}'."); } void AssertHasServerConnectionId(ConnectionId connectionId, bool value) @@ -250,11 +248,45 @@ void AssertHasServerConnectionId(ConnectionId connectionId, bool value) // So even though servers less than 4.2 don't provide connectionId, we still have this value through getLastError, so don't assert hasServerConnectionId=false. if (value) { - connectionId.ServerValue.Should().HaveValue(); + connectionId.LongServerValue.Should().HaveValue(); } } } + public bool DoEventsMatch(object actualEvent, BsonDocument expectedEventDocument) + { + try + { + AssertEventsMatch(actualEvent, expectedEventDocument); + return true; + } + catch + { + return false; + } + } + + // private methods + private void AssertEvents(List actualEvents, BsonArray expectedEventsDocuments, bool ignoreExtraEvents) + { + if (ignoreExtraEvents) + { + actualEvents.Count.Should().BeGreaterOrEqualTo(expectedEventsDocuments.Count); + } + else + { + actualEvents.Should().HaveSameCount(expectedEventsDocuments); + } + + for (int i = 0; i < expectedEventsDocuments.Count; i++) + { + var actualEvent = actualEvents[i]; + var expectedEventDocument = expectedEventsDocuments[i].AsBsonDocument; + + AssertEventsMatch(actualEvent, expectedEventDocument); + } + } + private string GetAssertionErrorMessage(List actualEvents, BsonArray expectedEventsDocuments) { var jsonWriterSettings = new JsonWriterSettings { Indent = true }; diff --git a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/Matchers/UnifiedLogMatcher.cs b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/Matchers/UnifiedLogMatcher.cs new file mode 100644 index 00000000000..96c7f13ab15 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/Matchers/UnifiedLogMatcher.cs @@ -0,0 +1,56 @@ +/* Copyright 2020-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.Collections.Generic; +using System.Linq; +using FluentAssertions; +using MongoDB.Bson; +using MongoDB.Driver.Core.TestHelpers.Logging; + +namespace MongoDB.Driver.Tests.UnifiedTestOperations.Matchers +{ + public sealed class UnifiedLogMatcher + { + private UnifiedValueMatcher _valueMatcher; + + public UnifiedLogMatcher(UnifiedValueMatcher valueMatcher) + { + _valueMatcher = valueMatcher; + } + + public void AssertLogsMatch(LogEntry[] actualLogs, BsonArray expectedLogs) + { + actualLogs.Length.Should().Be(expectedLogs.Count); + + for (int i = 0; i < expectedLogs.Count; i++) + { + var expectedLogDocument = expectedLogs[i].AsBsonDocument; + var actualLog = actualLogs[i]; + + var expectedCategory = UnifiedLogHelper.ParseCategory(expectedLogDocument["component"].AsString); + var expectedLogLevel = UnifiedLogHelper.ParseLogLevel(expectedLogDocument["level"].AsString); + + actualLog.Category.Should().Be(expectedCategory); + actualLog.LogLevel.Should().Be(expectedLogLevel); + + var actualLogDocument = new BsonDocument(actualLog.State.Select(ToBsonElement)); + _valueMatcher.AssertValuesMatch(actualLogDocument, expectedLogDocument["data"]); + } + } + + private static BsonElement ToBsonElement(KeyValuePair pair) => + new(MongoUtils.ToCamelCase(pair.Key), BsonValue.Create(pair.Value)); + } +} diff --git a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/Matchers/UnifiedValueMatcher.cs b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/Matchers/UnifiedValueMatcher.cs index 420960d4934..7c033913638 100644 --- a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/Matchers/UnifiedValueMatcher.cs +++ b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/Matchers/UnifiedValueMatcher.cs @@ -19,7 +19,7 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.IO; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver; using Xunit.Sdk; @@ -69,6 +69,7 @@ private void AssertValuesMatch(BsonValue actual, BsonValue expected, bool isRoot AssertExpectedType(actual, operatorValue); break; case "$$matchesHexBytes": + case "$$matchAsRoot": AssertValuesMatch(actual, operatorValue, true); break; case "$$unsetOrMatches": @@ -119,6 +120,10 @@ private void AssertValuesMatch(BsonValue actual, BsonValue expected, bool isRoot actualDocument.Names.Should().Contain(expectedName); AssertExpectedType(actualDocument[expectedName], operatorValue); continue; + case "$$matchAsDocument": + var parsedDocument = BsonDocument.Parse(actualDocument[expectedName].AsString); + AssertValuesMatch(parsedDocument, operatorValue, false); + continue; case "$$matchesEntity": var resultId = operatorValue.AsString; expectedValue = _entityMap.GetResult(resultId); diff --git a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedCloseClientOperation.cs b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedCloseClientOperation.cs new file mode 100644 index 00000000000..95ed42c02f2 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedCloseClientOperation.cs @@ -0,0 +1,71 @@ +/* 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.Threading; +using System.Threading.Tasks; +using MongoDB.Bson; +using MongoDB.Driver.TestHelpers; + +namespace MongoDB.Driver.Tests.UnifiedTestOperations +{ + public class UnifiedCloseClientOperation : IUnifiedEntityTestOperation + { + private readonly DisposableMongoClient _client; + + public UnifiedCloseClientOperation(DisposableMongoClient client) + { + _client = client; + } + + public OperationResult Execute(CancellationToken cancellationToken) + { + try + { + _client.Dispose(); + return OperationResult.Empty(); + } + catch (Exception exception) + { + return OperationResult.FromException(exception); + } + } + + public Task ExecuteAsync(CancellationToken cancellationToken) => + Task.FromResult(Execute(cancellationToken)); + } + + public class UnifiedCloseClientOperationBuilder + { + private readonly UnifiedEntityMap _entityMap; + + public UnifiedCloseClientOperationBuilder(UnifiedEntityMap entityMap) + { + _entityMap = entityMap; + } + + public UnifiedCloseClientOperation Build(string targetClientId, BsonDocument arguments) + { + if (arguments?.ElementCount > 0) + { + throw new FormatException($"{nameof(UnifiedCloseClientOperationBuilder)} does not expected any arguments."); + } + + var client = _entityMap.GetClient(targetClientId); + + return new(client); + } + } +} diff --git a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedCreateEntitiesOperation.cs b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedCreateEntitiesOperation.cs new file mode 100644 index 00000000000..671a8f3685b --- /dev/null +++ b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedCreateEntitiesOperation.cs @@ -0,0 +1,74 @@ +/* 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.Threading; +using System.Threading.Tasks; +using MongoDB.Bson; + +namespace MongoDB.Driver.Tests.UnifiedTestOperations +{ + public sealed class UnifiedCreateEntitiesOperation : IUnifiedEntityTestOperation + { + private readonly BsonArray _entitiesArray; + private readonly UnifiedEntityMap _entityMap; + + public UnifiedCreateEntitiesOperation(BsonArray entitiesArray, UnifiedEntityMap entityMap) + { + _entitiesArray = entitiesArray; + _entityMap = entityMap; + } + + public OperationResult Execute(CancellationToken cancellationToken) + { + try + { + var newEntityMap = new UnifiedEntityMapBuilder(null, _entityMap.LoggingSettings).Build(_entitiesArray); + _entityMap.AddEntities(newEntityMap); + + return OperationResult.Empty(); + } + catch (Exception exception) + { + return OperationResult.FromException(exception); + } + } + + public Task ExecuteAsync(CancellationToken cancellationToken) => + Task.FromResult(Execute(cancellationToken)); + } + + public class UnifiedCreateEntitiesOperationBuilder + { + private readonly UnifiedEntityMap _entityMap; + + public UnifiedCreateEntitiesOperationBuilder(UnifiedEntityMap entityMap) + { + _entityMap = entityMap; + } + + public UnifiedCreateEntitiesOperation Build(BsonDocument arguments) + { + if (arguments.ElementCount > 1) + { + throw new FormatException($"{nameof(UnifiedCreateEntitiesOperation)} does not expected any arguments except 'entities'."); + } + + var entities = arguments["entities"].AsBsonArray; + + return new(entities, _entityMap); + } + } +} diff --git a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedEntityMap.cs b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedEntityMap.cs index a721d728321..0317abbbc85 100644 --- a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedEntityMap.cs +++ b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedEntityMap.cs @@ -17,11 +17,12 @@ using System.Collections; using System.Collections.Generic; using System.Linq; +using Microsoft.Extensions.Logging; using MongoDB.Bson; using MongoDB.Driver.Core; +using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.Events; using MongoDB.Driver.Core.Misc; -using MongoDB.Driver.Core.TestHelpers.Logging; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; using MongoDB.Driver.Encryption; using MongoDB.Driver.GridFS; @@ -38,6 +39,7 @@ public sealed class UnifiedEntityMap : IDisposable private readonly Dictionary _clients; private readonly Dictionary _clientEncryptions; private readonly Dictionary _clientEventCapturers; + private readonly Dictionary> _loggingComponents; private readonly Dictionary> _collections; private readonly Dictionary> _cursors; private readonly Dictionary _databases; @@ -49,6 +51,7 @@ public sealed class UnifiedEntityMap : IDisposable private readonly Dictionary _sessions; private readonly Dictionary _sessionIds; private readonly Dictionary _successCounts; + private readonly LoggingSettings _loggingSettings; // public constructors public UnifiedEntityMap( @@ -57,6 +60,7 @@ public UnifiedEntityMap( Dictionary clients, Dictionary clientEncryptions, Dictionary clientEventCapturers, + Dictionary> loggingComponents, Dictionary> collections, Dictionary> cursors, Dictionary databases, @@ -66,13 +70,15 @@ public UnifiedEntityMap( Dictionary results, Dictionary sessions, Dictionary sessionIds, - Dictionary successCounts) + Dictionary successCounts, + LoggingSettings loggingSettings) { _buckets = buckets; _changeStreams = changeStreams; _clients = clients; _clientEncryptions = clientEncryptions; _clientEventCapturers = clientEventCapturers; + _loggingComponents = loggingComponents; _collections = collections; _cursors = cursors; _databases = databases; @@ -83,6 +89,7 @@ public UnifiedEntityMap( _sessions = sessions; _sessionIds = sessionIds; _successCounts = successCounts; + _loggingSettings = loggingSettings; } // public properties @@ -121,6 +128,7 @@ public Dictionary ErrorDocuments return _errorDocuments; } } + public Dictionary EventCapturers { get @@ -130,6 +138,15 @@ public Dictionary EventCapturers } } + public Dictionary> LoggingComponents + { + get + { + ThrowIfDisposed(); + return _loggingComponents; + } + } + public Dictionary FailureDocuments { get @@ -148,6 +165,15 @@ public Dictionary IterationCounts } } + public LoggingSettings LoggingSettings + { + get + { + ThrowIfDisposed(); + return _loggingSettings; + } + } + public Dictionary SuccessCounts { get @@ -186,7 +212,7 @@ public IGridFSBucket GetBucket(string bucketId) return _buckets[bucketId]; } - public IMongoClient GetClient(string clientId) + public DisposableMongoClient GetClient(string clientId) { ThrowIfDisposed(); return _clients[clientId]; @@ -260,17 +286,32 @@ private void ThrowIfDisposed() throw new ObjectDisposedException(nameof(UnifiedEntityMap)); } } + + public void AddEntities(UnifiedEntityMap unifiedEntityMap) + { + _buckets.AddRange(unifiedEntityMap._buckets); + _changeStreams.AddRange(unifiedEntityMap._changeStreams); + _clients.AddRange(unifiedEntityMap._clients); + _clientEncryptions.AddRange(unifiedEntityMap._clientEncryptions); + _clientEventCapturers.AddRange(unifiedEntityMap._clientEventCapturers); + _collections.AddRange(unifiedEntityMap._collections); + _cursors.AddRange(unifiedEntityMap._cursors); + _databases.AddRange(unifiedEntityMap._databases); + _loggingComponents.AddRange(unifiedEntityMap._loggingComponents); + _sessions.AddRange(unifiedEntityMap._sessions); + _sessionIds.AddRange(unifiedEntityMap._sessionIds); + } } public class UnifiedEntityMapBuilder { private readonly Dictionary _eventFormatters; - private readonly ILoggerFactory _loggerFactory; + private readonly LoggingSettings _loggingSettings; - public UnifiedEntityMapBuilder(Dictionary eventFormatters, ILoggerFactory loggerFactory) + public UnifiedEntityMapBuilder(Dictionary eventFormatters, LoggingSettings loggingSettings) { _eventFormatters = eventFormatters ?? new(); - _loggerFactory = loggerFactory; + _loggingSettings = loggingSettings; } public UnifiedEntityMap Build(BsonArray entitiesArray) @@ -278,6 +319,7 @@ public UnifiedEntityMap Build(BsonArray entitiesArray) var buckets = new Dictionary(); var changeStreams = new Dictionary>>(); var clientEventCapturers = new Dictionary(); + var loggingComponents = new Dictionary>(); var clients = new Dictionary(); var clientEncryptions = new Dictionary(); var collections = new Dictionary>(); @@ -312,12 +354,14 @@ public UnifiedEntityMap Build(BsonArray entitiesArray) break; case "client": EnsureIsNotHandled(clients, id); - var (client, eventCapturers) = CreateClient(entity); + var (client, eventCapturers, clientLoggingComponents) = CreateClient(entity); clients.Add(id, client); foreach (var createdEventCapturer in eventCapturers) { clientEventCapturers.Add(createdEventCapturer.Key, createdEventCapturer.Value); } + + loggingComponents.Add(id, clientLoggingComponents); break; case "clientEncryption": { @@ -355,6 +399,7 @@ public UnifiedEntityMap Build(BsonArray entitiesArray) clients, clientEncryptions, clientEventCapturers, + loggingComponents, collections, cursors, databases, @@ -364,7 +409,8 @@ public UnifiedEntityMap Build(BsonArray entitiesArray) results, sessions, sessionIds, - successCounts); + successCounts, + _loggingSettings); void EnsureIsNotHandled(Dictionary dictionary, string key) { @@ -399,20 +445,27 @@ private IGridFSBucket CreateBucket(BsonDocument entity, Dictionary ClientEventCapturers) CreateClient(BsonDocument entity) + private (DisposableMongoClient Client, Dictionary ClientEventCapturers, Dictionary LoggingComponents) + CreateClient(BsonDocument entity) { string appName = null; var clientEventCapturers = new Dictionary(); + Dictionary loggingComponents = null; string clientId = null; var commandNamesToSkipInEvents = new List(); List<(string Key, IEnumerable Events, List CommandNotToCapture)> eventTypesToCapture = new (); + int? heartbeatFrequencyMS = null; bool? loadBalanced = null; + int? maxConnecting = null; + TimeSpan? maxIdleTime = null; int? maxPoolSize = null; + int? minPoolSize = null; bool? observeSensitiveCommands = null; var readConcern = ReadConcern.Default; var retryReads = true; var retryWrites = true; var useMultipleShardRouters = false; + int? waitQueueSize = null; TimeSpan? waitQueueTimeout = null; var writeConcern = WriteConcern.Acknowledged; var serverApi = CoreTestConfiguration.ServerApi; @@ -432,9 +485,21 @@ private IGridFSBucket CreateBucket(BsonDocument entity, Dictionary UnifiedLogHelper.ParseCategory(pair.Name), + pair => UnifiedLogHelper.ParseLogLevel(pair.Value.AsString)); + break; case "observeEvents": var observeEvents = element.Value.AsBsonArray.Select(x => x.AsString); eventTypesToCapture.Add( @@ -568,13 +642,19 @@ private IGridFSBucket CreateBucket(BsonDocument entity, Dictionary 0) { @@ -587,10 +667,10 @@ private IGridFSBucket CreateBucket(BsonDocument entity, Dictionary(), + _loggingSettings, useMultipleShardRouters); - return (client, clientEventCapturers); + return (client, clientEventCapturers, loggingComponents); } private ClientEncryption CreateClientEncryption(Dictionary clients, BsonDocument entity) @@ -781,6 +861,9 @@ private EventCapturer CreateEventCapturer(IEnumerable eventTypesToCaptur case "poolreadyevent": // should be handled in the scope of CSHARP-3509 break; + case "serverdescriptionchangedevent": + eventCapturer = eventCapturer.Capture(); + break; default: throw new FormatException($"Invalid event name: {eventTypeToCapture}."); } diff --git a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedListCollectionNamesOperation.cs b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedListCollectionNamesOperation.cs new file mode 100644 index 00000000000..d124f3f9174 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedListCollectionNamesOperation.cs @@ -0,0 +1,114 @@ +/* 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.Threading; +using System.Threading.Tasks; +using MongoDB.Bson; +using MongoDB.Driver.Core.Misc; + +namespace MongoDB.Driver.Tests.UnifiedTestOperations +{ + public class UnifiedListCollectionNamesOperation : IUnifiedEntityTestOperation + { + private readonly IMongoDatabase _database; + private readonly ListCollectionNamesOptions _options; + private readonly IClientSessionHandle _session; + + public UnifiedListCollectionNamesOperation( + IMongoDatabase database, + ListCollectionNamesOptions options, + IClientSessionHandle session) + { + _database = Ensure.IsNotNull(database, nameof(database)); + _options = options; // can be null + _session = session; + } + + public OperationResult Execute(CancellationToken cancellationToken) + { + try + { + using var cursor = _session == null + ? _database.ListCollectionNames(_options, cancellationToken) + : _database.ListCollectionNames(_session, _options, cancellationToken); + + var collections = cursor.ToList(cancellationToken); + + return OperationResult.FromResult(new BsonArray(collections)); + } + catch (Exception ex) + { + return OperationResult.FromException(ex); + } + } + + public async Task ExecuteAsync(CancellationToken cancellationToken) + { + try + { + using var cursor = _session == null + ? await _database.ListCollectionNamesAsync(_options, cancellationToken) + : await _database.ListCollectionNamesAsync(_session, _options, cancellationToken); + + var collections = await cursor.ToListAsync(cancellationToken); + + return OperationResult.FromResult(new BsonArray(collections)); + } + catch (Exception ex) + { + return OperationResult.FromException(ex); + } + } + } + + public class UnifiedListCollectionNamesOperationBuilder + { + private readonly UnifiedEntityMap _entityMap; + + public UnifiedListCollectionNamesOperationBuilder(UnifiedEntityMap entityMap) + { + _entityMap = entityMap; + } + + public UnifiedListCollectionNamesOperation Build(string targetDatabaseId, BsonDocument arguments) + { + var database = _entityMap.GetDatabase(targetDatabaseId); + + var listCollectionsOptions = new ListCollectionNamesOptions(); + IClientSessionHandle session = null; + + if (arguments != null) + { + foreach (var argument in arguments) + { + switch (argument.Name) + { + case "filter": + listCollectionsOptions.Filter = argument.Value.AsBsonDocument; + break; + case "session": + session = _entityMap.GetSession(argument.Value.AsString); + break; + default: + throw new FormatException($"Invalid {nameof(UnifiedListCollectionNamesOperation)} argument name: '{argument.Name}'."); + } + } + } + + return new UnifiedListCollectionNamesOperation(database, listCollectionsOptions, session); + } + } +} diff --git a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedListCollectionsOperation.cs b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedListCollectionsOperation.cs index 28101f7f7ff..d02fc3e5b57 100644 --- a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedListCollectionsOperation.cs +++ b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedListCollectionsOperation.cs @@ -106,7 +106,7 @@ public UnifiedListCollectionsOperation Build(string targetDatabaseId, BsonDocume session = _entityMap.GetSession(argument.Value.AsString); break; default: - throw new FormatException($"Invalid AssertIndexNotExistsOperation argument name: '{argument.Name}'."); + throw new FormatException($"Invalid {nameof(UnifiedListCollectionsOperation)} argument name: '{argument.Name}'."); } } } diff --git a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedListDatabaseNamesOperation.cs b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedListDatabaseNamesOperation.cs new file mode 100644 index 00000000000..42e05d88e63 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedListDatabaseNamesOperation.cs @@ -0,0 +1,113 @@ +/* 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.Threading; +using System.Threading.Tasks; +using MongoDB.Bson; + +namespace MongoDB.Driver.Tests.UnifiedTestOperations +{ + public class UnifiedListDatabaseNamesOperation : IUnifiedEntityTestOperation + { + private readonly IMongoClient _client; + private readonly ListDatabaseNamesOptions _options = null; + private readonly IClientSessionHandle _session = null; + + public UnifiedListDatabaseNamesOperation( + IMongoClient client, + ListDatabaseNamesOptions options, + IClientSessionHandle session) + { + _client = client; + _options = options; + _session = session; + } + + public OperationResult Execute(CancellationToken cancellationToken) + { + try + { + using var cursor = _session == null + ? _client.ListDatabaseNames(_options, cancellationToken) + : _client.ListDatabaseNames(_session, _options, cancellationToken); + + var result = cursor.ToList(cancellationToken); + + return OperationResult.FromResult(new BsonArray(result)); + } + catch (Exception exception) + { + return OperationResult.FromException(exception); + } + } + + public async Task ExecuteAsync(CancellationToken cancellationToken) + { + try + { + using var cursor = _session == null + ? await _client.ListDatabaseNamesAsync(_options, cancellationToken) + : await _client.ListDatabaseNamesAsync(_session, _options, cancellationToken); + + var result = await cursor.ToListAsync(cancellationToken); + + return OperationResult.FromResult(new BsonArray(result)); + } + catch (Exception exception) + { + return OperationResult.FromException(exception); + } + } + } + + public class UnifiedListDatabaseNamesOperationBuilder + { + private readonly UnifiedEntityMap _entityMap; + + public UnifiedListDatabaseNamesOperationBuilder(UnifiedEntityMap entityMap) + { + _entityMap = entityMap; + } + + public UnifiedListDatabaseNamesOperation Build(string targetClientId, BsonDocument arguments) + { + var client = _entityMap.GetClient(targetClientId); + ListDatabaseNamesOptions options = null; + IClientSessionHandle session = null; + + if (arguments != null) + { + foreach (var argument in arguments) + { + switch (argument.Name) + { + case "filter": + options ??= new ListDatabaseNamesOptions(); + options.Filter = new BsonDocumentFilterDefinition(argument.Value.AsBsonDocument); + break; + case "session": + session = _entityMap.GetSession(argument.Value.AsString); + break; + default: + throw new FormatException($"Invalid ListDatabasesOperation argument name: '{argument.Name}'."); + } + } + } + + return new UnifiedListDatabaseNamesOperation(client, options, session); + } + } +} diff --git a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedListDatabasesOperation.cs b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedListDatabasesOperation.cs index 7fe7f6b0b47..61179089e90 100644 --- a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedListDatabasesOperation.cs +++ b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedListDatabasesOperation.cs @@ -23,13 +23,16 @@ namespace MongoDB.Driver.Tests.UnifiedTestOperations public class UnifiedListDatabasesOperation : IUnifiedEntityTestOperation { private readonly IMongoClient _client; + private readonly ListDatabasesOptions _options = null; private readonly IClientSessionHandle _session = null; public UnifiedListDatabasesOperation( IMongoClient client, + ListDatabasesOptions options, IClientSessionHandle session) { _client = client; + _options = options; _session = session; } @@ -38,8 +41,8 @@ public OperationResult Execute(CancellationToken cancellationToken) try { using var cursor = _session == null - ? _client.ListDatabases(cancellationToken) - : _client.ListDatabases(_session, cancellationToken); + ? _client.ListDatabases(_options, cancellationToken) + : _client.ListDatabases(_session, _options, cancellationToken); var result = cursor.ToList(cancellationToken); @@ -56,8 +59,8 @@ public async Task ExecuteAsync(CancellationToken cancellationTo try { using var cursor = _session == null - ? await _client.ListDatabasesAsync(cancellationToken) - : await _client.ListDatabasesAsync(_session, cancellationToken); + ? await _client.ListDatabasesAsync(_options, cancellationToken) + : await _client.ListDatabasesAsync(_session, _options, cancellationToken); var result = await cursor.ToListAsync(cancellationToken); @@ -82,6 +85,7 @@ public UnifiedListDatabasesOperationBuilder(UnifiedEntityMap entityMap) public UnifiedListDatabasesOperation Build(string targetClientId, BsonDocument arguments) { var client = _entityMap.GetClient(targetClientId); + ListDatabasesOptions options = null; IClientSessionHandle session = null; if (arguments != null) @@ -90,6 +94,10 @@ public UnifiedListDatabasesOperation Build(string targetClientId, BsonDocument a { switch (argument.Name) { + case "filter": + options ??= new ListDatabasesOptions(); + options.Filter = new BsonDocumentFilterDefinition(argument.Value.AsBsonDocument); + break; case "session": session = _entityMap.GetSession(argument.Value.AsString); break; @@ -99,7 +107,7 @@ public UnifiedListDatabasesOperation Build(string targetClientId, BsonDocument a } } - return new UnifiedListDatabasesOperation(client, session); + return new UnifiedListDatabasesOperation(client, options, session); } } } diff --git a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedLogHelper.cs b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedLogHelper.cs new file mode 100644 index 00000000000..e6c45bdaeee --- /dev/null +++ b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedLogHelper.cs @@ -0,0 +1,67 @@ +/* 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 Microsoft.Extensions.Logging; +using MongoDB.Driver.Core.Logging; +using MongoDB.Driver.Core.TestHelpers.Logging; + +namespace MongoDB.Driver.Tests.UnifiedTestOperations +{ + public static class UnifiedLogHelper + { + public static LogEntry[] FilterLogs( + LogEntry[] logs, + string clientId, + int clusterId, + Dictionary> componentsVerbosity, + Predicate filter = null) + { + var clientConfiguration = componentsVerbosity[clientId]; + + var result = logs.Where(l => + filter?.Invoke(l) != false && + l.ClusterId == clusterId && + clientConfiguration.TryGetValue(l.Category, out var logLevel) && + l.LogLevel >= logLevel) + .ToArray(); + + return result; + } + + public static string ParseCategory(string category) => + category switch + { + "command" => LogCategoryHelper.GetCategoryName(), + "connection" => LogCategoryHelper.GetCategoryName(), + "sdam" => LogCategoryHelper.GetCategoryName(), + "serverSelection" => LogCategoryHelper.GetCategoryName(), + _ => throw new ArgumentOutOfRangeException(nameof(category), category) + }; + + public static LogLevel ParseLogLevel(string logLevel) => + logLevel switch + { + "error" => LogLevel.Error, + "warning" => LogLevel.Warning, + "informational" or "notice" => LogLevel.Information, + "debug" => LogLevel.Debug, + "trace" => LogLevel.Trace, + _ => throw new ArgumentOutOfRangeException(nameof(logLevel), logLevel) + }; + } +} diff --git a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedLoopOperation.cs b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedLoopOperation.cs index 417b4bf3b65..78daf3870fb 100644 --- a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedLoopOperation.cs +++ b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedLoopOperation.cs @@ -19,7 +19,7 @@ using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Misc; namespace MongoDB.Driver.Tests.UnifiedTestOperations diff --git a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestOperationFactory.cs b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestOperationFactory.cs index 38bfd9d8f57..a3025bf260e 100644 --- a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestOperationFactory.cs +++ b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestOperationFactory.cs @@ -47,9 +47,11 @@ public IUnifiedTestOperation CreateOperation(string operationName, string target "assertSessionPinned" => new UnifiedAssertSessionPinnedOperationBuilder(_entityMap).Build(operationArguments), "assertSessionTransactionState" => new UnifiedAssertSessionTransactionStateOperationBuilder(_entityMap).Build(operationArguments), "assertSessionUnpinned" => new UnifiedAssertSessionUnpinnedOperationBuilder(_entityMap).Build(operationArguments), + "createEntities" => new UnifiedCreateEntitiesOperationBuilder(_entityMap).Build(operationArguments), "failPoint" => new UnifiedFailPointOperationBuilder(_entityMap).Build(operationArguments), "loop" => new UnifiedLoopOperationBuilder(_entityMap, _additionalArgs).Build(operationArguments), "targetedFailPoint" => new UnifiedTargetedFailPointOperationBuilder(_entityMap).Build(operationArguments), + "waitForEvent" => new UnifiedWaitForEventOperationBuilder(_entityMap).Build(operationArguments), _ => throw new FormatException($"Invalid method name: '{operationName}'."), }, _ when _entityMap.HasBucket(targetEntityId) => operationName switch @@ -67,8 +69,10 @@ public IUnifiedTestOperation CreateOperation(string operationName, string target }, _ when _entityMap.HasClient(targetEntityId) => operationName switch { + "close" => new UnifiedCloseClientOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), "createChangeStream" => new UnifiedCreateChangeStreamOnClientOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), "listDatabases" => new UnifiedListDatabasesOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), + "listDatabaseNames" => new UnifiedListDatabaseNamesOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), _ => throw new FormatException($"Invalid method name: '{operationName}'."), }, _ when _entityMap.HasCollection(targetEntityId) => operationName switch @@ -104,6 +108,7 @@ public IUnifiedTestOperation CreateOperation(string operationName, string target "createChangeStream" => new UnifiedCreateChangeStreamOnDatabaseOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), "dropCollection" => new UnifiedDropCollectionOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), "listCollections" => new UnifiedListCollectionsOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), + "listCollectionNames" => new UnifiedListCollectionNamesOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), "runCommand" => new UnifiedRunCommandOperationBuilder(_entityMap).Build(targetEntityId, operationArguments), _ => throw new FormatException($"Invalid method name: '{operationName}'."), }, diff --git a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestRunner.cs b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestRunner.cs index 2a204a0d24f..92ec76e001d 100644 --- a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestRunner.cs @@ -18,16 +18,18 @@ using System.Linq; using System.Threading; using FluentAssertions; +using Microsoft.Extensions.Logging; using MongoDB.Bson; using MongoDB.Bson.TestHelpers.JsonDrivenTests; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core; +using MongoDB.Driver.Core.Logging; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers; using MongoDB.Driver.Core.TestHelpers.Logging; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; using MongoDB.Driver.Tests.UnifiedTestOperations.Matchers; -using Xunit; +using Xunit.Sdk; namespace MongoDB.Driver.Tests.UnifiedTestOperations { @@ -38,18 +40,21 @@ public sealed class UnifiedTestRunner : IDisposable private readonly Dictionary _additionalArgs; private readonly Dictionary _eventFormatters; private bool _runHasBeenCalled; - private ILoggerFactory _loggerFactory; private readonly ILogger _logger; + private readonly Predicate _loggingFilter; + private readonly ILoggingService _loggingService; public UnifiedTestRunner( + ILoggingService loggingService, Dictionary additionalArgs = null, Dictionary eventFormatters = null, - ILoggerFactory loggerFactory = null) + Predicate loggingFilter = null) { _additionalArgs = additionalArgs; // can be null _eventFormatters = eventFormatters; // can be null - _loggerFactory = loggerFactory ?? EmptyLoggerFactory.Instance; - _logger = _loggerFactory.CreateLogger(); + _loggingFilter = loggingFilter; // can be null + _loggingService = Ensure.IsNotNull(loggingService, nameof(loggingService)); + _logger = loggingService.LoggingSettings.CreateLogger(); } // public properties @@ -57,7 +62,7 @@ public UnifiedTestRunner( public void Run(JsonDrivenTestCase testCase) { - _logger.Debug("Running {0}", testCase.Name); + _logger.LogDebug("Running {0}", testCase.Name); // Top-level fields var schemaVersion = testCase.Shared["schemaVersion"].AsString; // cannot be null @@ -69,10 +74,11 @@ public void Run(JsonDrivenTestCase testCase) var skipReason = testCase.Test.GetValue("skipReason", null)?.AsString; var operations = testCase.Test["operations"].AsBsonArray; // cannot be null var expectEvents = testCase.Test.GetValue("expectEvents", null)?.AsBsonArray; + var expectedLogs = testCase.Test.GetValue("expectLogMessages", null)?.AsBsonArray; var outcome = testCase.Test.GetValue("outcome", null)?.AsBsonArray; var async = testCase.Test["async"].AsBoolean; // cannot be null - Run(schemaVersion, testSetRunOnRequirements, entities, initialData, runOnRequirements, skipReason, operations, expectEvents, outcome, async); + Run(schemaVersion, testSetRunOnRequirements, entities, initialData, runOnRequirements, skipReason, operations, expectEvents, expectedLogs, outcome, async); } public void Run( @@ -84,6 +90,7 @@ public void Run( string skipReason, BsonArray operations, BsonArray expectedEvents, + BsonArray expectedLogs, BsonArray outcome, bool async) { @@ -95,7 +102,7 @@ public void Run( var schemaSemanticVersion = SemanticVersion.Parse(schemaVersion); if (schemaSemanticVersion < new SemanticVersion(1, 0, 0) || - schemaSemanticVersion > new SemanticVersion(1, 8, 0)) + schemaSemanticVersion > new SemanticVersion(1, 13, 0)) { throw new FormatException($"Schema version '{schemaVersion}' is not supported."); } @@ -114,7 +121,7 @@ public void Run( KillOpenTransactions(DriverTestConfiguration.Client); - _entityMap = new UnifiedEntityMapBuilder(_eventFormatters, _loggerFactory).Build(entities); + _entityMap = new UnifiedEntityMapBuilder(_eventFormatters, _loggingService.LoggingSettings).Build(entities); if (initialData != null) { @@ -131,6 +138,10 @@ public void Run( { AssertEvents(expectedEvents, _entityMap); } + if (expectedLogs != null) + { + AssertLogs(expectedLogs, _entityMap); + } if (outcome != null) { AssertOutcome(DriverTestConfiguration.Client, outcome); @@ -139,7 +150,7 @@ public void Run( public void Dispose() { - _logger.Debug("Disposing"); + _logger.LogDebug("Disposing"); if (_failPoints != null) { @@ -157,11 +168,11 @@ public void Dispose() // Ignored because Dispose shouldn't fail } - _logger.Debug("Disposing entity map"); + _logger.LogDebug("Disposing entity map"); _entityMap?.Dispose(); - _logger.Debug("Disposed"); + _logger.LogDebug("Disposed"); } // private methods @@ -186,7 +197,7 @@ private void AddInitialData(IMongoClient client, BsonArray initialData) .GetCollection(collectionName, mongoCollectionSettings) .WithWriteConcern(WriteConcern.WMajority); - _logger.Debug("Dropping {0}", collectionName); + _logger.LogDebug("Dropping {0}", collectionName); database.DropCollection(collectionName); if (documents.Any()) @@ -202,7 +213,7 @@ private void AddInitialData(IMongoClient client, BsonArray initialData) private void AssertEvents(BsonArray eventItems, UnifiedEntityMap entityMap) { - _logger.Debug("Asserting events"); + _logger.LogDebug("Asserting events"); var unifiedEventMatcher = new UnifiedEventMatcher(new UnifiedValueMatcher(entityMap)); foreach (var eventItem in eventItems.Cast()) @@ -217,6 +228,30 @@ private void AssertEvents(BsonArray eventItems, UnifiedEntityMap entityMap) } } + private void AssertLogs(BsonArray expectedLogs, UnifiedEntityMap entityMap) + { + _logger?.LogDebug("Asserting logs"); + + var actualLogs = _loggingService.Logs; + + var unifiedLogMatcher = new UnifiedLogMatcher(new UnifiedValueMatcher(entityMap)); + foreach (var logItem in expectedLogs.Cast()) + { + var clientId = logItem["client"].AsString; + var clusterId = entityMap.GetClient(clientId).Cluster.ClusterId.Value; + var logs = logItem.GetValue("messages", false).AsBsonArray; + + var actualLogsFiltered = UnifiedLogHelper.FilterLogs( + actualLogs, + clientId, + clusterId, + entityMap.LoggingComponents, + _loggingFilter); + + unifiedLogMatcher.AssertLogsMatch(actualLogsFiltered, logs); + } + } + private void CreateAndRunOperation(BsonDocument operationDocument, bool async, CancellationToken cancellationToken) { var operation = CreateOperation(operationDocument, _entityMap); @@ -253,7 +288,7 @@ private void CreateAndRunOperation(BsonDocument operationDocument, bool async, C private void AssertOutcome(IMongoClient client, BsonArray outcome) { - _logger.Debug("Asserting outcome"); + _logger.LogDebug("Asserting outcome"); foreach (var outcomeItem in outcome) { @@ -329,8 +364,7 @@ private IUnifiedTestOperation CreateOperation(BsonDocument operation, UnifiedEnt var operationTarget = operation["object"].AsString; var operationArguments = operation.GetValue("arguments", null)?.AsBsonDocument; - - _logger.Debug("Created {0} operation", operationName); + _logger.LogDebug("Created {0} operation", operationName); return factory.CreateOperation(operationName, operationTarget, operationArguments); } diff --git a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedWaitForEventOperation.cs b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedWaitForEventOperation.cs new file mode 100644 index 00000000000..bbe10ce204c --- /dev/null +++ b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedWaitForEventOperation.cs @@ -0,0 +1,84 @@ +/* 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 MongoDB.Bson; +using MongoDB.Driver.Core; +using MongoDB.Driver.Tests.UnifiedTestOperations.Matchers; + +namespace MongoDB.Driver.Tests.UnifiedTestOperations +{ + public class UnifiedWaitForEventOperation : IUnifiedSpecialTestOperation + { + private readonly int _count; + private readonly BsonDocument _eventDocument; + private readonly EventCapturer _eventCapturer; + private readonly UnifiedEventMatcher _unifiedEventMatcher; + + public UnifiedWaitForEventOperation( + UnifiedEventMatcher unifiedEventMatcher, + EventCapturer eventCapturer, + BsonDocument eventDocument, + int count) + { + _count = count; + _eventCapturer = eventCapturer; + _eventDocument = eventDocument; + _unifiedEventMatcher = unifiedEventMatcher; + } + + public void Execute() + { + _eventCapturer.WaitForOrThrowIfTimeout( + events => events.Where(DoEventsMatch).Take(_count).Count() == _count, + TimeSpan.FromSeconds(10), + timeout => + { + var triggeredEventsCount = _eventCapturer.Events.Count(DoEventsMatch); + return $"Waiting for {_count} {_eventDocument} exceeded the timeout {timeout}. The number of triggered events is {triggeredEventsCount}."; + }); + + bool DoEventsMatch(object @event) => _unifiedEventMatcher.DoEventsMatch(@event, _eventDocument); + } + } + + public class UnifiedWaitForEventOperationBuilder + { + private readonly UnifiedEntityMap _entityMap; + + public UnifiedWaitForEventOperationBuilder(UnifiedEntityMap entityMap) + { + _entityMap = entityMap; + } + + public UnifiedWaitForEventOperation Build(BsonDocument arguments) + { + var clientId = arguments["client"].AsString; + var eventObject = arguments["event"].AsBsonDocument; + var count = arguments["count"].AsInt32; + + if (arguments.ElementCount != 3) + { + throw new FormatException($"Invalid {nameof(UnifiedWaitForEventOperation)} arguments count."); + } + + var eventCapturer = _entityMap.EventCapturers[clientId]; + var unifiedEventMatcher = new UnifiedEventMatcher(new UnifiedValueMatcher(_entityMap)); + + return new UnifiedWaitForEventOperation(unifiedEventMatcher, eventCapturer, eventObject, count); + } + } +} diff --git a/tests/MongoDB.Driver.Tests/UpdateDefinitionBuilderTests.cs b/tests/MongoDB.Driver.Tests/UpdateDefinitionBuilderTests.cs index 50b2ac9eb8c..24d9773465d 100644 --- a/tests/MongoDB.Driver.Tests/UpdateDefinitionBuilderTests.cs +++ b/tests/MongoDB.Driver.Tests/UpdateDefinitionBuilderTests.cs @@ -20,11 +20,14 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Attributes; -using MongoDB.Bson.TestHelpers.XunitExtensions; +using MongoDB.Bson.TestHelpers; +using MongoDB.Driver.Linq; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.Tests { + [Collection(RegisterObjectSerializerFixture.CollectionName)] public class UpdateDefinitionBuilderTests { [Fact] @@ -242,25 +245,36 @@ public void Inc_Typed() public void Incorrect_index_should_throw_expected_exception_with_set() { var subject = CreateSubject(); - string expectedErrorMessage = "Array indexes must be greater than or equal to -1."; + string expectedErrorMessage = "because negative indexes are not valid"; #pragma warning disable 251 - AssertThrow(subject.Set(x => x.FavoriteColors[-2], "yellow"), expectedErrorMessage); + AssertThrow(subject.Set(x => x.FavoriteColors[-2], "yellow"), expectedErrorMessage, LinqProvider.V3); #pragma warning restore - AssertThrow(subject.Set(x => x.Pets[-2].Name, "Fluffencutters"), expectedErrorMessage); - AssertThrow(subject.Set(x => x.Pets.ElementAt(-2).Name, "Fluffencutters"), expectedErrorMessage); + AssertThrow(subject.Set(x => x.Pets[-2].Name, "Fluffencutters"), expectedErrorMessage, LinqProvider.V3); + AssertThrow(subject.Set(x => x.Pets.ElementAt(-2).Name, "Fluffencutters"), expectedErrorMessage, LinqProvider.V3); } - [Fact] - public void Indexed_Positional_Typed() + [Theory] + [ParameterAttributeData] + public void Indexed_Positional_Typed( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { var subject = CreateSubject(); + if (linqProvider == LinqProvider.V2) + { #pragma warning disable - Assert(subject.Set(x => x.FavoriteColors[-1], "yellow"), "{$set: {'colors.$': 'yellow'}}"); + Assert(subject.Set(x => x.FavoriteColors[-1], "yellow"), "{$set: {'colors.$': 'yellow'}}", linqProvider); #pragma warning restore - Assert(subject.Set(x => x.Pets[-1].Name, "Fluffencutters"), "{$set: {'pets.$.name': 'Fluffencutters'}}"); - Assert(subject.Set(x => x.Pets.ElementAt(-1).Name, "Fluffencutters"), "{$set: {'pets.$.name': 'Fluffencutters'}}"); + Assert(subject.Set(x => x.Pets[-1].Name, "Fluffencutters"), "{$set: {'pets.$.name': 'Fluffencutters'}}", linqProvider); + Assert(subject.Set(x => x.Pets.ElementAt(-1).Name, "Fluffencutters"), "{$set: {'pets.$.name': 'Fluffencutters'}}", linqProvider); + } + else + { + Assert(subject.Set(x => x.FavoriteColors.FirstMatchingElement(), "yellow"), "{$set: {'colors.$': 'yellow'}}", linqProvider); + Assert(subject.Set(x => x.Pets.FirstMatchingElement().Name, "Fluffencutters"), "{$set: {'pets.$.name': 'Fluffencutters'}}", linqProvider); + Assert(subject.Set(x => x.Pets.FirstMatchingElement().Name, "Fluffencutters"), "{$set: {'pets.$.name': 'Fluffencutters'}}", linqProvider); + } } [Fact] @@ -647,9 +661,9 @@ public void Unset_Typed() Assert(subject.Unset("Age"), "{$unset: {age: 1}}"); } - private void Assert(UpdateDefinition update, BsonDocument expected) + private void Assert(UpdateDefinition update, BsonDocument expected, LinqProvider linqProvider = LinqProvider.V3) { - var renderedUpdate = Render(update).AsBsonDocument; + var renderedUpdate = Render(update, linqProvider).AsBsonDocument; renderedUpdate.Should().Be(expected); } @@ -662,16 +676,16 @@ private void Assert(UpdateDefinition update, string[] expe renderedUpdate.Should().Be(bsonArray); } - private void Assert(UpdateDefinition update, string expected) + private void Assert(UpdateDefinition update, string expected, LinqProvider linqProvider = LinqProvider.V3) { - Assert(update, BsonDocument.Parse(expected)); + Assert(update, BsonDocument.Parse(expected), linqProvider); } - private void AssertThrow(UpdateDefinition update, string errorMessage) where TException : Exception + private void AssertThrow(UpdateDefinition update, string errorMessage, LinqProvider linqProvider = LinqProvider.V3) where TException : Exception { - var exception = Record.Exception(() => { Render(update); }); + var exception = Record.Exception(() => { Render(update, linqProvider); }); exception.Should().BeOfType(); - exception.Message.Should().Be(errorMessage); + exception.Message.Should().Contain(errorMessage); } private UpdateDefinitionBuilder CreateSubject() @@ -679,10 +693,10 @@ private UpdateDefinitionBuilder CreateSubject() return new UpdateDefinitionBuilder(); } - private BsonValue Render(UpdateDefinition update) + private BsonValue Render(UpdateDefinition update, LinqProvider linqProvider = LinqProvider.V3) { var documentSerializer = BsonSerializer.SerializerRegistry.GetSerializer(); - return update.Render(documentSerializer, BsonSerializer.SerializerRegistry); + return update.Render(documentSerializer, BsonSerializer.SerializerRegistry, linqProvider); } private class Person diff --git a/tests/MongoDB.TestHelpers/MongoDB.TestHelpers.csproj b/tests/MongoDB.TestHelpers/MongoDB.TestHelpers.csproj new file mode 100644 index 00000000000..ade074a59f1 --- /dev/null +++ b/tests/MongoDB.TestHelpers/MongoDB.TestHelpers.csproj @@ -0,0 +1,15 @@ + + + + + $(StandardTargetFrameworks) + ..\..\MongoDBLegacyTest.ruleset + + + + MongoDB.TestHelpers + MongoDB.TestHelpers + Helper classes applicable to all test projects. + + + diff --git a/tests/MongoDB.TestHelpers/Properties/AssemblyInfo.cs b/tests/MongoDB.TestHelpers/Properties/AssemblyInfo.cs new file mode 100644 index 00000000000..cc39fd111c9 --- /dev/null +++ b/tests/MongoDB.TestHelpers/Properties/AssemblyInfo.cs @@ -0,0 +1,18 @@ +/* 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.Runtime.InteropServices; + +[assembly: ComVisible(false)] diff --git a/tests/MongoDB.Bson.TestHelpers/XunitExtensions/AssertionException.cs b/tests/MongoDB.TestHelpers/XunitExtensions/AssertionException.cs similarity index 90% rename from tests/MongoDB.Bson.TestHelpers/XunitExtensions/AssertionException.cs rename to tests/MongoDB.TestHelpers/XunitExtensions/AssertionException.cs index a5c3a27be25..1499b69ac56 100644 --- a/tests/MongoDB.Bson.TestHelpers/XunitExtensions/AssertionException.cs +++ b/tests/MongoDB.TestHelpers/XunitExtensions/AssertionException.cs @@ -1,4 +1,4 @@ -/* Copyright 2016-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. @@ -16,7 +16,7 @@ using System; using Xunit.Sdk; -namespace MongoDB.Bson.TestHelpers.XunitExtensions +namespace MongoDB.TestHelpers.XunitExtensions { public class AssertionException : XunitException { diff --git a/tests/MongoDB.Bson.TestHelpers/XunitExtensions/ClassValuesAttribute.cs b/tests/MongoDB.TestHelpers/XunitExtensions/ClassValuesAttribute.cs similarity index 93% rename from tests/MongoDB.Bson.TestHelpers/XunitExtensions/ClassValuesAttribute.cs rename to tests/MongoDB.TestHelpers/XunitExtensions/ClassValuesAttribute.cs index 7925a0f05d2..64298bc2a1d 100644 --- a/tests/MongoDB.Bson.TestHelpers/XunitExtensions/ClassValuesAttribute.cs +++ b/tests/MongoDB.TestHelpers/XunitExtensions/ClassValuesAttribute.cs @@ -1,4 +1,4 @@ -/* Copyright 2020-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. @@ -15,7 +15,7 @@ using System; -namespace MongoDB.Bson.TestHelpers.XunitExtensions +namespace MongoDB.TestHelpers.XunitExtensions { [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)] public class ClassValuesAttribute : Attribute, IValueGeneratorAttribute diff --git a/tests/MongoDB.Bson.TestHelpers/XunitExtensions/IValueGenerator.cs b/tests/MongoDB.TestHelpers/XunitExtensions/IValueGenerator.cs similarity index 87% rename from tests/MongoDB.Bson.TestHelpers/XunitExtensions/IValueGenerator.cs rename to tests/MongoDB.TestHelpers/XunitExtensions/IValueGenerator.cs index d1b0ad98cf8..b15f3f295be 100644 --- a/tests/MongoDB.Bson.TestHelpers/XunitExtensions/IValueGenerator.cs +++ b/tests/MongoDB.TestHelpers/XunitExtensions/IValueGenerator.cs @@ -1,4 +1,4 @@ -/* Copyright 2020-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. @@ -13,7 +13,7 @@ * limitations under the License. */ -namespace MongoDB.Bson.TestHelpers.XunitExtensions +namespace MongoDB.TestHelpers.XunitExtensions { public interface IValueGenerator { diff --git a/tests/MongoDB.Bson.TestHelpers/XunitExtensions/IValueGeneratorAttribute.cs b/tests/MongoDB.TestHelpers/XunitExtensions/IValueGeneratorAttribute.cs similarity index 87% rename from tests/MongoDB.Bson.TestHelpers/XunitExtensions/IValueGeneratorAttribute.cs rename to tests/MongoDB.TestHelpers/XunitExtensions/IValueGeneratorAttribute.cs index d9bfaffe386..0f35a840c42 100644 --- a/tests/MongoDB.Bson.TestHelpers/XunitExtensions/IValueGeneratorAttribute.cs +++ b/tests/MongoDB.TestHelpers/XunitExtensions/IValueGeneratorAttribute.cs @@ -1,4 +1,4 @@ -/* Copyright 2016-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. @@ -13,7 +13,7 @@ * limitations under the License. */ -namespace MongoDB.Bson.TestHelpers.XunitExtensions +namespace MongoDB.TestHelpers.XunitExtensions { internal interface IValueGeneratorAttribute { diff --git a/tests/MongoDB.Bson.TestHelpers/XunitExtensions/ParameterAttributeDataAttribute.cs b/tests/MongoDB.TestHelpers/XunitExtensions/ParameterAttributeDataAttribute.cs similarity index 96% rename from tests/MongoDB.Bson.TestHelpers/XunitExtensions/ParameterAttributeDataAttribute.cs rename to tests/MongoDB.TestHelpers/XunitExtensions/ParameterAttributeDataAttribute.cs index 63136fb24e6..bc18fa1823a 100644 --- a/tests/MongoDB.Bson.TestHelpers/XunitExtensions/ParameterAttributeDataAttribute.cs +++ b/tests/MongoDB.TestHelpers/XunitExtensions/ParameterAttributeDataAttribute.cs @@ -1,4 +1,4 @@ -/* Copyright 2016-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. @@ -18,7 +18,7 @@ using System.Reflection; using Xunit.Sdk; -namespace MongoDB.Bson.TestHelpers.XunitExtensions +namespace MongoDB.TestHelpers.XunitExtensions { public class ParameterAttributeDataAttribute : DataAttribute { diff --git a/tests/MongoDB.Bson.TestHelpers/XunitExtensions/RandomSeedAttribute.cs b/tests/MongoDB.TestHelpers/XunitExtensions/RandomSeedAttribute.cs similarity index 92% rename from tests/MongoDB.Bson.TestHelpers/XunitExtensions/RandomSeedAttribute.cs rename to tests/MongoDB.TestHelpers/XunitExtensions/RandomSeedAttribute.cs index 194e851fce6..94dcf9341f4 100644 --- a/tests/MongoDB.Bson.TestHelpers/XunitExtensions/RandomSeedAttribute.cs +++ b/tests/MongoDB.TestHelpers/XunitExtensions/RandomSeedAttribute.cs @@ -1,4 +1,4 @@ -/* Copyright 2021-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. @@ -16,9 +16,8 @@ using System; using System.Diagnostics; using System.Linq; -using MongoDB.Bson.TestHelpers.XunitExtensions; -namespace MongoDB.Bson.TestHelpers +namespace MongoDB.TestHelpers.XunitExtensions { [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)] public class RandomSeedAttribute : Attribute, IValueGeneratorAttribute diff --git a/tests/MongoDB.Bson.TestHelpers/XunitExtensions/RangeAttribute.cs b/tests/MongoDB.TestHelpers/XunitExtensions/RangeAttribute.cs similarity index 94% rename from tests/MongoDB.Bson.TestHelpers/XunitExtensions/RangeAttribute.cs rename to tests/MongoDB.TestHelpers/XunitExtensions/RangeAttribute.cs index 670398e8d81..daca4517998 100644 --- a/tests/MongoDB.Bson.TestHelpers/XunitExtensions/RangeAttribute.cs +++ b/tests/MongoDB.TestHelpers/XunitExtensions/RangeAttribute.cs @@ -1,4 +1,4 @@ -/* Copyright 2016-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. @@ -16,7 +16,7 @@ using System; using System.Collections.Generic; -namespace MongoDB.Bson.TestHelpers.XunitExtensions +namespace MongoDB.TestHelpers.XunitExtensions { [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)] public sealed class RangeAttribute : Attribute, IValueGeneratorAttribute diff --git a/tests/MongoDB.Bson.TestHelpers/XunitExtensions/RequireEnvironment.cs b/tests/MongoDB.TestHelpers/XunitExtensions/RequireEnvironment.cs similarity index 54% rename from tests/MongoDB.Bson.TestHelpers/XunitExtensions/RequireEnvironment.cs rename to tests/MongoDB.TestHelpers/XunitExtensions/RequireEnvironment.cs index bfc70fa90ce..476dd3dc540 100644 --- a/tests/MongoDB.Bson.TestHelpers/XunitExtensions/RequireEnvironment.cs +++ b/tests/MongoDB.TestHelpers/XunitExtensions/RequireEnvironment.cs @@ -1,4 +1,4 @@ -/* Copyright 2016-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. @@ -15,9 +15,11 @@ using System; using System.Diagnostics; -using Xunit; +using System.Net; +using System.Net.Sockets; +using Xunit.Sdk; -namespace MongoDB.Bson.TestHelpers.XunitExtensions +namespace MongoDB.TestHelpers.XunitExtensions { public class RequireEnvironment { @@ -28,10 +30,11 @@ public static RequireEnvironment Check() } #endregion - public RequireEnvironment EnvironmentVariable(string name, bool isDefined = true) + public RequireEnvironment EnvironmentVariable(string name, bool isDefined = true, bool allowEmpty = true) { - var actualIsDefined = Environment.GetEnvironmentVariable(name) != null; - if (actualIsDefined == isDefined) + var actualValue = Environment.GetEnvironmentVariable(name); + var actualIsDefined = actualValue != null; + if (actualIsDefined == isDefined && (allowEmpty || !string.IsNullOrEmpty(actualValue))) { return this; } @@ -46,5 +49,30 @@ public RequireEnvironment ProcessStarted(string processName) } throw new SkipException($"Test skipped because an OS process {processName} has not been detected."); } + + public RequireEnvironment HostReachable(DnsEndPoint endPoint) + { + if (IsReachable()) + { + return this; + } + throw new SkipException($"Test skipped because expected server {endPoint} is not reachable."); + + bool IsReachable() + { + using (TcpClient tcpClient = new TcpClient()) + { + try + { + tcpClient.Connect(endPoint.Host, endPoint.Port); + return true; + } + catch (Exception) + { + return false; + } + } + } + } } } diff --git a/tests/MongoDB.Bson.TestHelpers/XunitExtensions/RequireProcess.cs b/tests/MongoDB.TestHelpers/XunitExtensions/RequireProcess.cs similarity index 91% rename from tests/MongoDB.Bson.TestHelpers/XunitExtensions/RequireProcess.cs rename to tests/MongoDB.TestHelpers/XunitExtensions/RequireProcess.cs index a575ad5f282..87d447dc5b6 100644 --- a/tests/MongoDB.Bson.TestHelpers/XunitExtensions/RequireProcess.cs +++ b/tests/MongoDB.TestHelpers/XunitExtensions/RequireProcess.cs @@ -1,4 +1,4 @@ -/* Copyright 2016-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. @@ -14,9 +14,9 @@ */ using System; -using Xunit; +using Xunit.Sdk; -namespace MongoDB.Bson.TestHelpers.XunitExtensions +namespace MongoDB.TestHelpers.XunitExtensions { public class RequireProcess { diff --git a/src/MongoDB.Driver.Core/Core/NativeLibraryLoader/INativeLibraryLoader.cs b/tests/MongoDB.TestHelpers/XunitExtensions/TimeoutEnforcing/ITestExceptionHandler.cs similarity index 75% rename from src/MongoDB.Driver.Core/Core/NativeLibraryLoader/INativeLibraryLoader.cs rename to tests/MongoDB.TestHelpers/XunitExtensions/TimeoutEnforcing/ITestExceptionHandler.cs index a054ab12699..2383cff3e6b 100644 --- a/src/MongoDB.Driver.Core/Core/NativeLibraryLoader/INativeLibraryLoader.cs +++ b/tests/MongoDB.TestHelpers/XunitExtensions/TimeoutEnforcing/ITestExceptionHandler.cs @@ -1,4 +1,4 @@ -/* Copyright 2019-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. @@ -15,10 +15,10 @@ using System; -namespace MongoDB.Driver.Core.NativeLibraryLoader +namespace MongoDB.TestHelpers.XunitExtensions.TimeoutEnforcing { - internal interface INativeLibraryLoader + public interface ITestExceptionHandler { - IntPtr GetFunctionPointer(string name); + void HandleException(Exception ex); } } diff --git a/tests/MongoDB.TestHelpers/XunitExtensions/TimeoutEnforcing/SkippableTestMessageBus.cs b/tests/MongoDB.TestHelpers/XunitExtensions/TimeoutEnforcing/SkippableTestMessageBus.cs new file mode 100644 index 00000000000..733ee106159 --- /dev/null +++ b/tests/MongoDB.TestHelpers/XunitExtensions/TimeoutEnforcing/SkippableTestMessageBus.cs @@ -0,0 +1,61 @@ +/* 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 Xunit.Abstractions; +using Xunit.Sdk; + +namespace MongoDB.TestHelpers.XunitExtensions.TimeoutEnforcing +{ + internal sealed class SkippableTestMessageBus : IMessageBus + { + private readonly static string __skippableExceptionName = typeof(SkipException).FullName; + + private readonly IMessageBus _messageBus; + private int _skippedCount; + + public SkippableTestMessageBus(IMessageBus messageBus) + { + if (messageBus == null) + { + throw new ArgumentNullException(nameof(messageBus)); + } + + _messageBus = messageBus; + } + + public int SkippedCount => _skippedCount; + + public void Dispose() + { + _messageBus.Dispose(); + } + + /// + public bool QueueMessage(IMessageSinkMessage message) + { + var failed = message as TestFailed; + if (message is TestFailed testFailed && + testFailed.ExceptionTypes.FirstOrDefault() == __skippableExceptionName) + { + _skippedCount++; + return _messageBus.QueueMessage(new TestSkipped(failed.Test, failed.Messages[0])); + } + + return _messageBus.QueueMessage(message); + } + } +} diff --git a/tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingTestInvoker.cs b/tests/MongoDB.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingTestInvoker.cs similarity index 59% rename from tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingTestInvoker.cs rename to tests/MongoDB.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingTestInvoker.cs index 351ca9c6b5f..69c16fc24d8 100644 --- a/tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingTestInvoker.cs +++ b/tests/MongoDB.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingTestInvoker.cs @@ -1,4 +1,4 @@ -/* Copyright 2021-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. @@ -17,18 +17,46 @@ using System.Collections.Generic; using System.Diagnostics; using System.Reflection; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; -using MongoDB.Driver.Core.TestHelpers.Logging; -using Xunit; using Xunit.Abstractions; using Xunit.Sdk; -namespace MongoDB.Driver.Core.TestHelpers.XunitExtensions.TimeoutEnforcing +namespace MongoDB.TestHelpers.XunitExtensions.TimeoutEnforcing { [DebuggerStepThrough] internal sealed class TimeoutEnforcingTestInvoker : XunitTestInvoker { + // This is a copy of MongoDB.Driver.Core.Misc.TaskExtensions.YieldNoContextAwaitable struct + // Remove this copy when moving TaskExtensions to BSON level. + private struct YieldNoContextAwaitable + { + public YieldNoContextAwaiter GetAwaiter() { return new YieldNoContextAwaiter(); } + + public struct YieldNoContextAwaiter : ICriticalNotifyCompletion + { + /// Gets whether a yield is not required. + /// This property is intended for compiler user rather than use directly in code. + public bool IsCompleted { get { return false; } } // yielding is always required for YieldNoContextAwaiter, hence false + + public void OnCompleted(Action continuation) + { + Task.Factory.StartNew(continuation, default, TaskCreationOptions.PreferFairness, TaskScheduler.Default); + } + + public void UnsafeOnCompleted(Action continuation) + { + Task.Factory.StartNew(continuation, default, TaskCreationOptions.PreferFairness, TaskScheduler.Default); + } + + public void GetResult() + { + // no op + } + } + } + public TimeoutEnforcingTestInvoker( ITest test, IMessageBus messageBus, @@ -45,7 +73,7 @@ public TimeoutEnforcingTestInvoker( private async Task InvokeBaseOnTaskScheduler(object testClassInstance) { - await Misc.TaskExtensions.YieldNoContext(); + await new YieldNoContextAwaitable(); return await base.InvokeTestMethodAsync(testClassInstance); } @@ -56,10 +84,10 @@ protected override async Task InvokeTestMethodAsync(object testClassIns var timeoutMS = xUnitTestCase?.Timeout ?? 0; var timeout = Debugger.IsAttached ? Timeout.InfiniteTimeSpan // allow more flexible debugging expirience - : timeoutMS <= 0 ? CoreTestConfiguration.DefaultTestTimeout : TimeSpan.FromMilliseconds(timeoutMS); + : timeoutMS <= 0 ? XunitExtensionsConstants.DefaultTestTimeout : TimeSpan.FromMilliseconds(timeoutMS); - var testLoggable = testClassInstance as LoggableTestClass; + var testExceptionHandler = testClassInstance as ITestExceptionHandler; decimal result; try @@ -72,13 +100,13 @@ protected override async Task InvokeTestMethodAsync(object testClassIns throw new TestTimeoutException((int)timeout.TotalMilliseconds); } - if (Aggregator.HasExceptions && testLoggable != null) + if (Aggregator.HasExceptions && testExceptionHandler != null) { var exception = Aggregator.ToException(); if (exception is not SkipException) { - testLoggable.OnException(exception); + testExceptionHandler.HandleException(exception); } } @@ -86,7 +114,7 @@ protected override async Task InvokeTestMethodAsync(object testClassIns } catch (Exception exception) { - testLoggable?.OnException(exception); + testExceptionHandler?.HandleException(exception); throw; } diff --git a/tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingTestRunner.cs b/tests/MongoDB.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingTestRunner.cs similarity index 93% rename from tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingTestRunner.cs rename to tests/MongoDB.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingTestRunner.cs index 0100333a3ab..23602b4e7b5 100644 --- a/tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingTestRunner.cs +++ b/tests/MongoDB.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingTestRunner.cs @@ -1,4 +1,4 @@ -/* Copyright 2021-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. @@ -22,7 +22,7 @@ using Xunit.Abstractions; using Xunit.Sdk; -namespace MongoDB.Driver.Core.TestHelpers.XunitExtensions.TimeoutEnforcing +namespace MongoDB.TestHelpers.XunitExtensions.TimeoutEnforcing { [DebuggerStepThrough] internal sealed class TimeoutEnforcingTestRunner : XunitTestRunner diff --git a/tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestAssemblyRunner.cs b/tests/MongoDB.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestAssemblyRunner.cs similarity index 93% rename from tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestAssemblyRunner.cs rename to tests/MongoDB.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestAssemblyRunner.cs index 553758b5dcf..7719fe7500e 100644 --- a/tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestAssemblyRunner.cs +++ b/tests/MongoDB.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestAssemblyRunner.cs @@ -1,4 +1,4 @@ -/* Copyright 2021-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. @@ -20,7 +20,7 @@ using Xunit.Abstractions; using Xunit.Sdk; -namespace MongoDB.Driver.Core.TestHelpers.XunitExtensions.TimeoutEnforcing +namespace MongoDB.TestHelpers.XunitExtensions.TimeoutEnforcing { [DebuggerStepThrough] internal sealed class TimeoutEnforcingXunitTestAssemblyRunner : XunitTestAssemblyRunner diff --git a/tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestCaseRunner.cs b/tests/MongoDB.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestCaseRunner.cs similarity index 94% rename from tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestCaseRunner.cs rename to tests/MongoDB.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestCaseRunner.cs index 9707b9b2466..b590b275d7b 100644 --- a/tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestCaseRunner.cs +++ b/tests/MongoDB.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestCaseRunner.cs @@ -1,4 +1,4 @@ -/* Copyright 2021-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. @@ -21,7 +21,7 @@ using Xunit.Abstractions; using Xunit.Sdk; -namespace MongoDB.Driver.Core.TestHelpers.XunitExtensions.TimeoutEnforcing +namespace MongoDB.TestHelpers.XunitExtensions.TimeoutEnforcing { [DebuggerStepThrough] internal sealed class TimeoutEnforcingXunitTestCaseRunner : XunitTestCaseRunner diff --git a/tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestClassRunner.cs b/tests/MongoDB.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestClassRunner.cs similarity index 93% rename from tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestClassRunner.cs rename to tests/MongoDB.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestClassRunner.cs index 15f6ea90f3f..95080257f2e 100644 --- a/tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestClassRunner.cs +++ b/tests/MongoDB.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestClassRunner.cs @@ -1,4 +1,4 @@ -/* Copyright 2021-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. @@ -21,7 +21,7 @@ using Xunit.Abstractions; using Xunit.Sdk; -namespace MongoDB.Driver.Core.TestHelpers.XunitExtensions.TimeoutEnforcing +namespace MongoDB.TestHelpers.XunitExtensions.TimeoutEnforcing { [DebuggerStepThrough] internal sealed class TimeoutEnforcingXunitTestClassRunner : XunitTestClassRunner diff --git a/tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestCollectionRunner.cs b/tests/MongoDB.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestCollectionRunner.cs similarity index 93% rename from tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestCollectionRunner.cs rename to tests/MongoDB.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestCollectionRunner.cs index c6c43384f9c..fe429789841 100644 --- a/tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestCollectionRunner.cs +++ b/tests/MongoDB.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestCollectionRunner.cs @@ -1,4 +1,4 @@ -/* Copyright 2021-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. @@ -20,7 +20,7 @@ using Xunit.Abstractions; using Xunit.Sdk; -namespace MongoDB.Driver.Core.TestHelpers.XunitExtensions.TimeoutEnforcing +namespace MongoDB.TestHelpers.XunitExtensions.TimeoutEnforcing { [DebuggerStepThrough] internal sealed class TimeoutEnforcingXunitTestCollectionRunner : XunitTestCollectionRunner diff --git a/tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestFramework.cs b/tests/MongoDB.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestFramework.cs similarity index 90% rename from tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestFramework.cs rename to tests/MongoDB.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestFramework.cs index f7b035f70e9..47c7ab5b85d 100644 --- a/tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestFramework.cs +++ b/tests/MongoDB.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestFramework.cs @@ -1,4 +1,4 @@ -/* Copyright 2021-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. @@ -18,7 +18,7 @@ using Xunit.Abstractions; using Xunit.Sdk; -namespace MongoDB.Driver.Core.TestHelpers.XunitExtensions.TimeoutEnforcing +namespace MongoDB.TestHelpers.XunitExtensions.TimeoutEnforcing { [DebuggerStepThrough] public sealed class TimeoutEnforcingXunitTestFramework : XunitTestFramework diff --git a/tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestFrameworkExecutor.cs b/tests/MongoDB.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestFrameworkExecutor.cs similarity index 92% rename from tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestFrameworkExecutor.cs rename to tests/MongoDB.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestFrameworkExecutor.cs index 31e734bc7c6..4b724557d75 100644 --- a/tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestFrameworkExecutor.cs +++ b/tests/MongoDB.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestFrameworkExecutor.cs @@ -1,4 +1,4 @@ -/* Copyright 2021-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. @@ -19,7 +19,7 @@ using Xunit.Abstractions; using Xunit.Sdk; -namespace MongoDB.Driver.Core.TestHelpers.XunitExtensions.TimeoutEnforcing +namespace MongoDB.TestHelpers.XunitExtensions.TimeoutEnforcing { [DebuggerStepThrough] internal sealed class TimeoutEnforcingXunitTestFrameworkExecutor : XunitTestFrameworkExecutor diff --git a/tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestMethodRunner.cs b/tests/MongoDB.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestMethodRunner.cs similarity index 90% rename from tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestMethodRunner.cs rename to tests/MongoDB.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestMethodRunner.cs index bbd95e563be..67f45241ff5 100644 --- a/tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestMethodRunner.cs +++ b/tests/MongoDB.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTestMethodRunner.cs @@ -1,4 +1,4 @@ -/* Copyright 2021-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. @@ -17,17 +17,14 @@ using System.Diagnostics; using System.Threading; using System.Threading.Tasks; -using Xunit; using Xunit.Abstractions; using Xunit.Sdk; -namespace MongoDB.Driver.Core.TestHelpers.XunitExtensions.TimeoutEnforcing +namespace MongoDB.TestHelpers.XunitExtensions.TimeoutEnforcing { [DebuggerStepThrough] internal sealed class TimeoutEnforcingXunitTestMethodRunner : XunitTestMethodRunner { - private static string[] __skippingExceptionNames = new string[] { typeof(SkipException).FullName }; - private readonly object[] _constructorArguments; private readonly IMessageSink _diagnosticMessageSink; @@ -39,7 +36,7 @@ public TimeoutEnforcingXunitTestMethodRunner(ITestMethod testMethod, IReflection protected override async Task RunTestCaseAsync(IXunitTestCase originalTestCase) { - var messageBusInterceptor = new SkippableTestMessageBus(MessageBus, __skippingExceptionNames); + var messageBusInterceptor = new SkippableTestMessageBus(MessageBus); var isTheory = originalTestCase is XunitTheoryTestCase; XunitTestCaseRunner testRunner = isTheory ? diff --git a/tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTheoryTestCaseRunner.cs b/tests/MongoDB.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTheoryTestCaseRunner.cs similarity index 94% rename from tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTheoryTestCaseRunner.cs rename to tests/MongoDB.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTheoryTestCaseRunner.cs index bf2a952c6f8..fe4433bf366 100644 --- a/tests/MongoDB.Driver.Core.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTheoryTestCaseRunner.cs +++ b/tests/MongoDB.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingXunitTheoryTestCaseRunner.cs @@ -1,4 +1,4 @@ -/* Copyright 2021-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. @@ -21,7 +21,7 @@ using Xunit.Abstractions; using Xunit.Sdk; -namespace MongoDB.Driver.Core.TestHelpers.XunitExtensions.TimeoutEnforcing +namespace MongoDB.TestHelpers.XunitExtensions.TimeoutEnforcing { [DebuggerStepThrough] internal sealed class TimeoutEnforcingXunitTheoryTestCaseRunner : XunitTheoryTestCaseRunner diff --git a/tests/MongoDB.Bson.TestHelpers/XunitExtensions/ValuesAttribute.cs b/tests/MongoDB.TestHelpers/XunitExtensions/ValuesAttribute.cs similarity index 91% rename from tests/MongoDB.Bson.TestHelpers/XunitExtensions/ValuesAttribute.cs rename to tests/MongoDB.TestHelpers/XunitExtensions/ValuesAttribute.cs index a18d9931139..18a949b0eed 100644 --- a/tests/MongoDB.Bson.TestHelpers/XunitExtensions/ValuesAttribute.cs +++ b/tests/MongoDB.TestHelpers/XunitExtensions/ValuesAttribute.cs @@ -1,4 +1,4 @@ -/* Copyright 2016-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. @@ -16,7 +16,7 @@ using System; using System.Linq; -namespace MongoDB.Bson.TestHelpers.XunitExtensions +namespace MongoDB.TestHelpers.XunitExtensions { [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)] public sealed class ValuesAttribute : Attribute, IValueGeneratorAttribute diff --git a/tests/MongoDB.TestHelpers/XunitExtensions/XunitExtensionsConstants.cs b/tests/MongoDB.TestHelpers/XunitExtensions/XunitExtensionsConstants.cs new file mode 100644 index 00000000000..6eef51ed01e --- /dev/null +++ b/tests/MongoDB.TestHelpers/XunitExtensions/XunitExtensionsConstants.cs @@ -0,0 +1,27 @@ +/* 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; + +namespace MongoDB.TestHelpers.XunitExtensions +{ + public static class XunitExtensionsConstants + { + public const string TimeoutEnforcingXunitFramework = "MongoDB.TestHelpers.XunitExtensions.TimeoutEnforcing.TimeoutEnforcingXunitTestFramework"; + public const string TimeoutEnforcingFrameworkAssembly = "MongoDB.TestHelpers"; + + public static readonly TimeSpan DefaultTestTimeout = TimeSpan.FromMinutes(3); + } +} diff --git a/tests/SkippableTests/FactTests.cs b/tests/SkippableTests/FactTests.cs deleted file mode 100644 index 3eebcf138a3..00000000000 --- a/tests/SkippableTests/FactTests.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using FluentAssertions; -using Xunit; - -namespace SkippableTests -{ - public class FactTests - { - [Fact] - public void Fact_should_fail() - { - true.Should().BeFalse(); - } - - [Fact] - public void Fact_should_pass() - { - true.Should().BeTrue(); - } - } -} diff --git a/tests/SkippableTests/SkippableFactTests.cs b/tests/SkippableTests/SkippableFactTests.cs deleted file mode 100644 index 9369907874c..00000000000 --- a/tests/SkippableTests/SkippableFactTests.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using FluentAssertions; -using Xunit; - -namespace SkippableTests -{ - public class SkippableFactTests - { - [SkippableFact] - public void SkippableFact_should_fail() - { - true.Should().BeFalse(); - } - - [SkippableFact] - public void SkippableFact_should_pass() - { - true.Should().BeTrue(); - } - - [SkippableFact] - public void SkippableFact_should_be_skipped() - { - throw new SkipException("test"); - } - } -} diff --git a/tests/SkippableTests/SkippableTests.csproj b/tests/SkippableTests/SkippableTests.csproj deleted file mode 100644 index c6f90e17adb..00000000000 --- a/tests/SkippableTests/SkippableTests.csproj +++ /dev/null @@ -1,40 +0,0 @@ - - - true - - - - netcoreapp2.1;net472 - netcoreapp2.1 - 9 - true - - false - ..\..\MongoDBLegacyTest.ruleset - - - - TRACE - - - - - - - - - - - - - - - - - - - Always - - - - diff --git a/tests/SkippableTests/SkippableTheoryTests.cs b/tests/SkippableTests/SkippableTheoryTests.cs deleted file mode 100644 index 9e66bcba3ed..00000000000 --- a/tests/SkippableTests/SkippableTheoryTests.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using FluentAssertions; -using Xunit; - -namespace SkippableTests -{ - public class SkippableTheoryTests - { - [SkippableTheory] - [InlineData(0)] - public void SkippableTheory_should_fail(int x) - { - x.Should().Be(1); - } - - [SkippableTheory] - [InlineData(0)] - public void SkippableTheory_should_pass(int x) - { - x.Should().Be(0); - } - - [SkippableTheory] - [InlineData(0)] - public void SkippableTheory_should_be_skipped(int x) - { - if (x == 0) - { - throw new SkipException("test"); - } - } - } -} diff --git a/tests/SkippableTests/TheoryTests.cs b/tests/SkippableTests/TheoryTests.cs deleted file mode 100644 index a8655b2be8b..00000000000 --- a/tests/SkippableTests/TheoryTests.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using FluentAssertions; -using Xunit; - -namespace SkippableTests -{ - public class TheoryTests - { - [Theory] - [InlineData(0)] - public void Theory_should_fail(int x) - { - x.Should().Be(1); - } - - [Theory] - [InlineData(0)] - public void Theory_should_pass(int x) - { - x.Should().Be(0); - } - } -} diff --git a/tests/SmokeTests/MongoDB.Driver.SmokeTests.Sdk/InfrastructureUtilities.cs b/tests/SmokeTests/MongoDB.Driver.SmokeTests.Sdk/InfrastructureUtilities.cs new file mode 100644 index 00000000000..a3b72feb3ab --- /dev/null +++ b/tests/SmokeTests/MongoDB.Driver.SmokeTests.Sdk/InfrastructureUtilities.cs @@ -0,0 +1,37 @@ +/* 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.Diagnostics; +using FluentAssertions; + +namespace MongoDB.Driver.SmokeTests.Sdk +{ + internal static class InfrastructureUtilities + { + public static void ValidateMongoDBPackageVersion() + { + var packageShaExpected = Environment.GetEnvironmentVariable("SmokeTestsPackageSha"); + + if (!string.IsNullOrEmpty(packageShaExpected)) + { + var fileVersionInfo = FileVersionInfo.GetVersionInfo(typeof(MongoClient).Assembly.Location); + + fileVersionInfo.ProductVersion.Contains(packageShaExpected) + .Should().BeTrue("Expected package sha {0} in {1}", packageShaExpected, fileVersionInfo.ProductVersion); + } + } + } +} diff --git a/tests/SmokeTests/MongoDB.Driver.SmokeTests.Sdk/LogEntry.cs b/tests/SmokeTests/MongoDB.Driver.SmokeTests.Sdk/LogEntry.cs new file mode 100644 index 00000000000..a079ede3303 --- /dev/null +++ b/tests/SmokeTests/MongoDB.Driver.SmokeTests.Sdk/LogEntry.cs @@ -0,0 +1,35 @@ +/* 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 Microsoft.Extensions.Logging; + +namespace MongoDB.Driver.SmokeTests.Sdk +{ + internal struct LogEntry + { + public LogEntry(LogLevel logLevel, string category, string message) + { + LogLevel = logLevel; + Category = category; + Message = message; + } + + public LogLevel LogLevel { get; } + public string Category { get; } + public string Message { get; } + + public override string ToString() => $"{LogLevel}_{Category}_{Message}"; + } +} diff --git a/tests/SmokeTests/MongoDB.Driver.SmokeTests.Sdk/LoggingTests.cs b/tests/SmokeTests/MongoDB.Driver.SmokeTests.Sdk/LoggingTests.cs new file mode 100644 index 00000000000..a84a854b581 --- /dev/null +++ b/tests/SmokeTests/MongoDB.Driver.SmokeTests.Sdk/LoggingTests.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.Diagnostics; +using System.Linq; +using FluentAssertions; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using MongoDB.Driver.Core.Configuration; +using Xunit; +using Xunit.Abstractions; + +namespace MongoDB.Driver.SmokeTests.Sdk +{ + public sealed class LoggingTests + { + private readonly ITestOutputHelper _output; + + public LoggingTests(ITestOutputHelper output) + { + InfrastructureUtilities.ValidateMongoDBPackageVersion(); + + _output = output; + } + + [Theory] + [InlineData(null)] + [InlineData("MongoDB.SDAM")] + [InlineData("MongoDB.Connection")] + [InlineData("MongoDB.Internal.IServerMonitor")] + public void MongoClient_should_log_only_configured_categories(string categoryName) + { + var expectedLogs = GetExpectedLogs(); + (string, string)[] categories = null; + + if (categoryName != null) + { + categories = new[] { ("LogLevel:Default", "Error"), ($"LogLevel:{categoryName}", "Trace")}; + expectedLogs = expectedLogs.Where(l => l.Category == categoryName).ToArray(); + } + + using var logsTracer = new LogsTraceListener(); + using (var loggerFactory = GetLoggerFactory(logsTracer, categories)) + { + var settings = GetMongoClientSettings(); + settings.LoggingSettings = new LoggingSettings(loggerFactory); + var mongoClient = new MongoClient(settings); + + try + { + mongoClient.ListDatabases(new ListDatabasesOptions()); + } + finally + { + ClusterRegistry.Instance.UnregisterAndDisposeCluster(mongoClient.Cluster); + } + } + + var actualLogs = logsTracer.GetLogs(); + + try + { + AssertLogs(expectedLogs, actualLogs); + + if (categoryName != null) + { + Array.ForEach(actualLogs, l => l.Category.Should().Be(categoryName)); + } + } + catch + { + _output.WriteLine("Logs observed:"); + foreach (var log in actualLogs) + { + _output.WriteLine(log.ToString()); + } + + throw; + } + } + + private static LogEntry[] GetExpectedLogs() + { + return new[] + { + SDAM("Description changed"), + SDAM("Server opening"), + Connection("Connection pool opening"), + Connection("Connection pool created"), + SDAM("Server opened"), + SDAM("Cluster opened"), + Connection("Connection checkout started"), + Connection("Connection created"), + Connection("Connection ready"), + Connection("Connection added"), + Connection("Connection checked out"), + SDAM("Cluster closing"), + SDAM("Removing server"), + SDAM("Server closing"), + Connection("Connection closing"), + Connection("Connection closed"), + Connection("Connection pool closed"), + SDAM("Server closed"), + SDAM("Removed server"), + SDAM("Disposing"), + SDAM("Description changed"), + SDAM("Disposed"), + SDAM("Cluster closed") + }; + + LogEntry Connection(string message) => new LogEntry(LogLevel.Debug, "MongoDB.Connection", message); + LogEntry SDAM(string message) => new LogEntry(LogLevel.Debug, "MongoDB.SDAM", message); + } + + private static void AssertLogs(LogEntry[] expectedLogs, LogEntry[] actualLogs) + { + var actualLogIndex = 0; + foreach (var logEntryExpected in expectedLogs) + { + var newIndex = Array.FindIndex(actualLogs, actualLogIndex, Match); + + if (newIndex < 0) + { + throw new Exception($"Log entry '{logEntryExpected}' not found. Previous matched log entry {actualLogs[actualLogIndex]}"); + } + + actualLogIndex = newIndex; + + bool Match(LogEntry logEntryActual) => + logEntryActual.LogLevel == logEntryExpected.LogLevel && + logEntryActual.Category.Contains(logEntryExpected.Category) && + logEntryActual.Message.Contains(logEntryExpected.Message); + } + } + + private static ILoggerFactory GetLoggerFactory(TraceListener traceListener, (string Category, string LogLevel)[] categoriesVerbosity = null) + { + var configurationKeyValuePairs = categoriesVerbosity?.Select(p => + new KeyValuePair(p.Category, p.LogLevel)) ?? + new[] { new KeyValuePair("LogLevel:Default", "Trace") }; + + var config = new ConfigurationBuilder() + .AddInMemoryCollection(configurationKeyValuePairs) + .Build(); + + var testSwitch = new SourceSwitch("TestSwitch"); + testSwitch.Level = SourceLevels.All; + + var serviceCollection = new ServiceCollection(); + serviceCollection.AddLogging(builder => builder + .AddConfiguration(config) + .AddTraceSource(testSwitch, traceListener)); + + var serviceProvider = serviceCollection.BuildServiceProvider(); + var loggerFactory = serviceProvider.GetService(); + + return loggerFactory; + } + + private static MongoClientSettings GetMongoClientSettings() + { + var uri = Environment.GetEnvironmentVariable("MONGODB_URI") ?? + Environment.GetEnvironmentVariable("MONGO_URI") ?? + "mongodb://localhost"; + + return MongoClientSettings.FromConnectionString(uri); + } + } +} diff --git a/tests/SmokeTests/MongoDB.Driver.SmokeTests.Sdk/LogsTraceListener.cs b/tests/SmokeTests/MongoDB.Driver.SmokeTests.Sdk/LogsTraceListener.cs new file mode 100644 index 00000000000..0ad7bdc52e3 --- /dev/null +++ b/tests/SmokeTests/MongoDB.Driver.SmokeTests.Sdk/LogsTraceListener.cs @@ -0,0 +1,71 @@ +/* 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.Diagnostics; +using Microsoft.Extensions.Logging; + +namespace MongoDB.Driver.SmokeTests.Sdk +{ + internal sealed class LogsTraceListener : TraceListener + { + private readonly List _logEntries = new(); + private readonly object _syncRoot = new(); + + private string _currentCategory; + private LogLevel _currentLogLevel; + + public LogEntry[] GetLogs() + { + lock (_syncRoot) + { + return _logEntries.ToArray(); + } + } + + public override void Write(string message) + { + // TraceListener delivers the log message in two parts, for example: + // Log message "MongoDB.Internal.IServerMonitor Verbose: 0 : 1_localhost:27017 Initializing" is split to + // Write method: "MongoDB.Internal.IServerMonitor Verbose: 0 :" + // WriteLine method : "1_localhost:27017 Initializing" + var parts = message.Split(' '); + var sourceLevel = Enum.Parse(typeof(SourceLevels), parts[1].Trim(':')); + + _currentCategory = parts[0]; + _currentLogLevel = sourceLevel switch + { + SourceLevels.All or + SourceLevels.ActivityTracing => LogLevel.Trace, + SourceLevels.Verbose => LogLevel.Debug, + SourceLevels.Information => LogLevel.Information, + SourceLevels.Warning => LogLevel.Warning, + SourceLevels.Error => LogLevel.Error, + SourceLevels.Critical => LogLevel.Critical, + SourceLevels.Off => LogLevel.None, + _ => LogLevel.Trace + }; + } + + public override void WriteLine(string message) + { + lock (_syncRoot) + { + _logEntries.Add(new LogEntry(_currentLogLevel, _currentCategory, message)); + } + } + } +} diff --git a/tests/SmokeTests/MongoDB.Driver.SmokeTests.Sdk/MongoDB.Driver.SmokeTests.Sdk.csproj b/tests/SmokeTests/MongoDB.Driver.SmokeTests.Sdk/MongoDB.Driver.SmokeTests.Sdk.csproj new file mode 100644 index 00000000000..8c56b707d69 --- /dev/null +++ b/tests/SmokeTests/MongoDB.Driver.SmokeTests.Sdk/MongoDB.Driver.SmokeTests.Sdk.csproj @@ -0,0 +1,63 @@ + + + netcoreapp2.1;netcoreapp3.1;net472;net5.0;net6.0 + 9 + ..\..\..\MongoDBTest.ruleset + + + + + $(MSBuildThisFileDirectory)..\..\..\artifacts\packages + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Always + + + + \ No newline at end of file diff --git a/tests/SkippableTests/xunit.runner.json b/tests/SmokeTests/MongoDB.Driver.SmokeTests.Sdk/xunit.runner.json similarity index 52% rename from tests/SkippableTests/xunit.runner.json rename to tests/SmokeTests/MongoDB.Driver.SmokeTests.Sdk/xunit.runner.json index 3b86d1533b9..fb4173812b3 100644 --- a/tests/SkippableTests/xunit.runner.json +++ b/tests/SmokeTests/MongoDB.Driver.SmokeTests.Sdk/xunit.runner.json @@ -1,5 +1,6 @@ { "longRunningTestSeconds": 10, "parallelizeAssembly": false, - "parallelizeTestCollections": false + "parallelizeTestCollections": false, + "shadowCopy": false } \ No newline at end of file