Beispiel #1
0
 public void Run(JsonDrivenTestCase testCase)
 {
     RequireServer.Check().Supports(Feature.Transactions).ClusterType(ClusterType.ReplicaSet);
     Run(testCase.Shared, testCase.Test);
 }
        public void CreateDataKeyAndDoubleEncryptionTest(
            [Values("local", "aws")] string kmsProvider,
            [Values(false, true)] bool async)
        {
            RequireServer.Check().Supports(Feature.ClientSideEncryption);

            using (var client = ConfigureClient())
                using (var clientEncrypted = ConfigureClientEncrypted(BsonDocument.Parse(SchemaMap)))
                    using (var clientEncryption = ConfigureClientEncryption(clientEncrypted.Wrapped as MongoClient))
                    {
                        var dataKeyOptions = CreateDataKeyOptions(kmsProvider);

                        Guid dataKey;
                        if (async)
                        {
                            dataKey = clientEncryption
                                      .CreateDataKeyAsync(kmsProvider, dataKeyOptions, CancellationToken.None)
                                      .GetAwaiter()
                                      .GetResult();
                        }
                        else
                        {
                            dataKey = clientEncryption.CreateDataKey(kmsProvider, dataKeyOptions, CancellationToken.None);
                        }

                        var keyVaultCollection = GetCollection(client, __keyVaultCollectionNamespace);
                        var keyVaultDocument   =
                            Find(
                                keyVaultCollection,
                                new BsonDocument("_id", new BsonBinaryData(dataKey, GuidRepresentation.Standard)),
                                async)
                            .Single();
                        keyVaultDocument["masterKey"]["provider"].Should().Be(BsonValue.Create(kmsProvider));

                        var encryptOptions = new EncryptOptions(
                            EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic.ToString(),
                            keyId: dataKey);

                        var encryptedValue = ExplicitEncrypt(
                            clientEncryption,
                            encryptOptions,
                            $"hello {kmsProvider}",
                            async);
                        encryptedValue.SubType.Should().Be(BsonBinarySubType.Encrypted);

                        var coll = GetCollection(clientEncrypted, __collCollectionNamespace);
                        Insert(
                            coll,
                            async,
                            new BsonDocument
                        {
                            { "_id", kmsProvider },
                            { "value", encryptedValue }
                        });

                        var findResult = Find(coll, new BsonDocument("_id", kmsProvider), async).Single();
                        findResult["value"].ToString().Should().Be($"hello {kmsProvider}");

                        encryptOptions = new EncryptOptions(
                            EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic.ToString(),
                            alternateKeyName: $"{kmsProvider}_altname");
                        var encryptedValueWithAlternateKeyName = ExplicitEncrypt(
                            clientEncryption,
                            encryptOptions,
                            $"hello {kmsProvider}",
                            async);
                        encryptedValueWithAlternateKeyName.SubType.Should().Be(BsonBinarySubType.Encrypted);
                        encryptedValueWithAlternateKeyName.Should().Be(encryptedValue);

                        if (kmsProvider == "local") // the test description expects this assert only once for a local kms provider
                        {
                            coll = GetCollection(clientEncrypted, __collCollectionNamespace);
                            var exception = Record.Exception(() => Insert(coll, async, new BsonDocument("encrypted_placeholder", encryptedValue)));
                            exception.Should().BeOfType <MongoEncryptionException>();
                        }
                    }
        }
        public void BsonSizeLimitAndBatchSizeSplittingTest(
            [Values(false, true)] bool async)
        {
            RequireServer.Check().Supports(Feature.ClientSideEncryption);

            var eventCapturer = new EventCapturer().Capture <CommandStartedEvent>(e => e.CommandName == "insert");

            using (var client = ConfigureClient())
                using (var clientEncrypted = ConfigureClientEncrypted(kmsProviderFilter: "local", eventCapturer: eventCapturer))
                {
                    var collLimitSchema = JsonFileReader.Instance.Documents["limits.limits-schema.json"];
                    CreateCollection(client, __collCollectionNamespace, new BsonDocument("$jsonSchema", collLimitSchema));
                    var datakeysLimitsKey  = JsonFileReader.Instance.Documents["limits.limits-key.json"];
                    var keyVaultCollection = GetCollection(client, __keyVaultCollectionNamespace);
                    Insert(keyVaultCollection, async, datakeysLimitsKey);

                    var coll = GetCollection(clientEncrypted, __collCollectionNamespace);

                    var exception = Record.Exception(
                        () => Insert(
                            coll,
                            async,
                            new BsonDocument
                    {
                        { "_id", "no_encryption_under_2mib" },
                        { "unencrypted", new string('a', 2097152 - 1000) }
                    }));
                    exception.Should().BeNull();
                    eventCapturer.Clear();

                    exception = Record.Exception(
                        () => Insert(
                            coll,
                            async,
                            new BsonDocument
                    {
                        { "_id", "no_encryption_over_2mib" },
                        { "unencrypted", new string('a', 2097152) }
                    }));
                    exception.Should().NotBeNull();
                    eventCapturer.Clear();

                    var limitsDoc = JsonFileReader.Instance.Documents["limits.limits-doc.json"];
                    limitsDoc.AddRange(
                        new BsonDocument
                    {
                        { "_id", "encryption_exceeds_2mib" },
                        { "unencrypted", new string('a', 2097152 - 2000) }
                    });
                    exception = Record.Exception(
                        () => Insert(
                            coll,
                            async,
                            limitsDoc));
                    exception.Should().BeNull();
                    eventCapturer.Clear();

                    exception = Record.Exception(
                        () => Insert(
                            coll,
                            async,
                            new BsonDocument
                    {
                        { "_id", "no_encryption_under_2mib_1" },
                        { "unencrypted", new string('a', 2097152 - 1000) }
                    },
                            new BsonDocument
                    {
                        { "_id", "no_encryption_under_2mib_2" },
                        { "unencrypted", new string('a', 2097152 - 1000) }
                    }));
                    exception.Should().BeNull();
                    eventCapturer.Count.Should().Be(2);
                    eventCapturer.Clear();

                    var limitsDoc1 = JsonFileReader.Instance.Documents["limits.limits-doc.json"];
                    limitsDoc1.AddRange(
                        new BsonDocument
                    {
                        { "_id", "encryption_exceeds_2mib_1" },
                        { "unencrypted", new string('a', 2097152 - 2000) }
                    });
                    var limitsDoc2 = JsonFileReader.Instance.Documents["limits.limits-doc.json"];
                    limitsDoc2.AddRange(
                        new BsonDocument
                    {
                        { "_id", "encryption_exceeds_2mib_2" },
                        { "unencrypted", new string('a', 2097152 - 2000) }
                    });
                    exception = Record.Exception(
                        () => Insert(
                            coll,
                            async,
                            limitsDoc1,
                            limitsDoc2));
                    exception.Should().BeNull();
                    eventCapturer.Count.Should().Be(2);
                    eventCapturer.Clear();
                }
        }
        public void Example1()
        {
            RequireServer.Check().ClusterTypes(ClusterType.ReplicaSet, ClusterType.Sharded).Supports(Feature.Transactions);

            var connectionString = CoreTestConfiguration.ConnectionString.ToString();

            DropCollections(
                connectionString,
                CollectionNamespace.FromFullName("mydb1.foo"),
                CollectionNamespace.FromFullName("mydb2.bar"));
            string result = null;

            // Start Transactions withTxn API Example 1
            // For a replica set, include the replica set name and a seedlist of the members in the URI string; e.g.
            // string uri = "mongodb://mongodb0.example.com:27017,mongodb1.example.com:27017/?replicaSet=myRepl";
            // For a sharded cluster, connect to the mongos instances; e.g.
            // string uri = "mongodb://mongos0.example.com:27017,mongos1.example.com:27017/";
            var client = new MongoClient(connectionString);

            // Prereq: Create collections.
            var database1   = client.GetDatabase("mydb1");
            var collection1 = database1.GetCollection <BsonDocument>("foo").WithWriteConcern(WriteConcern.WMajority);

            collection1.InsertOne(new BsonDocument("abc", 0));

            var database2   = client.GetDatabase("mydb2");
            var collection2 = database2.GetCollection <BsonDocument>("bar").WithWriteConcern(WriteConcern.WMajority);

            collection2.InsertOne(new BsonDocument("xyz", 0));

            // Step 1: Start a client session.
            using (var session = client.StartSession())
            {
                // Step 2: Optional. Define options to use for the transaction.
                var transactionOptions = new TransactionOptions(
                    readPreference: ReadPreference.Primary,
                    readConcern: ReadConcern.Local,
                    writeConcern: WriteConcern.WMajority);

                // Step 3: Define the sequence of operations to perform inside the transactions
                var cancellationToken = CancellationToken.None; // normally a real token would be used
                result = session.WithTransaction(
                    (s, ct) =>
                {
                    collection1.InsertOne(s, new BsonDocument("abc", 1), cancellationToken: ct);
                    collection2.InsertOne(s, new BsonDocument("xyz", 999), cancellationToken: ct);
                    return("Inserted into collections in different databases");
                },
                    transactionOptions,
                    cancellationToken);
            }
            //End Transactions withTxn API Example 1

            result.Should().Be("Inserted into collections in different databases");

            var collection1Documents = collection1.Find(FilterDefinition <BsonDocument> .Empty).ToList();

            collection1Documents.Count.Should().Be(2);
            collection1Documents[0]["abc"].Should().Be(0);
            collection1Documents[1]["abc"].Should().Be(1);

            var collection2Documents = collection2.Find(FilterDefinition <BsonDocument> .Empty).ToList();

            collection2Documents.Count.Should().Be(2);
            collection2Documents[0]["xyz"].Should().Be(0);
            collection2Documents[1]["xyz"].Should().Be(999);
        }
        public void CorpusTest(
            [Values(false, true)] bool useLocalSchema,
            [Values(false, true)] bool async)
        {
            RequireServer.Check().Supports(Feature.ClientSideEncryption);

            var corpusSchema = JsonFileReader.Instance.Documents["corpus.corpus-schema.json"];
            var schemaMap    = useLocalSchema ? new BsonDocument("db.coll", corpusSchema) : null;

            using (var client = ConfigureClient())
                using (var clientEncrypted = ConfigureClientEncrypted(schemaMap))
                    using (var clientEncryption = ConfigureClientEncryption(clientEncrypted.Wrapped as MongoClient))
                    {
                        CreateCollection(client, __collCollectionNamespace, new BsonDocument("$jsonSchema", corpusSchema));

                        var corpusKeyLocal     = JsonFileReader.Instance.Documents["corpus.corpus-key-local.json"];
                        var corpusKeyAws       = JsonFileReader.Instance.Documents["corpus.corpus-key-aws.json"];
                        var keyVaultCollection = GetCollection(client, __keyVaultCollectionNamespace);
                        Insert(keyVaultCollection, async, corpusKeyLocal, corpusKeyAws);

                        var corpus       = JsonFileReader.Instance.Documents["corpus.corpus.json"];
                        var corpusCopied = new BsonDocument
                        {
                            corpus.GetElement("_id"),
                            corpus.GetElement("altname_aws"),
                            corpus.GetElement("altname_local")
                        };

                        foreach (var corpusElement in corpus.Elements.Where(c => c.Value.IsBsonDocument))
                        {
                            var corpusValue = corpusElement.Value.DeepClone();
                            var kms         = corpusValue["kms"].AsString;
                            var abbreviatedAlgorithmName = corpusValue["algo"].AsString;
                            var identifier = corpusValue["identifier"].AsString;
                            var allowed    = corpusValue["allowed"].ToBoolean();
                            var value      = corpusValue["value"];
                            var method     = corpusValue["method"].AsString;
                            switch (method)
                            {
                            case "auto":
                                corpusCopied.Add(corpusElement);
                                continue;

                            case "explicit":
                            {
                                var            encryptionOptions = CreateEncryptOptions(abbreviatedAlgorithmName, identifier, kms);
                                BsonBinaryData encrypted         = null;
                                var            exception         = Record.Exception(() =>
                                    {
                                        encrypted = ExplicitEncrypt(
                                            clientEncryption,
                                            encryptionOptions,
                                            value,
                                            async);
                                    });
                                if (allowed)
                                {
                                    exception.Should().BeNull();
                                    encrypted.Should().NotBeNull();
                                    corpusValue["value"] = encrypted;
                                }
                                else
                                {
                                    exception.Should().NotBeNull();
                                }
                                corpusCopied.Add(new BsonElement(corpusElement.Name, corpusValue));
                            }
                            break;

                            default:
                                throw new ArgumentException($"Unsupported method name {method}.", nameof(method));
                            }
                        }

                        var coll = GetCollection(clientEncrypted, __collCollectionNamespace);
                        Insert(coll, async, corpusCopied);

                        var corpusDecrypted = Find(coll, new BsonDocument(), async).Single();
                        corpusDecrypted.Should().Be(corpus);

                        var corpusEncryptedExpected = JsonFileReader.Instance.Documents["corpus.corpus-encrypted.json"];
                        coll = GetCollection(client, __collCollectionNamespace);
                        var corpusEncryptedActual = Find(coll, new BsonDocument(), async).Single();
                        foreach (var expectedElement in corpusEncryptedExpected.Elements.Where(c => c.Value.IsBsonDocument))
                        {
                            var expectedElementValue = expectedElement.Value;
                            var expectedAlgorithm    = ParseAlgorithm(expectedElementValue["algo"].AsString);
                            var expectedAllowed      = expectedElementValue["allowed"].ToBoolean();
                            var expectedValue        = expectedElementValue["value"];
                            var actualValue          = corpusEncryptedActual.GetValue(expectedElement.Name)["value"];

                            switch (expectedAlgorithm)
                            {
                            case EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic:
                                actualValue.Should().Be(expectedValue);
                                break;

                            case EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random:
                                if (expectedAllowed)
                                {
                                    actualValue.Should().NotBe(expectedValue);
                                }
                                break;

                            default:
                                throw new ArgumentException($"Unsupported expected algorithm {expectedAllowed}.", nameof(expectedAlgorithm));
                            }

                            if (expectedAllowed)
                            {
                                var actualDecryptedValue   = ExplicitDecrypt(clientEncryption, actualValue.AsBsonBinaryData, async);
                                var expectedDecryptedValue = ExplicitDecrypt(clientEncryption, expectedValue.AsBsonBinaryData, async);
                                actualDecryptedValue.Should().Be(expectedDecryptedValue);
                            }
                            else
                            {
                                actualValue.Should().Be(expectedValue);
                            }
                        }
                    }

            EncryptOptions CreateEncryptOptions(string algorithm, string identifier, string kms)
            {
                Guid?  keyId         = null;
                string alternateName = null;

                if (identifier == "id")
                {
                    switch (kms)
                    {
                    case "local":
                        keyId = GuidConverter.FromBytes(Convert.FromBase64String("LOCALAAAAAAAAAAAAAAAAA=="), GuidRepresentation.Standard);
                        break;

                    case "aws":
                        keyId = GuidConverter.FromBytes(Convert.FromBase64String("AWSAAAAAAAAAAAAAAAAAAA=="), GuidRepresentation.Standard);
                        break;

                    default:
                        throw new ArgumentException($"Unsupported kms type {kms}.");
                    }
                }
                else if (identifier == "altname")
                {
                    alternateName = kms;
                }
                else
                {
                    throw new ArgumentException($"Unsupported identifier {identifier}.", nameof(identifier));
                }

                return(new EncryptOptions(ParseAlgorithm(algorithm).ToString(), alternateName, keyId));
            }

            EncryptionAlgorithm ParseAlgorithm(string algorithm)
            {
                switch (algorithm)
                {
                case "rand":
                    return(EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random);

                case "det":
                    return(EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic);

                default:
                    throw new ArgumentException($"Unsupported algorithm {algorithm}.");
                }
            }
        }
Beispiel #6
0
        public void Lookup_with_let_and_mismatched_pipeline_condition_should_return_the_expected_result()
        {
            RequireServer.Check().Supports(Feature.AggregateLet);

            string databaseName             = "test";
            string ordersCollectionName     = "orders";
            string warehousesCollectionName = "warehouses";

            var client = CreateClient();

            DropCollection(client, databaseName, ordersCollectionName);
            DropCollection(client, databaseName, warehousesCollectionName);

            var ordersCollection     = client.GetDatabase(databaseName).GetCollection <Order>(ordersCollectionName);
            var warehousesCollection = client.GetDatabase(databaseName).GetCollection <Warehouse>(warehousesCollectionName);

            var orderDocuments = new[]
            {
                new Order {
                    Item = "almonds", Price = 12, Ordered = 2
                },
                new Order {
                    Item = "pecans", Price = 20, Ordered = 1
                },
                new Order {
                    Item = "cookies", Price = 10, Ordered = 60
                }
            };

            ordersCollection.InsertMany(orderDocuments);

            var warehouseDocuments = new[]
            {
                new Warehouse {
                    StockItem = "almonds", Instock = 120
                },
                new Warehouse {
                    StockItem = "pecans", Instock = 80
                },
                new Warehouse {
                    StockItem = "almonds", Instock = 60
                },
                new Warehouse {
                    StockItem = "cookies", Instock = 40
                },
                new Warehouse {
                    StockItem = "cookies", Instock = 80
                },
            };

            warehousesCollection.InsertMany(warehouseDocuments);

            var lookupPipeline = new EmptyPipelineDefinition <Warehouse>()
                                 .Match(new BsonDocument("$expr",
                                                         new BsonDocument("$and", new BsonArray
            {
                new BsonDocument("$eq", new BsonArray {
                    "$stock_item", "not_exist_item"
                }),
            })))
                                 .Project <Warehouse, Warehouse, StockData>(
                Builders <Warehouse> .Projection
                .Exclude(warehouses => warehouses.StockItem)
                .Exclude(warehouses => warehouses.Id));

            var result = ordersCollection
                         .Aggregate()
                         .Lookup(
                warehousesCollection,
                new BsonDocument {
                { "order_item", "$item" }, { "order_qty", "$ordered" }
            },
                lookupPipeline,
                new ExpressionFieldDefinition <Order, IEnumerable <StockData> >(order => order.StockData))
                         .ToList()
                         .Select(item =>
            {
                var document = item.ToBsonDocument();
                document.Remove("_id");
                return(document);
            })
                         .ToList();

            result.Count.Should().Be(3);
            result[0].Should().Be("{ 'item' : 'almonds', 'price' : 12, 'ordered' : 2, 'stockdata' : [] }");
            result[1].Should().Be("{ 'item' : 'pecans', 'price' : 20, 'ordered' : 1, 'stockdata' : [] }");
            result[2].Should().Be("{ 'item' : 'cookies', 'price' : 10, 'ordered' : 60, 'stockdata' : [] }");
        }
        // private methods
        private void Run(BsonDocument shared, BsonDocument test)
        {
            JsonDrivenHelper.EnsureAllFieldsAreValid(shared, "_path", "database_name", "database2_name", "collection_name", "collection2_name", "tests");
            JsonDrivenHelper.EnsureAllFieldsAreValid(test, "description", "minServerVersion", "maxServerVersion", "topology", "target", "changeStreamPipeline", "changeStreamOptions", "operations", "expectations", "result", "async", "failPoint");

            RequireServer.Check().RunOn(EmulateRunOn());

            _databaseName    = shared["database_name"].AsString;
            _database2Name   = shared.GetValue("database2_name", null)?.AsString;
            _collectionName  = shared["collection_name"].AsString;
            _collection2Name = shared.GetValue("collection2_name", null)?.AsString;

            CreateCollections();

            List <ChangeStreamDocument <BsonDocument> > actualResult = null;
            Exception actualException = null;
            List <CommandStartedEvent> actualEvents = null;

            var eventCapturer = CreateEventCapturer();

            using (ConfigureFailPoint(test))
                using (var client = CreateDisposableClient(eventCapturer))
                {
                    try
                    {
                        var async = test["async"].AsBoolean;
                        using (var cursor = Watch(client, test, async))
                        {
                            var globalClient = DriverTestConfiguration.Client;
                            ExecuteOperations(globalClient, test["operations"].AsBsonArray);

                            actualResult = ReadChangeStreamDocuments(cursor, test, async);
                            actualEvents = GetEvents(eventCapturer);
                        }
                    }
                    catch (Exception exception)
                    {
                        actualException = exception;
                    }
                }

            if (test.Contains("expectations") && actualEvents != null)
            {
                var expectedEvents = test["expectations"].AsBsonArray.Cast <BsonDocument>().ToList();
                AssertEvents(actualEvents, expectedEvents);
            }

            if (test.Contains("result"))
            {
                var expectedResult = test["result"].AsBsonDocument;
                AssertResult(actualResult, actualException, expectedResult);
            }

            BsonArray EmulateRunOn()
            {
                var condition = new BsonDocument();

                if (test.TryGetElement("minServerVersion", out var minServerVersion))
                {
                    condition.Add(minServerVersion);
                }
                if (test.TryGetElement("maxServerVersion", out var maxServerVersion))
                {
                    condition.Add(maxServerVersion);
                }
                if (test.TryGetElement("topology", out var topology))
                {
                    condition.Add(topology);
                }

                return(new BsonArray {
                    condition
                });
            }
        }
        public void Supported_single_statement_writes_should_have_transaction_id(
            [Values("insertOne", "updateOne", "replaceOne", "deleteOne", "findOneAndDelete", "findOneAndReplace", "findOneAndUpdate")] string operation,
            [Values(false, true)] bool async)
        {
            RequireServer.Check().VersionGreaterThanOrEqualTo("3.6.0").ClusterTypes(ClusterType.ReplicaSet, ClusterType.Sharded);

            DropCollection();
            var eventCapturer = CreateEventCapturer();

            using (var client = CreateDisposableClient(eventCapturer))
            {
                var database   = client.GetDatabase(_databaseName);
                var collection = database.GetCollection <BsonDocument>(_collectionName);

                switch (operation)
                {
                case "deleteOne":
                    var deleteOneFilter = Builders <BsonDocument> .Filter.Eq("_id", 1);

                    if (async)
                    {
                        collection.DeleteOneAsync(deleteOneFilter).GetAwaiter().GetResult();
                    }
                    else
                    {
                        collection.DeleteOne(deleteOneFilter);
                    }
                    break;

                case "findOneAndDelete":
                    var findOneAndDeleteFilter = Builders <BsonDocument> .Filter.Eq("_id", 1);

                    if (async)
                    {
                        collection.FindOneAndDeleteAsync(findOneAndDeleteFilter).GetAwaiter().GetResult();
                    }
                    else
                    {
                        collection.FindOneAndDelete(findOneAndDeleteFilter);
                    }
                    break;

                case "findOneAndReplace":
                    var findOneAndReplaceFilter = Builders <BsonDocument> .Filter.Eq("_id", 1);

                    var findOneAndReplaceReplacement = new BsonDocument("_id", 1);
                    if (async)
                    {
                        collection.FindOneAndReplaceAsync(findOneAndReplaceFilter, findOneAndReplaceReplacement).GetAwaiter().GetResult();
                    }
                    else
                    {
                        collection.FindOneAndReplace(findOneAndReplaceFilter, findOneAndReplaceReplacement);
                    }
                    break;

                case "findOneAndUpdate":
                    var findOneAndUpdateFilter = Builders <BsonDocument> .Filter.Eq("_id", 1);

                    var findOneAndUpdateUpdate = Builders <BsonDocument> .Update.Set("x", 2);

                    if (async)
                    {
                        collection.FindOneAndUpdateAsync(findOneAndUpdateFilter, findOneAndUpdateUpdate).GetAwaiter().GetResult();
                    }
                    else
                    {
                        collection.FindOneAndUpdate(findOneAndUpdateFilter, findOneAndUpdateUpdate);
                    }
                    break;

                case "insertOne":
                    var document = new BsonDocument("_id", 1);
                    if (async)
                    {
                        collection.InsertOneAsync(document).GetAwaiter().GetResult();
                    }
                    else
                    {
                        collection.InsertOne(document);
                    }
                    break;

                case "replaceOne":
                    var replaceOneFilter = Builders <BsonDocument> .Filter.Eq("_id", 1);

                    var replacement = new BsonDocument("_id", 1);
                    if (async)
                    {
                        collection.ReplaceOneAsync(replaceOneFilter, replacement).GetAwaiter().GetResult();
                    }
                    else
                    {
                        collection.ReplaceOne(replaceOneFilter, replacement);
                    }
                    break;

                case "updateOne":
                    var updateOneFilter = Builders <BsonDocument> .Filter.Eq("_id", 1);

                    var updateOne = Builders <BsonDocument> .Update.Set("x", 2);

                    if (async)
                    {
                        collection.UpdateOneAsync(updateOneFilter, updateOne).GetAwaiter().GetResult();
                    }
                    else
                    {
                        collection.UpdateOne(updateOneFilter, updateOne);
                    }
                    break;

                default:
                    throw new Exception($"Unexpected operation: {operation}.");
                }

                AssertCommandHasTransactionId(eventCapturer);
            }
        }
        public void Unacknowledged_writes_should_not_have_transaction_id(
            [Values("delete", "insert", "update")] string operation,
            [Values(false, true)] bool async)
        {
            RequireServer.Check().ClusterTypes(ClusterType.ReplicaSet, ClusterType.Sharded);

            DropCollection();
            var eventCapturer = CreateEventCapturer();

            using (var client = CreateDisposableClient(eventCapturer))
            {
                var database   = client.GetDatabase(_databaseName);
                var collection = database.GetCollection <BsonDocument>(_collectionName).WithWriteConcern(WriteConcern.Unacknowledged);

                switch (operation)
                {
                case "delete":
                    var deleteFilter = Builders <BsonDocument> .Filter.Eq("_id", 1);

                    if (async)
                    {
                        collection.DeleteOneAsync(deleteFilter).GetAwaiter().GetResult();
                    }
                    else
                    {
                        collection.DeleteOne(deleteFilter);
                    }
                    break;

                case "insert":
                    var document = new BsonDocument("_id", 1);
                    if (async)
                    {
                        collection.InsertOneAsync(document).GetAwaiter().GetResult();;
                    }
                    else
                    {
                        collection.InsertOne(document);
                    }
                    SpinUntilCollectionIsNotEmpty();     // wait for unacknowledged insert to complete so it won't execute later while another test is running
                    break;

                case "update":
                    var updateFilter = Builders <BsonDocument> .Filter.Eq("_id", 1);

                    var update = Builders <BsonDocument> .Update.Set("x", 1);

                    if (async)
                    {
                        collection.UpdateOneAsync(updateFilter, update).GetAwaiter().GetResult();
                    }
                    else
                    {
                        collection.UpdateOne(updateFilter, update);
                    }
                    break;

                default:
                    throw new Exception($"Unexpected operation: {operation}.");
                }

                AssertCommandDoesNotHaveTransactionId(eventCapturer);
            }
        }
Beispiel #10
0
        public void CustomEndpointTest([Values(false, true)] bool async)
        {
            RequireServer.Check().Supports(Feature.ClientSideEncryption);

            using (var client = ConfigureClient())
                using (var clientEncryption = ConfigureClientEncryption(client.Wrapped as MongoClient))
                {
                    var testCaseMasterKey = new BsonDocument
                    {
                        { "region", "us-east-1" },
                        { "key", "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" }
                    };
                    TestCase(testCaseMasterKey);

                    testCaseMasterKey = new BsonDocument
                    {
                        { "region", "us-east-1" },
                        { "key", "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" },
                        { "endpoint", "kms.us-east-1.amazonaws.com" }
                    };
                    TestCase(testCaseMasterKey);

                    testCaseMasterKey = new BsonDocument
                    {
                        { "region", "us-east-1" },
                        { "key", "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" },
                        { "endpoint", "kms.us-east-1.amazonaws.com:443" }
                    };
                    TestCase(testCaseMasterKey);

                    testCaseMasterKey = new BsonDocument
                    {
                        { "region", "us-east-1" },
                        { "key", "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" },
                        { "endpoint", "kms.us-east-1.amazonaws.com:12345" }
                    };
                    var exception = Record.Exception(() => TestCase(testCaseMasterKey));
                    exception.InnerException.Should().BeAssignableTo <SocketException>();

                    testCaseMasterKey = new BsonDocument
                    {
                        { "region", "us-east-1" },
                        { "key", "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" },
                        { "endpoint", "kms.us-east-2.amazonaws.com" }
                    };
                    exception = Record.Exception(() => TestCase(testCaseMasterKey));
                    exception.Should().NotBeNull();
                    exception.Message.Should().Contain("us-east-1");

                    testCaseMasterKey = new BsonDocument
                    {
                        { "region", "us-east-1" },
                        { "key", "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" },
                        { "endpoint", "example.com" }
                    };
                    exception = Record.Exception(() => TestCase(testCaseMasterKey));
                    exception.Should().NotBeNull();
                    exception.Message.Should().Contain("parse error");

                    // additional not spec tests
                    testCaseMasterKey = new BsonDocument
                    {
                        { "region", "us-east-1" },
                        { "key", "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" },
                        { "endpoint", "$test$" }
                    };
                    exception = Record.Exception(() => TestCase(testCaseMasterKey));
                    exception.Should().NotBeNull();
                    exception.InnerException.Should().BeAssignableTo <SocketException>();

                    void TestCase(BsonDocument masterKey)
                    {
                        var dataKeyOptions = new DataKeyOptions(masterKey: masterKey);
                        var dataKey        = CreateDataKey(clientEncryption, "aws", dataKeyOptions, async);

                        var encryptOptions = new EncryptOptions(
                            algorithm: EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic.ToString(),
                            keyId: dataKey);
                        var value     = "test";
                        var encrypted = ExplicitEncrypt(clientEncryption, encryptOptions, value, async);
                        var decrypted = ExplicitDecrypt(clientEncryption, encrypted, async);

                        decrypted.Should().Be(BsonValue.Create(value));
                    }
                }
        }
        // private methods
        private void Run(BsonDocument shared, BsonDocument test)
        {
            JsonDrivenHelper.EnsureAllFieldsAreValid(shared, "_path", "database_name", "database2_name", "collection_name", "collection2_name", "tests");
            JsonDrivenHelper.EnsureAllFieldsAreValid(test, "description", "minServerVersion", "topology", "target", "changeStreamPipeline", "changeStreamOptions", "operations", "expectations", "result", "async");

            if (test.Contains("minServerVersion"))
            {
                var minServerVersion = test["minServerVersion"].AsString;
                RequireServer.Check().VersionGreaterThanOrEqualTo(minServerVersion);
            }

            if (test.Contains("topology"))
            {
                var clusterTypes = MapTopologyToClusterTypes(test["topology"].AsBsonArray);
                RequireServer.Check().ClusterTypes(clusterTypes);
            }

            _databaseName    = shared["database_name"].AsString;
            _database2Name   = shared["database2_name"].AsString;
            _collectionName  = shared["collection_name"].AsString;
            _collection2Name = shared["collection2_name"].AsString;

            CreateCollections();

            List <ChangeStreamDocument <BsonDocument> > actualResult = null;
            Exception actualException = null;
            List <CommandStartedEvent> actualEvents = null;

            var eventCapturer = CreateEventCapturer();

            using (var client = CreateDisposableClient(eventCapturer))
            {
                try
                {
                    var async = test["async"].AsBoolean;
                    using (var cursor = Watch(client, test, async))
                    {
                        var globalClient = DriverTestConfiguration.Client;
                        ExecuteOperations(globalClient, test["operations"].AsBsonArray);

                        actualResult = ReadChangeStreamDocuments(cursor, test, async);
                        actualEvents = GetEvents(eventCapturer);
                    }
                }
                catch (Exception exception)
                {
                    actualException = exception;
                }
            }

            if (test.Contains("expectations") && actualEvents != null)
            {
                var expectedEvents = test["expectations"].AsBsonArray.Cast <BsonDocument>().ToList();
                AssertEvents(actualEvents, expectedEvents);
            }

            if (test.Contains("result"))
            {
                var expectedResult = test["result"].AsBsonDocument;
                AssertResult(actualResult, actualException, expectedResult);
            }
        }
        public void Connection_pool_should_not_be_cleared_when_replSetStepDown_and_GetMore([Values(false, true)] bool async)
        {
            RequireServer.Check().Supports(Feature.KeepConnectionPoolWhenReplSetStepDown).ClusterType(ClusterType.ReplicaSet);

            var eventCapturer = new EventCapturer().Capture <ConnectionPoolRemovedConnectionEvent>();

            using (var client = CreateDisposableClient(eventCapturer))
            {
                var database = client.GetDatabase(_databaseName, new MongoDatabaseSettings {
                    WriteConcern = WriteConcern.WMajority
                });
                database.DropCollection(_databaseName);
                var collection = database.GetCollection <BsonDocument>(_collectionName, new MongoCollectionSettings {
                    WriteConcern = WriteConcern.WMajority
                });
                var adminDatabase = client.GetDatabase("admin").WithWriteConcern(WriteConcern.W1);

                collection.InsertMany(
                    new[]
                {
                    new BsonDocument("x", 1),
                    new BsonDocument("x", 2),
                    new BsonDocument("x", 3),
                    new BsonDocument("x", 4),
                    new BsonDocument("x", 5),
                });
                eventCapturer.Clear();

                var cursor = collection.FindSync(FilterDefinition <BsonDocument> .Empty, new FindOptions <BsonDocument> {
                    BatchSize = 2
                });
                cursor.MoveNext();

                foreach (var secondary in client.Cluster.Description.Servers.Where(c => c.Type == ServerType.ReplicaSetSecondary))
                {
                    RunOnSecondary(client, secondary.EndPoint, BsonDocument.Parse("{ replSetFreeze : 0 }"));
                }

                var          replSetStepDownCommand = BsonDocument.Parse("{ replSetStepDown : 20, force : true }");
                BsonDocument replSetStepDownResult;
                if (async)
                {
                    replSetStepDownResult = adminDatabase.RunCommandAsync <BsonDocument>(replSetStepDownCommand).GetAwaiter().GetResult();
                }
                else
                {
                    replSetStepDownResult = adminDatabase.RunCommand <BsonDocument>(replSetStepDownCommand);
                }

                replSetStepDownResult.Should().NotBeNull();
                replSetStepDownResult.GetValue("ok", false).ToBoolean().Should().BeTrue();

                cursor.MoveNext();

                eventCapturer.Events.Should().BeEmpty();
            }

            void RunOnSecondary(IMongoClient primaryClient, EndPoint secondaryEndpoint, BsonDocument command)
            {
                var secondarySettings = primaryClient.Settings.Clone();

                secondarySettings.ClusterConfigurator = null;
#pragma warning disable CS0618 // Type or member is obsolete
                secondarySettings.ConnectionMode = ConnectionMode.Direct;
#pragma warning restore CS0618 // Type or member is obsolete
                var secondaryDnsEndpoint = (DnsEndPoint)secondaryEndpoint;
                secondarySettings.Server = new MongoServerAddress(secondaryDnsEndpoint.Host, secondaryDnsEndpoint.Port);
                using (var secondaryClient = DriverTestConfiguration.CreateDisposableClient(secondarySettings))
                {
                    var adminDatabase = secondaryClient.GetDatabase(DatabaseNamespace.Admin.DatabaseName);
                    adminDatabase.RunCommand <BsonDocument>(command);
                }
            }
        }
 private void RequireSupportForRetryableWrites()
 {
     RequireServer.Check().ClusterTypes(ClusterType.Sharded, ClusterType.ReplicaSet);
 }
Beispiel #14
0
        public void GraphLookup_untyped_based_should_return_expected_result()
        {
            RequireServer.Check().Supports(Feature.AggregateGraphLookupStage);
            EnsureTestData();
            var subject = __employeesCollection.Aggregate();

            var result = subject
                         .GraphLookup(__employeesCollection, "reportsTo", "name", "$reportsTo", "reportingHierarchy")
                         .ToList();

            var comparer = new EmployeeWithReportingHierarchyBsonDocumentEqualityComparer();

            result.WithComparer(comparer).Should().Equal(
                BsonDocument.Parse(@"{ _id : 1, name : 'Dev', reportingHierarchy : [ ] }"),
                BsonDocument.Parse(@"{
                   _id : 2,
                   name : 'Eliot',
                   reportsTo : 'Dev',
                   reportingHierarchy : [
                      { _id : 1, name : 'Dev' }
                   ]
                }"),
                BsonDocument.Parse(@"{
                   _id : 3,
                   name : 'Ron',
                   reportsTo : 'Eliot',
                   reportingHierarchy : [
                      { _id : 1, name : 'Dev' },
                      { _id : 2, name : 'Eliot', reportsTo : 'Dev' }
                   ]
                }"),
                BsonDocument.Parse(@"{
                   _id : 4,
                   name : 'Andrew',
                   reportsTo : 'Eliot',
                   reportingHierarchy : [
                      { _id : 1, name : 'Dev' },
                      { _id : 2, name : 'Eliot', reportsTo : 'Dev' }
                   ]
                }"),
                BsonDocument.Parse(@"{
                   _id : 5,
                   name : 'Asya',
                   reportsTo : 'Ron',
                   reportingHierarchy : [
                      { _id : 1, name : 'Dev' },
                      { _id : 2, name : 'Eliot', reportsTo : 'Dev' },
                      { _id : 3, name : 'Ron', reportsTo : 'Eliot' }
                   ]
                }"),
                BsonDocument.Parse(@"{
                   _id : 6,
                   name : 'Dan',
                   reportsTo : 'Andrew',
                   reportingHierarchy : [
                      { _id : 1, name : 'Dev' },
                      { _id : 2, name : 'Eliot', reportsTo : 'Dev' },
                      { _id : 4, name : 'Andrew', reportsTo : 'Eliot' }
                   ]
                }"));
        }
        private (DisposableMongoClient Client, Dictionary <string, EventCapturer> ClientEventCapturers) CreateClient(BsonDocument entity)
        {
            string appName = null;
            var    clientEventCapturers       = new Dictionary <string, EventCapturer>();
            string clientId                   = null;
            var    commandNamesToSkipInEvents = new List <string>();
            List <(string Key, IEnumerable <string> Events, List <string> CommandNotToCapture)> eventTypesToCapture = new ();
            bool?    loadBalanced             = null;
            int?     maxPoolSize              = null;
            bool?    observeSensitiveCommands = null;
            var      readConcern              = ReadConcern.Default;
            var      retryReads              = true;
            var      retryWrites             = true;
            var      useMultipleShardRouters = false;
            TimeSpan?waitQueueTimeout        = null;
            var      writeConcern            = WriteConcern.Acknowledged;
            var      serverApi = CoreTestConfiguration.ServerApi;

            foreach (var element in entity)
            {
                switch (element.Name)
                {
                case "id":
                    clientId = element.Value.AsString;
                    break;

                case "uriOptions":
                    foreach (var option in element.Value.AsBsonDocument)
                    {
                        switch (option.Name)
                        {
                        case "appname":
                            appName = option.Value.ToString();
                            break;

                        case "loadBalanced":
                            loadBalanced = option.Value.ToBoolean();
                            break;

                        case "maxPoolSize":
                            maxPoolSize = option.Value.ToInt32();
                            break;

                        case "retryWrites":
                            retryWrites = option.Value.AsBoolean;
                            break;

                        case "retryReads":
                            retryReads = option.Value.AsBoolean;
                            break;

                        case "readConcernLevel":
                            var levelValue = option.Value.AsString;
                            var level      = (ReadConcernLevel)Enum.Parse(typeof(ReadConcernLevel), levelValue, true);
                            readConcern = new ReadConcern(level);
                            break;

                        case "w":
                            writeConcern = new WriteConcern(option.Value.AsInt32);
                            break;

                        case "waitQueueTimeoutMS":
                            waitQueueTimeout = TimeSpan.FromMilliseconds(option.Value.ToInt32());
                            break;

                        default:
                            throw new FormatException($"Invalid client uriOption argument name: '{option.Name}'.");
                        }
                    }
                    break;

                case "useMultipleMongoses":
                    useMultipleShardRouters = element.Value.AsBoolean;
                    RequireServer.Check().MultipleMongosesIfSharded(required: useMultipleShardRouters);
                    break;

                case "observeEvents":
                    var observeEvents = element.Value.AsBsonArray.Select(x => x.AsString);
                    eventTypesToCapture.Add(
                        (Key: Ensure.IsNotNull(clientId, nameof(clientId)),
                         Events: observeEvents,
                         CommandNotToCapture: commandNamesToSkipInEvents));
                    break;

                case "observeSensitiveCommands":
                    observeSensitiveCommands = element.Value.AsBoolean;
                    break;

                case "ignoreCommandMonitoringEvents":
                    commandNamesToSkipInEvents.AddRange(element.Value.AsBsonArray.Select(x => x.AsString));
                    break;

                case "serverApi":
                    ServerApiVersion serverApiVersion           = null;
                    bool?            serverApiStrict            = null;
                    bool?            serverApiDeprecationErrors = null;
                    foreach (var option in element.Value.AsBsonDocument)
                    {
                        switch (option.Name)
                        {
                        case "version":
                            var serverApiVersionString = option.Value.AsString;
                            switch (serverApiVersionString)
                            {
                            case "1":
                                serverApiVersion = ServerApiVersion.V1;
                                break;

                            default:
                                throw new FormatException($"Invalid serverApi version: '{serverApiVersionString}'.");
                            }
                            break;

                        case "strict":
                            serverApiStrict = option.Value.AsBoolean;
                            break;

                        case "deprecationErrors":
                            serverApiDeprecationErrors = option.Value.AsBoolean;
                            break;

                        default:
                            throw new FormatException($"Invalid client serverApi argument name: '{option.Name}'.");
                        }
                    }
                    if (serverApiVersion != null)
                    {
                        serverApi = new ServerApi(serverApiVersion, serverApiStrict, serverApiDeprecationErrors);
                    }
                    break;

                case "storeEventsAsEntities":
                    var eventsBatches = element.Value.AsBsonArray;
                    foreach (var batch in eventsBatches.Cast <BsonDocument>())
                    {
                        var id     = batch["id"].AsString;
                        var events = batch["events"].AsBsonArray.Select(e => e.AsString);
                        eventTypesToCapture.Add((id, events, CommandNotToCapture: null));
                    }
                    break;

                default:
                    throw new FormatException($"Invalid client argument name: '{element.Name}'.");
                }
            }

            // Regardless of whether events are observed, we still need to track some info about the pool in order to implement
            // the assertNumberConnectionsCheckedOut operation
            if (eventTypesToCapture.Count == 0)
            {
                eventTypesToCapture.Add(
                    (Key: Ensure.IsNotNull(clientId, nameof(clientId)),
                     Events: new[] { "connectionCheckedInEvent", "connectionCheckedOutEvent" },
                     CommandNotToCapture: commandNamesToSkipInEvents));
            }

            var defaultCommandNamesToSkip = new List <string>
            {
                "configureFailPoint",
                "getLastError",
                OppressiveLanguageConstants.LegacyHelloCommandName,  // skip handshake events, should be reconsidered in the scope of CSHARP-3823
                "hello"
            };

            if (!observeSensitiveCommands.GetValueOrDefault())
            {
                defaultCommandNamesToSkip.AddRange(new[]
                {
                    "authenticate",
                    "getnonce",
                    "saslContinue",
                    "saslStart"
                });
            }

            foreach (var eventsDetails in eventTypesToCapture)
            {
                var commandNamesNotToCapture = Enumerable.Concat(eventsDetails.CommandNotToCapture ?? Enumerable.Empty <string>(), defaultCommandNamesToSkip);
                var formatter     = _eventFormatters.ContainsKey(eventsDetails.Key) ? _eventFormatters[eventsDetails.Key] : null;
                var eventCapturer = CreateEventCapturer(eventsDetails.Events, commandNamesNotToCapture, formatter);
                clientEventCapturers.Add(eventsDetails.Key, eventCapturer);
            }

            var eventCapturers = clientEventCapturers.Select(c => c.Value).ToArray();
            var client         = DriverTestConfiguration.CreateDisposableClient(
                settings =>
            {
                settings.ApplicationName       = appName;
                settings.LoadBalanced          = loadBalanced.GetValueOrDefault(defaultValue: settings.LoadBalanced);
                settings.MaxConnectionPoolSize = maxPoolSize.GetValueOrDefault(defaultValue: settings.MaxConnectionPoolSize);
                settings.RetryReads            = retryReads;
                settings.RetryWrites           = retryWrites;
                settings.ReadConcern           = readConcern;
                settings.WaitQueueTimeout      = waitQueueTimeout.GetValueOrDefault(defaultValue: settings.WaitQueueTimeout);
                settings.WriteConcern          = writeConcern;
                settings.HeartbeatInterval     = TimeSpan.FromMilliseconds(5); // the default value for spec tests
                settings.ServerApi             = serverApi;
                if (eventCapturers.Length > 0)
                {
                    settings.ClusterConfigurator = c =>
                    {
                        foreach (var eventCapturer in eventCapturers)
                        {
                            c.Subscribe(eventCapturer);
                        }
                    };
                }
            },
                _loggerFactory.CreateLogger <DisposableMongoClient>(),
                useMultipleShardRouters);

            return(client, clientEventCapturers);
        }
Beispiel #16
0
        public void Run(
            string schemaVersion,
            BsonArray testSetRunOnRequirements,
            BsonArray entities,
            BsonArray initialData,
            BsonArray runOnRequirements,
            string skipReason,
            BsonArray operations,
            BsonArray expectedEvents,
            BsonArray outcome,
            bool async)
        {
            if (_runHasBeenCalled)
            {
                throw new InvalidOperationException("The test suite has already been run.");
            }
            _runHasBeenCalled = true;

            var schemaSemanticVersion = SemanticVersion.Parse(schemaVersion);

            if (schemaSemanticVersion < new SemanticVersion(1, 0, 0) ||
                schemaSemanticVersion > new SemanticVersion(1, 7, 0))
            {
                throw new FormatException($"Schema version '{schemaVersion}' is not supported.");
            }
            if (testSetRunOnRequirements != null)
            {
                RequireServer.Check().RunOn(testSetRunOnRequirements);
            }
            if (runOnRequirements != null)
            {
                RequireServer.Check().RunOn(runOnRequirements);
            }
            if (skipReason != null)
            {
                throw new SkipException($"Test skipped because '{skipReason}'.");
            }

            KillOpenTransactions(DriverTestConfiguration.Client);

            _entityMap = new UnifiedEntityMapBuilder(_eventFormatters, _loggerFactory).Build(entities);

            if (initialData != null)
            {
                AddInitialData(DriverTestConfiguration.Client, initialData);
            }

            foreach (var operation in operations)
            {
                var cancellationToken = CancellationToken.None;
                CreateAndRunOperation(operation.AsBsonDocument, async, cancellationToken);
            }

            if (expectedEvents != null)
            {
                AssertEvents(expectedEvents, _entityMap);
            }
            if (outcome != null)
            {
                AssertOutcome(DriverTestConfiguration.Client, outcome);
            }
        }
Beispiel #17
0
        public void Lookup_with_let_and_bsondocuments_params_should_return_the_expected_result()
        {
            RequireServer.Check().Supports(Feature.AggregateLet);

            string databaseName             = "test";
            string ordersCollectionName     = "orders";
            string warehousesCollectionName = "warehouses";

            var client = CreateClient();

            DropCollection(client, databaseName, ordersCollectionName);
            DropCollection(client, databaseName, warehousesCollectionName);

            var ordersCollection     = client.GetDatabase(databaseName).GetCollection <BsonDocument>(ordersCollectionName);
            var warehousesCollection = client.GetDatabase(databaseName).GetCollection <BsonDocument>(warehousesCollectionName);

            var orderDocuments = new[]
            {
                new BsonDocument {
                    { "item", "almonds" }, { "price", 12 }, { "ordered", 2 }
                },
                new BsonDocument {
                    { "item", "pecans" }, { "price", 20 }, { "ordered", 1 }
                },
                new BsonDocument {
                    { "item", "cookies" }, { "price", 10 }, { "ordered", 60 }
                }
            };

            ordersCollection.InsertMany(orderDocuments);

            var warehouseDocuments = new[]
            {
                new BsonDocument {
                    { "stock_item", "almonds" }, { "instock", 120 }
                },
                new BsonDocument {
                    { "stock_item", "pecans" }, { "instock", 80 }
                },
                new BsonDocument {
                    { "stock_item", "almonds" }, { "instock", 60 }
                },
                new BsonDocument {
                    { "stock_item", "cookies" }, { "instock", 40 }
                },
                new BsonDocument {
                    { "stock_item", "cookies" }, { "instock", 80 }
                }
            };

            warehousesCollection.InsertMany(warehouseDocuments);

            var lookupPipeline = new EmptyPipelineDefinition <BsonDocument>()
                                 .Match(new BsonDocument("$expr",
                                                         new BsonDocument("$and", new BsonArray
            {
                new BsonDocument("$eq", new BsonArray {
                    "$stock_item", "$$order_item"
                }),
                new BsonDocument("$gte", new BsonArray {
                    "$instock", "$$order_qty"
                })
            })))
                                 .Project <BsonDocument, BsonDocument, BsonDocument>(
                Builders <BsonDocument> .Projection
                .Exclude("stock_item")
                .Exclude("_id"));

            var result = ordersCollection
                         .Aggregate()
                         .Lookup <BsonDocument, BsonDocument, IEnumerable <BsonDocument>, BsonDocument>(
                warehousesCollection,
                new BsonDocument {
                { "order_item", "$item" }, { "order_qty", "$ordered" }
            },
                lookupPipeline,
                "stockdata")
                         .ToList()
                         .Select(item =>
            {
                var document = item.ToBsonDocument();
                document.Remove("_id");
                return(document);
            })
                         .ToList();

            result.Count.Should().Be(3);
            result[0].Should().Be("{ 'item' : 'almonds', 'price' : 12, 'ordered' : 2, 'stockdata' : [{ 'instock' : 120 }, { 'instock' : 60 }] }");
            result[1].Should().Be("{ 'item' : 'pecans', 'price' : 20, 'ordered' : 1, 'stockdata' : [{ 'instock' : 80 }] }");
            result[2].Should().Be("{ 'item' : 'cookies', 'price' : 10, 'ordered' : 60, 'stockdata' : [{ 'instock' : 80 }] }");
        }
        public void ClientSideEncryptionAutoEncryptionSettingsTour()
        {
            RequireServer.Check().Supports(Feature.ClientSideEncryption);

            var localMasterKey = Convert.FromBase64String(LocalMasterKey);

            var kmsProviders = new Dictionary <string, IReadOnlyDictionary <string, object> >();
            var localKey     = new Dictionary <string, object>
            {
                { "key", localMasterKey }
            };

            kmsProviders.Add("local", localKey);

            var keyVaultNamespace        = CollectionNamespace.FromFullName("encryption.__keyVault");
            var keyVaultMongoClient      = new MongoClient();
            var clientEncryptionSettings = new ClientEncryptionOptions(
                keyVaultMongoClient,
                keyVaultNamespace,
                kmsProviders);

            var clientEncryption = new ClientEncryption(clientEncryptionSettings);
            var dataKeyId        = clientEncryption.CreateDataKey("local", new DataKeyOptions(), CancellationToken.None);
            var base64DataKeyId  = Convert.ToBase64String(GuidConverter.ToBytes(dataKeyId, GuidRepresentation.Standard));

            clientEncryption.Dispose();

            var collectionNamespace = CollectionNamespace.FromFullName("test.coll");

            var schemaMap = $@"{{
                properties: {{
                    encryptedField: {{
                        encrypt: {{
                            keyId: [{{
                                '$binary' : {{
                                    'base64' : '{base64DataKeyId}',
                                    'subType' : '04'
                                }}
                            }}],
                        bsonType: 'string',
                        algorithm: 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'
                        }}
                    }}
                }},
                'bsonType': 'object'
            }}";
            var autoEncryptionSettings = new AutoEncryptionOptions(
                keyVaultNamespace,
                kmsProviders,
                schemaMap: new Dictionary <string, BsonDocument>()
            {
                { collectionNamespace.ToString(), BsonDocument.Parse(schemaMap) }
            });
            var clientSettings = new MongoClientSettings
            {
                AutoEncryptionOptions = autoEncryptionSettings
            };
            var client   = new MongoClient(clientSettings);
            var database = client.GetDatabase("test");

            database.DropCollection("coll");
            var collection = database.GetCollection <BsonDocument>("coll");

            collection.InsertOne(new BsonDocument("encryptedField", "123456789"));

            var result = collection.Find(FilterDefinition <BsonDocument> .Empty).First();

            _output.WriteLine(result.ToJson());
        }
Beispiel #19
0
        public void Lookup_without_let_should_return_the_expected_result([Values(null, "{}")] string emptyLetValue)
        {
            RequireServer.Check().Supports(Feature.AggregateLet);

            string databaseName             = "test";
            string ordersCollectionName     = "orders";
            string warehousesCollectionName = "warehouses";

            var client = CreateClient();

            DropCollection(client, databaseName, ordersCollectionName);
            DropCollection(client, databaseName, warehousesCollectionName);

            var ordersCollection     = client.GetDatabase(databaseName).GetCollection <Order>(ordersCollectionName);
            var warehousesCollection = client.GetDatabase(databaseName).GetCollection <Warehouse>(warehousesCollectionName);

            var orderDocuments = new[]
            {
                new Order {
                    Item = "almonds", Price = 12, Ordered = 2
                },
                new Order {
                    Item = "pecans", Price = 20, Ordered = 1
                },
                new Order {
                    Item = "cookies", Price = 10, Ordered = 60
                }
            };

            ordersCollection.InsertMany(orderDocuments);

            var warehouseDocuments = new[]
            {
                new Warehouse {
                    StockItem = "almonds", Instock = 120
                },
                new Warehouse {
                    StockItem = "pecans", Instock = 80
                },
                new Warehouse {
                    StockItem = "almonds", Instock = 60
                },
                new Warehouse {
                    StockItem = "cookies", Instock = 40
                },
                new Warehouse {
                    StockItem = "cookies", Instock = 80
                },
            };

            warehousesCollection.InsertMany(warehouseDocuments);

            var lookupPipeline = new EmptyPipelineDefinition <Warehouse>()
                                 .Project <Warehouse, Warehouse, StockData>(
                Builders <Warehouse> .Projection
                .Exclude(warehouses => warehouses.StockItem)
                .Exclude(warehouses => warehouses.Id));

            var result = ordersCollection
                         .Aggregate()
                         .Lookup(
                warehousesCollection,
                emptyLetValue != null ? BsonDocument.Parse(emptyLetValue) : null,
                lookupPipeline,
                new ExpressionFieldDefinition <Order, IEnumerable <StockData> >(order => order.StockData))
                         .ToList()
                         .Select(item =>
            {
                var document = item.ToBsonDocument();
                document.Remove("_id");
                return(document);
            })
                         .ToList();

            result.Count.Should().Be(3);
            result[0].Should().Be("{ 'item' : 'almonds', 'price' : 12, 'ordered' : 2, 'stockdata' : [{ 'instock' : 120 }, { 'instock' : 80 }, { 'instock' : 60 }, { 'instock' : 40 }, { 'instock' : 80 }] }");
            result[1].Should().Be("{ 'item' : 'pecans', 'price' : 20, 'ordered' : 1, 'stockdata' : [{ 'instock' : 120 }, { 'instock' : 80 }, { 'instock' : 60 }, { 'instock' : 40 }, { 'instock' : 80 }] }");
            result[2].Should().Be("{ 'item' : 'cookies', 'price' : 10, 'ordered' : 60, 'stockdata' : [{ 'instock' : 120 }, { 'instock' : 80 }, { 'instock' : 60 }, { 'instock' : 40 }, { 'instock' : 80 }] }");
        }
        public void GetResumeToken_should_return_expected_results_when_batch_is_empty_or_fully_iterated(
            [Values(false, true)] bool async,
            [Values(false, true)] bool withResumeAfter)
        {
            RequireServer.Check().Supports(Feature.ChangeStreamStage).ClusterTypes(ClusterType.ReplicaSet);
            var pipeline               = new BsonDocument[0];
            var resultSerializer       = new ChangeStreamDocumentSerializer <BsonDocument>(BsonDocumentSerializer.Instance);
            var messageEncoderSettings = new MessageEncoderSettings();
            var subject = new ChangeStreamOperation <ChangeStreamDocument <BsonDocument> >(_collectionNamespace, pipeline, resultSerializer, messageEncoderSettings)
            {
                BatchSize = 2
            };

            EnsureDatabaseExists();
            DropCollection();

            if (withResumeAfter)
            {
                subject.ResumeAfter = GenerateResumeAfterToken(async, true);
            }

            using (var cursor = ExecuteOperation(subject, async))
                using (var enumerator = new AsyncCursorEnumerator <ChangeStreamDocument <BsonDocument> >(cursor, CancellationToken.None))
                {
                    var resumeResult = cursor.GetResumeToken();
                    // the batch is empty
                    if (Feature.ChangeStreamPostBatchResumeToken.IsSupported(CoreTestConfiguration.ServerVersion))
                    {
                        var postBatchResumeToken = cursor._postBatchResumeToken();
                        postBatchResumeToken.Should().NotBeNull();
                        resumeResult.Should().Be(postBatchResumeToken);
                    }
                    else
                    {
                        if (withResumeAfter)
                        {
                            resumeResult.Should().Be(subject.ResumeAfter);
                        }
                        else
                        {
                            resumeResult.Should().BeNull();
                        }
                    }

                    // the batch has been iterated to the last document
                    Insert("{ a : 1 }");
                    enumerator.MoveNext();
                    resumeResult = cursor.GetResumeToken();
                    if (Feature.ChangeStreamPostBatchResumeToken.IsSupported(CoreTestConfiguration.ServerVersion))
                    {
                        var postBatchResumeToken = cursor._postBatchResumeToken();
                        postBatchResumeToken.Should().NotBeNull();
                        resumeResult.Should().Be(postBatchResumeToken);
                    }
                    else
                    {
                        var documentResumeToken = cursor._documentResumeToken();
                        documentResumeToken.Should().NotBeNull();
                        resumeResult.Should().Be(documentResumeToken);
                    }
                }
        }
        public void Lookup_with_let_should_return_the_expected_result()
        {
            RequireServer.Check().Supports(Feature.AggregateLet);

            var client = new MongoClient(CoreTestConfiguration.ConnectionString.ToString());
            var warehousesCollection = client.GetDatabase("test").GetCollection <BsonDocument>("warehouses");

            var lookupPipeline = new EmptyPipelineDefinition <BsonDocument>()
                                 .Match(new BsonDocument("$expr",
                                                         new BsonDocument("$and", new BsonArray
            {
                new BsonDocument("$eq", new BsonArray {
                    "$stock_item", "$$order_item"
                }),
                new BsonDocument("$gte", new BsonArray {
                    "$instock", "$$order_qty"
                })
            })))
                                 .Project(
                Builders <BsonDocument> .Projection
                .Exclude("_id")
                .Exclude("stock_item"));

            var result = PipelineStageDefinitionBuilder.Lookup <BsonDocument, BsonDocument, BsonDocument, IEnumerable <BsonDocument>, BsonDocument>(
                warehousesCollection,
                new BsonDocument
            {
                { "order_item", "$item" },
                { "order_qty", "$ordered" }
            },
                lookupPipeline,
                new StringFieldDefinition <BsonDocument, IEnumerable <BsonDocument> >("stockdata")
                );

            RenderStage(result).Document.Should().Be(@"
                {
                    '$lookup' :
                    {
                        'from' : 'warehouses',
                        'let' :
                        {
                            'order_item' : '$item',
                            'order_qty' : '$ordered'
                        },
                        'pipeline' : [
                        {
                            '$match' :
                            { 
                                '$expr' :
                                { 
                                    '$and' : [
                                        { '$eq' : ['$stock_item', '$$order_item'] },
                                        { '$gte' : ['$instock', '$$order_qty'] }]
                                }
                            }
                        },
                        { '$project' : { '_id' : 0, 'stock_item' : 0 } }],
                        'as' : 'stockdata'
                    }
                }");
        }
        public void Command_should_use_serverApi([Values(false, true)] bool async)
        {
            RequireServer.Check().Supports(Feature.CommandMessage);

            var serverApi     = new ServerApi(ServerApiVersion.V1);
            var eventCapturer = new EventCapturer().Capture <CommandStartedEvent>(e => e.CommandName == "ping");
            var builder       = CoreTestConfiguration
                                .ConfigureCluster(new ClusterBuilder())
                                .Subscribe(eventCapturer)
                                .ConfigureCluster(x => x.With(serverApi: serverApi));

            using (var cluster = CoreTestConfiguration.CreateCluster(builder))
                using (var session = cluster.StartSession())
                {
                    var cancellationToken = CancellationToken.None;
                    var server            = (Server)cluster.SelectServer(WritableServerSelector.Instance, cancellationToken);
                    using (var channel = server.GetChannel(cancellationToken))
                    {
                        var command = BsonDocument.Parse("{ ping : 1 }");
                        if (async)
                        {
                            channel
                            .CommandAsync(
                                session,
                                ReadPreference.Primary,
                                DatabaseNamespace.Admin,
                                command,
                                null, // payloads
                                NoOpElementNameValidator.Instance,
                                null, // additionalOptions
                                null, // postWriteAction
                                CommandResponseHandling.Return,
                                BsonDocumentSerializer.Instance,
                                new MessageEncoderSettings(),
                                cancellationToken)
                            .GetAwaiter()
                            .GetResult();
                        }
                        else
                        {
                            channel.Command(
                                session,
                                ReadPreference.Primary,
                                DatabaseNamespace.Admin,
                                command,
                                null, // payloads
                                NoOpElementNameValidator.Instance,
                                null, // additionalOptions
                                null, // postWriteAction
                                CommandResponseHandling.Return,
                                BsonDocumentSerializer.Instance,
                                new MessageEncoderSettings(),
                                cancellationToken);
                        }
                    }
                }

            var commandStartedEvent = eventCapturer.Next().Should().BeOfType <CommandStartedEvent>().Subject;

            commandStartedEvent.Command["apiVersion"].AsString.Should().Be("1");
        }