PageRenderTime 77ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/tests/MongoDB.Driver.Tests/MongoCollectionImplTests.cs

http://github.com/mongodb/mongo-csharp-driver
C# | 3657 lines | 3344 code | 282 blank | 31 comment | 171 complexity | 9dbd03b8576f485536d7520f954ab9a7 MD5 | raw file
Possible License(s): Apache-2.0
  1. /* Copyright 2010-present MongoDB Inc.
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. using System;
  16. using System.Collections.Generic;
  17. using System.Linq;
  18. using System.Linq.Expressions;
  19. using System.Net;
  20. using System.Threading;
  21. using FluentAssertions;
  22. using MongoDB.Bson;
  23. using MongoDB.Bson.Serialization;
  24. using MongoDB.Bson.Serialization.Attributes;
  25. using MongoDB.Bson.Serialization.Serializers;
  26. using MongoDB.Bson.TestHelpers.XunitExtensions;
  27. using MongoDB.Driver.Core.Bindings;
  28. using MongoDB.Driver.Core.Clusters;
  29. using MongoDB.Driver.Core.Connections;
  30. using MongoDB.Driver.Core.Misc;
  31. using MongoDB.Driver.Core.Operations;
  32. using MongoDB.Driver.Core.Servers;
  33. using MongoDB.Driver.Core.TestHelpers.XunitExtensions;
  34. using MongoDB.Driver.Tests;
  35. using Moq;
  36. using Xunit;
  37. namespace MongoDB.Driver
  38. {
  39. public class MongoCollectionImplTests
  40. {
  41. private readonly ConnectionId _connectionId = new ConnectionId(new ServerId(new ClusterId(0), new DnsEndPoint("localhost", 27017)), 0);
  42. private readonly ReadConcern _readConcern = ReadConcern.Majority;
  43. private MockOperationExecutor _operationExecutor;
  44. public MongoCollectionImplTests()
  45. {
  46. var mockClient = new Mock<IMongoClient>();
  47. var mockCluster = new Mock<ICluster>();
  48. mockClient.SetupGet(m => m.Cluster).Returns(mockCluster.Object);
  49. _operationExecutor = new MockOperationExecutor();
  50. _operationExecutor.Client = mockClient.Object;
  51. }
  52. [Fact]
  53. public void CollectionName_should_be_set()
  54. {
  55. var subject = CreateSubject<BsonDocument>();
  56. subject.CollectionNamespace.CollectionName.Should().Be("bar");
  57. }
  58. public void Database_should_be_set()
  59. {
  60. var subject = CreateSubject<BsonDateTime>();
  61. subject.Database.Should().NotBeNull();
  62. }
  63. [Fact]
  64. public void Settings_should_be_set()
  65. {
  66. var subject = CreateSubject<BsonDocument>();
  67. subject.Settings.Should().NotBeNull();
  68. }
  69. [Theory]
  70. [ParameterAttributeData]
  71. public void Aggregate_should_execute_an_AggregateOperation_when_out_is_not_specified(
  72. [Values(false, true)] bool usingSession,
  73. [Values(false, true)] bool async)
  74. {
  75. var subject = CreateSubject<BsonDocument>();
  76. var session = CreateSession(usingSession);
  77. var pipeline = new EmptyPipelineDefinition<BsonDocument>().Match("{ x : 2 }");
  78. var options = new AggregateOptions()
  79. {
  80. AllowDiskUse = true,
  81. BatchSize = 10,
  82. Collation = new Collation("en_US"),
  83. Comment = "test",
  84. Hint = new BsonDocument("x", 1),
  85. MaxAwaitTime = TimeSpan.FromSeconds(4),
  86. MaxTime = TimeSpan.FromSeconds(3),
  87. #pragma warning disable 618
  88. UseCursor = false
  89. #pragma warning restore 618
  90. };
  91. var cancellationToken = new CancellationTokenSource().Token;
  92. var renderedPipeline = RenderPipeline(subject, pipeline);
  93. if (usingSession)
  94. {
  95. if (async)
  96. {
  97. subject.AggregateAsync(session, pipeline, options, cancellationToken).GetAwaiter().GetResult();
  98. }
  99. else
  100. {
  101. subject.Aggregate(session, pipeline, options, cancellationToken);
  102. }
  103. }
  104. else
  105. {
  106. if (async)
  107. {
  108. subject.AggregateAsync(pipeline, options, cancellationToken).GetAwaiter().GetResult();
  109. }
  110. else
  111. {
  112. subject.Aggregate(pipeline, options, cancellationToken);
  113. }
  114. }
  115. var call = _operationExecutor.GetReadCall<IAsyncCursor<BsonDocument>>();
  116. VerifySessionAndCancellationToken(call, session, cancellationToken);
  117. var operation = call.Operation.Should().BeOfType<AggregateOperation<BsonDocument>>().Subject;
  118. operation.AllowDiskUse.Should().Be(options.AllowDiskUse);
  119. operation.BatchSize.Should().Be(options.BatchSize);
  120. operation.Collation.Should().BeSameAs(options.Collation);
  121. operation.CollectionNamespace.Should().Be(subject.CollectionNamespace);
  122. operation.Comment.Should().Be(options.Comment);
  123. operation.Hint.Should().Be(options.Hint);
  124. operation.MaxAwaitTime.Should().Be(options.MaxAwaitTime);
  125. operation.MaxTime.Should().Be(options.MaxTime);
  126. operation.Pipeline.Should().Equal(renderedPipeline.Documents);
  127. operation.ReadConcern.Should().Be(_readConcern);
  128. operation.RetryRequested.Should().BeTrue();
  129. operation.ResultSerializer.Should().BeSameAs(renderedPipeline.OutputSerializer);
  130. #pragma warning disable 618
  131. operation.UseCursor.Should().Be(options.UseCursor);
  132. #pragma warning restore 618
  133. }
  134. [Theory]
  135. [ParameterAttributeData]
  136. public void Aggregate_should_execute_an_AggregateToCollectionOperation_and_a_FindOperation_when_out_is_specified(
  137. [Values(false, true)] bool usingSession,
  138. [Values(false, true)] bool usingDifferentOutputDatabase,
  139. [Values(false, true)] bool async)
  140. {
  141. var writeConcern = new WriteConcern(1);
  142. var readConcern = new ReadConcern(ReadConcernLevel.Majority);
  143. var inputDatabase = CreateDatabase(databaseName: "inputDatabaseName");
  144. var subject = CreateSubject<BsonDocument>(database: inputDatabase).WithWriteConcern(writeConcern);
  145. var session = CreateSession(usingSession);
  146. var outputDatabase = usingDifferentOutputDatabase ? subject.Database.Client.GetDatabase("outputDatabaseName") : inputDatabase;
  147. var outputCollection = outputDatabase.GetCollection<BsonDocument>("outputCollectionName");
  148. var pipeline = new EmptyPipelineDefinition<BsonDocument>()
  149. .Match("{ x : 2 }")
  150. .Out(outputCollection);
  151. var options = new AggregateOptions()
  152. {
  153. AllowDiskUse = true,
  154. BatchSize = 10,
  155. BypassDocumentValidation = true,
  156. Collation = new Collation("en_US"),
  157. Comment = "test",
  158. Hint = new BsonDocument("x", 1),
  159. MaxTime = TimeSpan.FromSeconds(3),
  160. #pragma warning disable 618
  161. UseCursor = false
  162. #pragma warning restore 618
  163. };
  164. var cancellationToken1 = new CancellationTokenSource().Token;
  165. var cancellationToken2 = new CancellationTokenSource().Token;
  166. var expectedPipeline = new List<BsonDocument>(RenderPipeline(subject, pipeline).Documents);
  167. if (!usingDifferentOutputDatabase)
  168. {
  169. expectedPipeline[1] = new BsonDocument("$out", outputCollection.CollectionNamespace.CollectionName);
  170. }
  171. IAsyncCursor<BsonDocument> result;
  172. if (usingSession)
  173. {
  174. if (async)
  175. {
  176. result = subject.AggregateAsync(session, pipeline, options, cancellationToken1).GetAwaiter().GetResult();
  177. }
  178. else
  179. {
  180. result = subject.Aggregate(session, pipeline, options, cancellationToken1);
  181. }
  182. }
  183. else
  184. {
  185. if (async)
  186. {
  187. result = subject.AggregateAsync(pipeline, options, cancellationToken1).GetAwaiter().GetResult();
  188. }
  189. else
  190. {
  191. result = subject.Aggregate(pipeline, options, cancellationToken1);
  192. }
  193. }
  194. var aggregateCall = _operationExecutor.GetWriteCall<BsonDocument>();
  195. VerifySessionAndCancellationToken(aggregateCall, session, cancellationToken1);
  196. var aggregateOperation = aggregateCall.Operation.Should().BeOfType<AggregateToCollectionOperation>().Subject;
  197. aggregateOperation.AllowDiskUse.Should().Be(options.AllowDiskUse);
  198. aggregateOperation.BypassDocumentValidation.Should().Be(options.BypassDocumentValidation);
  199. aggregateOperation.Collation.Should().BeSameAs(options.Collation);
  200. aggregateOperation.CollectionNamespace.Should().Be(subject.CollectionNamespace);
  201. aggregateOperation.Comment.Should().Be(options.Comment);
  202. aggregateOperation.Hint.Should().Be(options.Hint);
  203. aggregateOperation.MaxTime.Should().Be(options.MaxTime);
  204. aggregateOperation.Pipeline.Should().Equal(expectedPipeline);
  205. aggregateOperation.ReadConcern.Should().Be(readConcern);
  206. aggregateOperation.WriteConcern.Should().BeSameAs(writeConcern);
  207. var mockCursor = new Mock<IAsyncCursor<BsonDocument>>();
  208. _operationExecutor.EnqueueResult(mockCursor.Object);
  209. if (async)
  210. {
  211. result.MoveNextAsync(cancellationToken2).GetAwaiter().GetResult();
  212. }
  213. else
  214. {
  215. result.MoveNext(cancellationToken2);
  216. }
  217. var findCall = _operationExecutor.GetReadCall<IAsyncCursor<BsonDocument>>();
  218. VerifySessionAndCancellationToken(findCall, session, cancellationToken2);
  219. var findOperation = findCall.Operation.Should().BeOfType<FindOperation<BsonDocument>>().Subject;
  220. findOperation.AllowDiskUse.Should().NotHaveValue();
  221. findOperation.AllowPartialResults.Should().NotHaveValue();
  222. findOperation.BatchSize.Should().Be(options.BatchSize);
  223. findOperation.Collation.Should().BeSameAs(options.Collation);
  224. findOperation.CollectionNamespace.FullName.Should().Be(outputCollection.CollectionNamespace.FullName);
  225. findOperation.Comment.Should().BeNull();
  226. findOperation.CursorType.Should().Be(Core.Operations.CursorType.NonTailable);
  227. findOperation.Filter.Should().BeNull();
  228. findOperation.Limit.Should().Be(null);
  229. findOperation.MaxTime.Should().Be(options.MaxTime);
  230. #pragma warning disable 618
  231. findOperation.Modifiers.Should().BeNull();
  232. #pragma warning restore 618
  233. findOperation.NoCursorTimeout.Should().NotHaveValue();
  234. #pragma warning disable 618
  235. findOperation.OplogReplay.Should().NotHaveValue();
  236. #pragma warning restore 618
  237. findOperation.Projection.Should().BeNull();
  238. findOperation.RetryRequested.Should().BeTrue();
  239. findOperation.Skip.Should().Be(null);
  240. findOperation.Sort.Should().BeNull();
  241. }
  242. [Theory]
  243. [ParameterAttributeData]
  244. public void AggregateToCollection_should_execute_an_AggregateToCollectionOperation(
  245. [Values(false, true)] bool usingSession,
  246. [Values(false, true)] bool usingDifferentOutputDatabase,
  247. [Values(false, true)] bool async)
  248. {
  249. var writeConcern = new WriteConcern(1);
  250. var readConcern = new ReadConcern(ReadConcernLevel.Majority);
  251. var inputDatabase = CreateDatabase(databaseName: "inputDatabaseName");
  252. var subject = CreateSubject<BsonDocument>(database: inputDatabase).WithWriteConcern(writeConcern);
  253. var session = CreateSession(usingSession);
  254. var outputDatabase = usingDifferentOutputDatabase ? subject.Database.Client.GetDatabase("outputDatabaseName") : inputDatabase;
  255. var outputCollection = outputDatabase.GetCollection<BsonDocument>("outputCollectionName");
  256. var pipeline = new EmptyPipelineDefinition<BsonDocument>()
  257. .Match("{ x : 2 }")
  258. .Out(outputCollection);
  259. var options = new AggregateOptions()
  260. {
  261. AllowDiskUse = true,
  262. BatchSize = 10,
  263. BypassDocumentValidation = true,
  264. Collation = new Collation("en_US"),
  265. Comment = "test",
  266. Hint = new BsonDocument("x", 1),
  267. MaxTime = TimeSpan.FromSeconds(3),
  268. #pragma warning disable 618
  269. UseCursor = false
  270. #pragma warning restore 618
  271. };
  272. var cancellationToken = new CancellationTokenSource().Token;
  273. var expectedPipeline = new List<BsonDocument>(RenderPipeline(subject, pipeline).Documents);
  274. if (!usingDifferentOutputDatabase)
  275. {
  276. expectedPipeline[1] = new BsonDocument("$out", outputCollection.CollectionNamespace.CollectionName);
  277. }
  278. if (async)
  279. {
  280. if (usingSession)
  281. {
  282. subject.AggregateToCollectionAsync(session, pipeline, options, cancellationToken).GetAwaiter().GetResult();
  283. }
  284. else
  285. {
  286. subject.AggregateToCollectionAsync(pipeline, options, cancellationToken).GetAwaiter().GetResult();
  287. }
  288. }
  289. else
  290. {
  291. if (usingSession)
  292. {
  293. subject.AggregateToCollection(session, pipeline, options, cancellationToken);
  294. }
  295. else
  296. {
  297. subject.AggregateToCollection(pipeline, options, cancellationToken);
  298. }
  299. }
  300. var aggregateCall = _operationExecutor.GetWriteCall<BsonDocument>();
  301. VerifySessionAndCancellationToken(aggregateCall, session, cancellationToken);
  302. var aggregateOperation = aggregateCall.Operation.Should().BeOfType<AggregateToCollectionOperation>().Subject;
  303. aggregateOperation.AllowDiskUse.Should().Be(options.AllowDiskUse);
  304. aggregateOperation.BypassDocumentValidation.Should().Be(options.BypassDocumentValidation);
  305. aggregateOperation.Collation.Should().BeSameAs(options.Collation);
  306. aggregateOperation.CollectionNamespace.Should().Be(subject.CollectionNamespace);
  307. aggregateOperation.Comment.Should().Be(options.Comment);
  308. aggregateOperation.Hint.Should().Be(options.Hint);
  309. aggregateOperation.MaxTime.Should().Be(options.MaxTime);
  310. aggregateOperation.Pipeline.Should().Equal(expectedPipeline);
  311. aggregateOperation.ReadConcern.Should().Be(readConcern);
  312. aggregateOperation.WriteConcern.Should().BeSameAs(writeConcern);
  313. }
  314. [Theory]
  315. [ParameterAttributeData]
  316. public void AggregateToCollection_should_throw_when_last_stage_is_not_an_output_stage(
  317. [Values(false, true)] bool usingSession,
  318. [Values(false, true)] bool async)
  319. {
  320. var subject = CreateSubject<BsonDocument>();
  321. var session = CreateSession(usingSession);
  322. var pipeline = new EmptyPipelineDefinition<BsonDocument>()
  323. .Match("{ x : 2 }");
  324. var options = new AggregateOptions();
  325. var cancellationToken = new CancellationTokenSource().Token;
  326. Exception exception;
  327. if (async)
  328. {
  329. if (usingSession)
  330. {
  331. exception = Record.Exception(() => subject.AggregateToCollectionAsync(session, pipeline, options, cancellationToken).GetAwaiter().GetResult());
  332. }
  333. else
  334. {
  335. exception = Record.Exception(() => subject.AggregateToCollectionAsync(pipeline, options, cancellationToken).GetAwaiter().GetResult());
  336. }
  337. }
  338. else
  339. {
  340. if (usingSession)
  341. {
  342. exception = Record.Exception(() => subject.AggregateToCollection(session, pipeline, options, cancellationToken));
  343. }
  344. else
  345. {
  346. exception = Record.Exception(() => subject.AggregateToCollection(pipeline, options, cancellationToken));
  347. }
  348. }
  349. exception.Should().BeOfType<InvalidOperationException>();
  350. }
  351. [Theory]
  352. [ParameterAttributeData]
  353. public void BulkWrite_should_execute_a_BulkMixedWriteOperation(
  354. [Values(false, true)] bool usingSession,
  355. [Values(null, false, true)] bool? bypassDocumentValidation,
  356. [Values(false, true)] bool isOrdered,
  357. [Values(false, true)] bool async)
  358. {
  359. var subject = CreateSubject<BsonDocument>();
  360. var session = CreateSession(usingSession);
  361. var collation = new Collation("en_US");
  362. var hint = new BsonDocument("x", 1);
  363. var requests = new WriteModel<BsonDocument>[]
  364. {
  365. new InsertOneModel<BsonDocument>(new BsonDocument("_id", 1).Add("a",1)),
  366. new DeleteManyModel<BsonDocument>(new BsonDocument("b", 1)) { Collation = collation },
  367. new DeleteManyModel<BsonDocument>(new BsonDocument("c", 1)) { Collation = collation, Hint = hint },
  368. new DeleteOneModel<BsonDocument>(new BsonDocument("d", 1)) { Collation = collation },
  369. new DeleteOneModel<BsonDocument>(new BsonDocument("e", 1)) { Collation = collation, Hint = hint },
  370. new ReplaceOneModel<BsonDocument>(new BsonDocument("f", 1), new BsonDocument("g", 1)) { Collation = collation },
  371. new ReplaceOneModel<BsonDocument>(new BsonDocument("h", 1), new BsonDocument("i", 1)) { Collation = collation, Hint = hint },
  372. new ReplaceOneModel<BsonDocument>(new BsonDocument("j", 1), new BsonDocument("k", 1)) { Collation = collation, IsUpsert = true },
  373. new UpdateManyModel<BsonDocument>(new BsonDocument("l", 1), new BsonDocument("$set", new BsonDocument("m", 1))) { Collation = collation },
  374. new UpdateManyModel<BsonDocument>(new BsonDocument("n", 1), new BsonDocument("$set", new BsonDocument("o", 1))) { Collation = collation, Hint = hint },
  375. new UpdateManyModel<BsonDocument>(new BsonDocument("p", 1), new BsonDocument("$set", new BsonDocument("q", 1))) { Collation = collation, IsUpsert = true },
  376. new UpdateOneModel<BsonDocument>(new BsonDocument("r", 1), new BsonDocument("$set", new BsonDocument("s", 1))) { Collation = collation },
  377. new UpdateOneModel<BsonDocument>(new BsonDocument("t", 1), new BsonDocument("$set", new BsonDocument("u", 1))) { Collation = collation, Hint = hint },
  378. new UpdateOneModel<BsonDocument>(new BsonDocument("v", 1), new BsonDocument("$set", new BsonDocument("w", 1))) { Collation = collation, IsUpsert = true },
  379. };
  380. var options = new BulkWriteOptions
  381. {
  382. BypassDocumentValidation = bypassDocumentValidation,
  383. IsOrdered = isOrdered
  384. };
  385. var cancellationToken = new CancellationTokenSource().Token;
  386. var operationResult = new BulkWriteOperationResult.Unacknowledged(14, new[] { new InsertRequest(new BsonDocument("b", 1)) });
  387. _operationExecutor.EnqueueResult<BulkWriteOperationResult>(operationResult);
  388. BulkWriteResult<BsonDocument> result;
  389. if (usingSession)
  390. {
  391. if (async)
  392. {
  393. result = subject.BulkWriteAsync(session, requests, options, cancellationToken).GetAwaiter().GetResult();
  394. }
  395. else
  396. {
  397. result = subject.BulkWrite(session, requests, options, cancellationToken);
  398. }
  399. }
  400. else
  401. {
  402. if (async)
  403. {
  404. result = subject.BulkWriteAsync(requests, options, cancellationToken).GetAwaiter().GetResult();
  405. }
  406. else
  407. {
  408. result = subject.BulkWrite(requests, options, cancellationToken);
  409. }
  410. }
  411. var call = _operationExecutor.GetWriteCall<BulkWriteOperationResult>();
  412. VerifySessionAndCancellationToken(call, session, cancellationToken);
  413. // I know, this is a lot of stuff in one test :(
  414. var operation = call.Operation.Should().BeOfType<BulkMixedWriteOperation>().Subject;
  415. operation.BypassDocumentValidation.Should().Be(bypassDocumentValidation);
  416. operation.CollectionNamespace.Should().Be(subject.CollectionNamespace);
  417. operation.IsOrdered.Should().Be(isOrdered);
  418. operation.Requests.Count().Should().Be(14);
  419. var convertedRequests = operation.Requests.ToList();
  420. // InsertOneModel
  421. convertedRequests[0].Should().BeOfType<InsertRequest>();
  422. convertedRequests[0].CorrelationId.Should().Be(0);
  423. var convertedRequest0 = (InsertRequest)convertedRequests[0];
  424. convertedRequest0.Document.Should().Be("{_id:1, a:1}");
  425. // RemoveManyModel
  426. convertedRequests[1].Should().BeOfType<DeleteRequest>();
  427. convertedRequests[1].CorrelationId.Should().Be(1);
  428. var convertedRequest1 = (DeleteRequest)convertedRequests[1];
  429. convertedRequest1.Collation.Should().BeSameAs(collation);
  430. convertedRequest1.Filter.Should().Be("{b:1}");
  431. convertedRequest1.Hint.Should().BeNull();
  432. convertedRequest1.Limit.Should().Be(0);
  433. // RemoveManyModel with hint
  434. convertedRequests[2].Should().BeOfType<DeleteRequest>();
  435. convertedRequests[2].CorrelationId.Should().Be(2);
  436. var convertedRequest2 = (DeleteRequest)convertedRequests[2];
  437. convertedRequest2.Collation.Should().BeSameAs(collation);
  438. convertedRequest2.Filter.Should().Be("{c:1}");
  439. convertedRequest2.Hint.Should().Be(hint);
  440. convertedRequest2.Limit.Should().Be(0);
  441. // RemoveOneModel
  442. convertedRequests[3].Should().BeOfType<DeleteRequest>();
  443. convertedRequests[3].CorrelationId.Should().Be(3);
  444. var convertedRequest3 = (DeleteRequest)convertedRequests[3];
  445. convertedRequest3.Collation.Should().BeSameAs(collation);
  446. convertedRequest3.Filter.Should().Be("{d:1}");
  447. convertedRequest3.Hint.Should().BeNull();
  448. convertedRequest3.Limit.Should().Be(1);
  449. // RemoveOneModel with hint
  450. convertedRequests[4].Should().BeOfType<DeleteRequest>();
  451. convertedRequests[4].CorrelationId.Should().Be(4);
  452. var convertedRequest4 = (DeleteRequest)convertedRequests[4];
  453. convertedRequest4.Collation.Should().BeSameAs(collation);
  454. convertedRequest4.Filter.Should().Be("{e:1}");
  455. convertedRequest4.Hint.Should().Be(hint);
  456. convertedRequest4.Limit.Should().Be(1);
  457. // ReplaceOneModel
  458. convertedRequests[5].Should().BeOfType<UpdateRequest>();
  459. convertedRequests[5].CorrelationId.Should().Be(5);
  460. var convertedRequest5 = (UpdateRequest)convertedRequests[5];
  461. convertedRequest5.Collation.Should().BeSameAs(collation);
  462. convertedRequest5.Filter.Should().Be("{f:1}");
  463. convertedRequest5.Hint.Should().BeNull();
  464. convertedRequest5.Update.Should().Be("{g:1}");
  465. convertedRequest5.UpdateType.Should().Be(UpdateType.Replacement);
  466. convertedRequest5.IsMulti.Should().BeFalse();
  467. convertedRequest5.IsUpsert.Should().BeFalse();
  468. // ReplaceOneModel with hint
  469. convertedRequests[6].Should().BeOfType<UpdateRequest>();
  470. convertedRequests[6].CorrelationId.Should().Be(6);
  471. var convertedRequest6 = (UpdateRequest)convertedRequests[6];
  472. convertedRequest6.Collation.Should().BeSameAs(collation);
  473. convertedRequest6.Filter.Should().Be("{h:1}");
  474. convertedRequest6.Hint.Should().Be(hint);
  475. convertedRequest6.Update.Should().Be("{i:1}");
  476. convertedRequest6.UpdateType.Should().Be(UpdateType.Replacement);
  477. convertedRequest6.IsMulti.Should().BeFalse();
  478. convertedRequest6.IsUpsert.Should().BeFalse();
  479. // ReplaceOneModel with upsert
  480. convertedRequests[7].Should().BeOfType<UpdateRequest>();
  481. convertedRequests[7].CorrelationId.Should().Be(7);
  482. var convertedRequest7 = (UpdateRequest)convertedRequests[7];
  483. convertedRequest7.Collation.Should().BeSameAs(collation);
  484. convertedRequest7.Filter.Should().Be("{j:1}");
  485. convertedRequest7.Hint.Should().BeNull();
  486. convertedRequest7.Update.Should().Be("{k:1}");
  487. convertedRequest7.UpdateType.Should().Be(UpdateType.Replacement);
  488. convertedRequest7.IsMulti.Should().BeFalse();
  489. convertedRequest7.IsUpsert.Should().BeTrue();
  490. // UpdateManyModel
  491. convertedRequests[8].Should().BeOfType<UpdateRequest>();
  492. convertedRequests[8].CorrelationId.Should().Be(8);
  493. var convertedRequest8 = (UpdateRequest)convertedRequests[8];
  494. convertedRequest8.Collation.Should().BeSameAs(collation);
  495. convertedRequest8.Filter.Should().Be("{l:1}");
  496. convertedRequest8.Hint.Should().BeNull();
  497. convertedRequest8.Update.Should().Be("{$set:{m:1}}");
  498. convertedRequest8.UpdateType.Should().Be(UpdateType.Update);
  499. convertedRequest8.IsMulti.Should().BeTrue();
  500. convertedRequest8.IsUpsert.Should().BeFalse();
  501. // UpdateManyModel with hint
  502. convertedRequests[9].Should().BeOfType<UpdateRequest>();
  503. convertedRequests[9].CorrelationId.Should().Be(9);
  504. var convertedRequest9 = (UpdateRequest)convertedRequests[9];
  505. convertedRequest9.Collation.Should().BeSameAs(collation);
  506. convertedRequest9.Filter.Should().Be("{n:1}");
  507. convertedRequest9.Hint.Should().Be(hint);
  508. convertedRequest9.Update.Should().Be("{$set:{o:1}}");
  509. convertedRequest9.UpdateType.Should().Be(UpdateType.Update);
  510. convertedRequest9.IsMulti.Should().BeTrue();
  511. convertedRequest9.IsUpsert.Should().BeFalse();
  512. // UpdateManyModel with upsert
  513. convertedRequests[10].Should().BeOfType<UpdateRequest>();
  514. convertedRequests[10].CorrelationId.Should().Be(10);
  515. var convertedRequest10 = (UpdateRequest)convertedRequests[10];
  516. convertedRequest10.Collation.Should().BeSameAs(collation);
  517. convertedRequest10.Filter.Should().Be("{p:1}");
  518. convertedRequest10.Hint.Should().BeNull();
  519. convertedRequest10.Update.Should().Be("{$set:{q:1}}");
  520. convertedRequest10.UpdateType.Should().Be(UpdateType.Update);
  521. convertedRequest10.IsMulti.Should().BeTrue();
  522. convertedRequest10.IsUpsert.Should().BeTrue();
  523. // UpdateOneModel
  524. convertedRequests[11].Should().BeOfType<UpdateRequest>();
  525. convertedRequests[11].CorrelationId.Should().Be(11);
  526. var convertedRequest11 = (UpdateRequest)convertedRequests[11];
  527. convertedRequest11.Collation.Should().BeSameAs(collation);
  528. convertedRequest11.Filter.Should().Be("{r:1}");
  529. convertedRequest11.Hint.Should().BeNull();
  530. convertedRequest11.Update.Should().Be("{$set:{s:1}}");
  531. convertedRequest11.UpdateType.Should().Be(UpdateType.Update);
  532. convertedRequest11.IsMulti.Should().BeFalse();
  533. convertedRequest11.IsUpsert.Should().BeFalse();
  534. // UpdateOneModel with hint
  535. convertedRequests[12].Should().BeOfType<UpdateRequest>();
  536. convertedRequests[12].CorrelationId.Should().Be(12);
  537. var convertedRequest12 = (UpdateRequest)convertedRequests[12];
  538. convertedRequest12.Collation.Should().BeSameAs(collation);
  539. convertedRequest12.Filter.Should().Be("{t:1}");
  540. convertedRequest12.Hint.Should().Be(hint);
  541. convertedRequest12.Update.Should().Be("{$set:{u:1}}");
  542. convertedRequest12.UpdateType.Should().Be(UpdateType.Update);
  543. convertedRequest12.IsMulti.Should().BeFalse();
  544. convertedRequest12.IsUpsert.Should().BeFalse();
  545. // UpdateOneModel with upsert
  546. convertedRequests[13].Should().BeOfType<UpdateRequest>();
  547. convertedRequests[13].CorrelationId.Should().Be(13);
  548. var convertedRequest13 = (UpdateRequest)convertedRequests[13];
  549. convertedRequest13.Collation.Should().BeSameAs(collation);
  550. convertedRequest13.Filter.Should().Be("{v:1}");
  551. convertedRequest13.Hint.Should().BeNull();
  552. convertedRequest13.Update.Should().Be("{$set:{w:1}}");
  553. convertedRequest13.UpdateType.Should().Be(UpdateType.Update);
  554. convertedRequest13.IsMulti.Should().BeFalse();
  555. convertedRequest13.IsUpsert.Should().BeTrue();
  556. // Result
  557. result.Should().NotBeNull();
  558. result.IsAcknowledged.Should().BeFalse();
  559. result.RequestCount.Should().Be(14);
  560. result.ProcessedRequests.Should().BeEquivalentTo(requests);
  561. for (int i = 0; i < requests.Length; i++)
  562. {
  563. result.ProcessedRequests[i].Should().BeSameAs(requests[i]);
  564. }
  565. }
  566. [Theory]
  567. [ParameterAttributeData]
  568. public void BulkWrite_should_throw_if_model_is_invalid([Values(false, true)] bool async)
  569. {
  570. var subject = CreateSubject<BsonDocument>();
  571. var pipeline = new BsonDocumentStagePipelineDefinition<BsonDocument, BsonDocument>(new[] { new BsonDocument("$project", "{ value : 1 }") });
  572. var update = new PipelineUpdateDefinition<BsonDocument>(pipeline);
  573. var arrayFilters = new List<ArrayFilterDefinition>()
  574. {
  575. new BsonDocumentArrayFilterDefinition<BsonDocument>(new BsonDocument("x", 1))
  576. };
  577. var models = new WriteModel<BsonDocument>[]
  578. {
  579. new UpdateOneModel<BsonDocument>(new BsonDocument("n", 1), update)
  580. {
  581. ArrayFilters = arrayFilters
  582. },
  583. new UpdateManyModel<BsonDocument>(new BsonDocument("n", 2), update)
  584. {
  585. ArrayFilters = arrayFilters
  586. }
  587. };
  588. foreach (var model in models)
  589. {
  590. Exception exception;
  591. if (async)
  592. {
  593. exception = Record.ExceptionAsync(async () => { await subject.BulkWriteAsync(new[] { model }); })
  594. .GetAwaiter()
  595. .GetResult();
  596. }
  597. else
  598. {
  599. exception = Record.Exception(() => { subject.BulkWrite(new[] { model }); });
  600. }
  601. exception.Should().BeOfType<NotSupportedException>();
  602. }
  603. }
  604. [Theory]
  605. [ParameterAttributeData]
  606. public void Count_should_execute_a_CountOperation(
  607. [Values(false, true)] bool usingSession,
  608. [Values(false, true)] bool async)
  609. {
  610. var subject = CreateSubject<BsonDocument>();
  611. var session = CreateSession(usingSession);
  612. var filter = new BsonDocument("x", 1);
  613. var options = new CountOptions
  614. {
  615. Collation = new Collation("en_US"),
  616. Hint = "funny",
  617. Limit = 10,
  618. MaxTime = TimeSpan.FromSeconds(20),
  619. Skip = 30
  620. };
  621. var cancellationToken = new CancellationTokenSource().Token;
  622. if (usingSession)
  623. {
  624. if (async)
  625. {
  626. #pragma warning disable 618
  627. subject.CountAsync(session, filter, options, cancellationToken).GetAwaiter().GetResult();
  628. #pragma warning restore
  629. }
  630. else
  631. {
  632. #pragma warning disable 618
  633. subject.Count(session, filter, options, cancellationToken);
  634. #pragma warning restore
  635. }
  636. }
  637. else
  638. {
  639. if (async)
  640. {
  641. #pragma warning disable 618
  642. subject.CountAsync(filter, options, cancellationToken).GetAwaiter().GetResult();
  643. #pragma warning restore
  644. }
  645. else
  646. {
  647. #pragma warning disable 618
  648. subject.Count(filter, options, cancellationToken);
  649. #pragma warning restore
  650. }
  651. }
  652. var call = _operationExecutor.GetReadCall<long>();
  653. VerifySessionAndCancellationToken(call, session, cancellationToken);
  654. var operation = call.Operation.Should().BeOfType<CountOperation>().Subject;
  655. operation.Collation.Should().BeSameAs(options.Collation);
  656. operation.CollectionNamespace.Should().Be(subject.CollectionNamespace);
  657. operation.Filter.Should().Be(filter);
  658. operation.Hint.Should().Be(options.Hint);
  659. operation.Limit.Should().Be(options.Limit);
  660. operation.MaxTime.Should().Be(options.MaxTime);
  661. operation.ReadConcern.Should().Be(_readConcern);
  662. operation.RetryRequested.Should().BeTrue();
  663. operation.Skip.Should().Be(options.Skip);
  664. }
  665. [Theory]
  666. [ParameterAttributeData]
  667. public void CountDocuments_should_execute_a_CountDocumentsOperation(
  668. [Values(false, true)] bool usingSession,
  669. [Values(false, true)] bool async)
  670. {
  671. var subject = CreateSubject<BsonDocument>();
  672. var session = CreateSession(usingSession);
  673. var filter = new BsonDocument("x", 1);
  674. var options = new CountOptions
  675. {
  676. Collation = new Collation("en_US"),
  677. Hint = "funny",
  678. Limit = 10,
  679. MaxTime = TimeSpan.FromSeconds(20),
  680. Skip = 30
  681. };
  682. var cancellationToken = new CancellationTokenSource().Token;
  683. if (usingSession)
  684. {
  685. if (async)
  686. {
  687. subject.CountDocumentsAsync(session, filter, options, cancellationToken).GetAwaiter().GetResult();
  688. }
  689. else
  690. {
  691. subject.CountDocuments(session, filter, options, cancellationToken);
  692. }
  693. }
  694. else
  695. {
  696. if (async)
  697. {
  698. subject.CountDocumentsAsync(filter, options, cancellationToken).GetAwaiter().GetResult();
  699. }
  700. else
  701. {
  702. subject.CountDocuments(filter, options, cancellationToken);
  703. }
  704. }
  705. var call = _operationExecutor.GetReadCall<long>();
  706. VerifySessionAndCancellationToken(call, session, cancellationToken);
  707. var operation = call.Operation.Should().BeOfType<CountDocumentsOperation>().Subject;
  708. operation.Collation.Should().BeSameAs(options.Collation);
  709. operation.CollectionNamespace.Should().Be(subject.CollectionNamespace);
  710. operation.Filter.Should().Be(filter);
  711. operation.Hint.Should().Be(options.Hint);
  712. operation.Limit.Should().Be(options.Limit);
  713. operation.MaxTime.Should().Be(options.MaxTime);
  714. operation.ReadConcern.Should().Be(_readConcern);
  715. operation.RetryRequested.Should().BeTrue();
  716. operation.Skip.Should().Be(options.Skip);
  717. }
  718. [Theory]
  719. [ParameterAttributeData]
  720. public void DeleteMany_should_execute_a_BulkMixedOperation(
  721. [Values(false, true)] bool usingSession,
  722. [Values(false, true)] bool async)
  723. {
  724. var subject = CreateSubject<BsonDocument>();
  725. var session = CreateSession(usingSession);
  726. var filter = new BsonDocument("a", 1);
  727. var collation = new Collation("en_US");
  728. var hint = new BsonDocument("_id", 1);
  729. var options = new DeleteOptions { Collation = collation };
  730. var cancellationToken = new CancellationTokenSource().Token;
  731. var processedRequest = new DeleteRequest(filter) { Collation = collation, CorrelationId = 0, Hint = hint, Limit = 0 };
  732. var operationResult = new BulkWriteOperationResult.Unacknowledged(9, new[] { processedRequest });
  733. _operationExecutor.EnqueueResult<BulkWriteOperationResult>(operationResult);
  734. if (usingSession)
  735. {
  736. if (async)
  737. {
  738. subject.DeleteManyAsync(session, filter, options, cancellationToken).GetAwaiter().GetResult();
  739. }
  740. else
  741. {
  742. subject.DeleteMany(session, filter, options, cancellationToken);
  743. }
  744. }
  745. else
  746. {
  747. if (async)
  748. {
  749. subject.DeleteManyAsync(filter, options, cancellationToken).GetAwaiter().GetResult();
  750. }
  751. else
  752. {
  753. subject.DeleteMany(filter, options, cancellationToken);
  754. }
  755. }
  756. var call = _operationExecutor.GetWriteCall<BulkWriteOperationResult>();
  757. VerifySessionAndCancellationToken(call, session, cancellationToken);
  758. VerifySingleWrite(call, null, true, processedRequest);
  759. }
  760. [Theory]
  761. [ParameterAttributeData]
  762. public void DeleteMany_should_throw_a_WriteException_when_an_error_occurs(
  763. [Values(false, true)] bool usingSession,
  764. [Values(false, true)] bool async)
  765. {
  766. var subject = CreateSubject<BsonDocument>();
  767. var session = CreateSession(usingSession);
  768. var filter = new BsonDocument("a", 1);
  769. var options = new DeleteOptions();
  770. var cancellationToken = new CancellationTokenSource().Token;
  771. var processedRequest = new DeleteRequest(filter) { CorrelationId = 0, Limit = 0 };
  772. var operationException = new MongoBulkWriteOperationException(
  773. _connectionId,
  774. new BulkWriteOperationResult.Acknowledged(
  775. requestCount: 1,
  776. matchedCount: 1,
  777. deletedCount: 0,
  778. insertedCount: 0,
  779. modifiedCount: 0,
  780. processedRequests: new[] { processedRequest },
  781. upserts: new List<BulkWriteOperationUpsert>()),
  782. new[] { new BulkWriteOperationError(10, 1, "blah", new BsonDocument()) },
  783. null,
  784. new List<WriteRequest>());
  785. _operationExecutor.EnqueueException<BulkWriteOperationResult>(operationException);
  786. Exception exception;
  787. if (usingSession)
  788. {
  789. if (async)
  790. {
  791. exception = Record.Exception(() => subject.DeleteManyAsync(session, filter, options, cancellationToken).GetAwaiter().GetResult());
  792. }
  793. else
  794. {
  795. exception = Record.Exception(() => subject.DeleteMany(session, filter, options, cancellationToken));
  796. }
  797. }
  798. else
  799. {
  800. if (async)
  801. {
  802. exception = Record.Exception(() => subject.DeleteManyAsync(filter, options, cancellationToken).GetAwaiter().GetResult());
  803. }
  804. else
  805. {
  806. exception = Record.Exception(() => subject.DeleteMany(filter, options, cancellationToken));
  807. }
  808. }
  809. var call = _operationExecutor.GetWriteCall<BulkWriteOperationResult>();
  810. VerifySessionAndCancellationToken(call, session, cancellationToken);
  811. exception.Should().BeOfType<MongoWriteException>();
  812. }
  813. [Theory]
  814. [ParameterAttributeData]
  815. public void DeleteOne_should_execute_a_BulkMixedOperation(
  816. [Values(false, true)] bool usingSession,
  817. [Values(false, true)] bool async)
  818. {
  819. var subject = CreateSubject<BsonDocument>();
  820. var session = CreateSession(usingSession);
  821. var filter = new BsonDocument("a", 1);
  822. var collation = new Collation("en_US");
  823. var hint = new BsonDocument("_id", 1);
  824. var options = new DeleteOptions { Collation = collation };
  825. var cancellationToken = new CancellationTokenSource().Token;
  826. var processedRequest = new DeleteRequest(filter)
  827. {
  828. Collation = collation,
  829. CorrelationId = 0,
  830. Hint = hint,
  831. Limit = 1
  832. };
  833. var operationResult = new BulkWriteOperationResult.Unacknowledged(9, new[] { processedRequest });
  834. _operationExecutor.EnqueueResult<BulkWriteOperationResult>(operationResult);
  835. if (usingSession)
  836. {
  837. if (async)
  838. {
  839. subject.DeleteOneAsync(session, filter, options, cancellationToken).GetAwaiter().GetResult();
  840. }
  841. else
  842. {
  843. subject.DeleteOne(session, filter, options, cancellationToken);
  844. }
  845. }
  846. else
  847. {
  848. if (async)
  849. {
  850. subject.DeleteOneAsync(filter, options, cancellationToken).GetAwaiter().GetResult();
  851. }
  852. else
  853. {
  854. subject.DeleteOne(filter, options, cancellationToken);
  855. }
  856. }
  857. var call = _operationExecutor.GetWriteCall<BulkWriteOperationResult>();
  858. VerifySessionAndCancellationToken(call, session, cancellationToken);
  859. VerifySingleWrite(call, null, true, processedRequest);
  860. }
  861. [Theory]
  862. [ParameterAttributeData]
  863. public void DeleteOne_should_throw_a_WriteException_when_an_error_occurs(
  864. [Values(false, true)] bool usingSession,
  865. [Values(false, true)] bool async)
  866. {
  867. var subject = CreateSubject<BsonDocument>();
  868. var session = CreateSession(usingSession);
  869. var filter = new BsonDocument("a", 1);
  870. var options = new DeleteOptions();
  871. var cancellationToken = new CancellationTokenSource().Token;
  872. var processedRequest = new DeleteRequest(filter) { CorrelationId = 0, Limit = 1 };
  873. var operationException = new MongoBulkWriteOperationException(
  874. _connectionId,
  875. new BulkWriteOperationResult.Acknowledged(
  876. requestCount: 1,
  877. matchedCount: 1,
  878. deletedCount: 0,
  879. insertedCount: 0,
  880. modifiedCount: 0,
  881. processedRequests: new[] { processedRequest },
  882. upserts: new List<BulkWriteOperationUpsert>()),
  883. new[] { new BulkWriteOperationError(0, 1, "blah", new BsonDocument()) },
  884. null,
  885. new List<WriteRequest>());
  886. _operationExecutor.EnqueueException<BulkWriteOperationResult>(operationException);
  887. Exception exception;
  888. if (usingSession)
  889. {
  890. if (async)
  891. {
  892. exception = Record.Exception(() => subject.DeleteOneAsync(session, filter, options, cancellationToken).GetAwaiter().GetResult());
  893. }
  894. else
  895. {
  896. exception = Record.Exception(() => subject.DeleteOne(session, filter, options, cancellationToken));
  897. }
  898. }
  899. else
  900. {
  901. if (async)
  902. {
  903. exception = Record.Exception(() => subject.DeleteOneAsync(filter, options, cancellationToken).GetAwaiter().GetResult());
  904. }
  905. else
  906. {
  907. exception = Record.Exception(() => subject.DeleteOne(filter, options, cancellationToken));
  908. }
  909. }
  910. var call = _operationExecutor.GetWriteCall<BulkWriteOperationResult>();
  911. VerifySessionAndCancellationToken(call, session, cancellationToken);
  912. exception.Should().BeOfType<MongoWriteException>();
  913. }
  914. [Theory]
  915. [ParameterAttributeData]
  916. public void Distinct_should_execute_a_DistinctOperation(
  917. [Values(false, true)] bool usingSession,
  918. [Values(false, true)] bool async)
  919. {
  920. var subject = CreateSubject<BsonDocument>();
  921. var session = CreateSession(usingSession);
  922. var fieldName = "a.b";
  923. var fieldDefinition = (FieldDefinition<BsonDocument, int>)fieldName;
  924. var filterDocument = new BsonDocument("x", 1);
  925. var filterDefinition = (FilterDefinition<BsonDocument>)filterDocument;
  926. var options = new DistinctOptions
  927. {
  928. Collation = new Collation("en_US"),
  929. MaxTime = TimeSpan.FromSeconds(20)
  930. };
  931. var cancellationToken = new CancellationTokenSource().Token;
  932. if (usingSession)
  933. {
  934. if (async)
  935. {
  936. subject.DistinctAsync(session, fieldDefinition, filterDefinition, options, cancellationToken).GetAwaiter().GetResult();
  937. }
  938. else
  939. {
  940. subject.Distinct(session, fieldDefinition, filterDefinition, options, cancellationToken);
  941. }
  942. }
  943. else
  944. {
  945. if (async)
  946. {
  947. subject.DistinctAsync(fieldDefinition, filterDefinition, options, cancellationToken).GetAwaiter().GetResult();
  948. }
  949. else
  950. {
  951. subject.Distinct(fieldDefinition, filterDefinition, options, cancellationToken);
  952. }
  953. }
  954. var call = _operationExecutor.GetReadCall<IAsyncCursor<int>>();
  955. VerifySessionAndCancellationToken(call, session, cancellationToken);
  956. var operation = call.Operation.Should().BeOfType<DistinctOperation<int>>().Subject;
  957. operation.Collation.Should().BeSameAs(options.Collation);
  958. operation.CollectionNamespace.Should().Be(subject.CollectionNamespace);
  959. operation.FieldName.Should().Be(fieldName);
  960. operation.Filter.Should().Be(filterDocument);
  961. operation.MaxTime.Should().Be(options.MaxTime);
  962. operation.ReadConcern.Should().Be(_readConcern);
  963. operation.RetryRequested.Should().BeTrue();
  964. operation.ValueSerializer.ValueType.Should().Be(typeof(int));
  965. }
  966. private enum EnumForDistinctWithArrayField { A, B }
  967. private class ClassForDistinctWithArrayField
  968. {
  969. public int Id { get; set; }
  970. [BsonRepresentation(BsonType.String)]
  971. public EnumForDistinctWithArrayField[] A { get; set; }
  972. }
  973. [Theory]
  974. [ParameterAttributeData]
  975. public void Distinct_should_execute_a_DistinctOperation_when_type_parameter_is_array_field_item_type(
  976. [Values(false, true)] bool usingSession,
  977. [Values(false, true)] bool async)
  978. {
  979. var subject = CreateSubject<ClassForDistinctWithArrayField>();
  980. var session = CreateSession(usingSession);
  981. var fieldName = "A";
  982. var fieldDefinition = (FieldDefinition<ClassForDistinctWithArrayField, EnumForDistinctWithArrayField>)fieldName;
  983. var filterDocument = new BsonDocument("x", 1);
  984. var filterDefinition = (FilterDefinition<ClassForDistinctWithArrayField>)filterDocument;
  985. var options = new DistinctOptions
  986. {
  987. Collation = new Collation("en_US"),
  988. MaxTime = TimeSpan.FromSeconds(20)
  989. };
  990. var cancellationToken = new CancellationTokenSource().Token;
  991. if (usingSession)
  992. {
  993. if (async)
  994. {
  995. subject.DistinctAsync(session, fieldDefinition, filterDefinition, options, cancellationToken).GetAwaiter().GetResult();
  996. }
  997. else
  998. {
  999. subject.Distinct(session, fieldDefinition, filterDefinition, options, cancellationToken);
  1000. }
  1001. }
  1002. else
  1003. {
  1004. if (async)
  1005. {
  1006. subject.DistinctAsync(fieldDefinition, filterDefinition, options, cancellationToken).GetAwaiter().GetResult();
  1007. }
  1008. else
  1009. {
  1010. subject.Distinct(fieldDefinition, filterDefinition, options, cancellationToken);
  1011. }
  1012. }
  1013. var call = _operationExecutor.GetReadCall<IAsyncCursor<EnumForDistinctWithArrayField>>();
  1014. VerifySessionAndCancellationToken(call, session, cancellationToken);
  1015. var operation = call.Operation.Should().BeOfType<DistinctOperation<EnumForDistinctWithArrayField>>().Subject;
  1016. operation.Collation.Should().BeSameAs(options.Collation);
  1017. operation.CollectionNamespace.Should().Be(subject.CollectionNamespace);
  1018. operation.FieldName.Should().Be(fieldName);
  1019. operation.Filter.Should().Be(filterDocument);
  1020. operation.MaxTime.Should().Be(options.MaxTime);
  1021. operation.ReadConcern.Should().Be(_readConcern);
  1022. operation.RetryRequested.Should().BeTrue();
  1023. var documentSerializer = BsonSerializer.SerializerRegistry.GetSerializer<ClassForDistinctWithArrayField>();
  1024. BsonSerializationInfo fieldSerializationInfo;
  1025. ((IBsonDocumentSerializer)documentSerializer).TryGetMemberSerializationInfo(fieldName, out fieldSerializationInfo).Should().BeTrue();
  1026. var fieldSerializer = (ArraySerializer<EnumForDistinctWithArrayField>)fieldSerializationInfo.Serializer;
  1027. operation.ValueSerializer.Should().BeSameAs(fieldSerializer.ItemSerializer);
  1028. }
  1029. [Theory]
  1030. [ParameterAttributeData]
  1031. public void Distinct_should_execute_a_DistinctOperation_when_type_parameter_is_string_instead_of_enum(
  1032. [Values(false, true)] bool usingSession,
  1033. [Values(false, true)] bool async)
  1034. {
  1035. var subject = CreateSubject<ClassForDistinctWithArrayField>();
  1036. var session = CreateSession(usingSession);
  1037. var fieldName = "A";
  1038. var fieldDefinition = (FieldDefinition<ClassForDistinctWithArrayField, string>)fieldName;
  1039. var filterDocument = new BsonDocument("x", 1);
  1040. var filterDefinition = (FilterDefinition<ClassForDistinctWithArrayField>)filterDocument;
  1041. var options = new DistinctOptions
  1042. {
  1043. Collation = new Collation("en_US"),
  1044. MaxTime = TimeSpan.FromSeconds(20)
  1045. };
  1046. var cancellationToken = new CancellationTokenSource().Token;
  1047. if (usingSession)
  1048. {
  1049. if (async)
  1050. {
  1051. subject.DistinctAsync(session, fieldDefinition, filterDefinition, options, cancellationToken).GetAwaiter().GetResult();
  1052. }
  1053. else
  1054. {
  1055. subject.Distinct(session, fieldDefinition, filterDefinition, options, cancellationToken);
  1056. }
  1057. }
  1058. else
  1059. {
  1060. if (async)
  1061. {
  1062. subject.DistinctAsync(fieldDefinition, filterDefinition, options, cancellationToken).GetAwaiter().GetResult();
  1063. }
  1064. else
  1065. {
  1066. subject.Distinct(fieldDefinition, filterDefinition, options, cancellationToken);
  1067. }
  1068. }
  1069. var call = _operationExecutor.GetReadCall<IAsyncCursor<string>>();
  1070. VerifySessionAndCancellationToken(call, session, cancellationToken);
  1071. var operation = call.Operation.Should().BeOfType<DistinctOperation<string>>().Subject;
  1072. operation.Collation.Should().BeSameAs(options.Collation);
  1073. operation.CollectionNamespace.Should().Be(subject.CollectionNamespace);
  1074. operation.FieldName.Should().Be(fieldName);
  1075. operation.Filter.Should().Be(filterDocument);
  1076. operation.MaxTime.Should().Be(options.MaxTime);
  1077. operation.ReadConcern.Should().Be(_readConcern);
  1078. operation.RetryRequested.Should().BeTrue();
  1079. var stringSerializer = BsonSerializer.SerializerRegistry.GetSerializer<string>();
  1080. operation.ValueSerializer.Should().BeSameAs(stringSerializer);
  1081. }
  1082. [Theory]
  1083. [ParameterAttributeData]
  1084. public void EstimatedDocumentCount_should_execute_a_CountOperation(
  1085. [Values(false, true)] bool async)
  1086. {
  1087. var subject = CreateSubject<BsonDocument>();
  1088. var options = new EstimatedDocumentCountOptions
  1089. {
  1090. MaxTime = TimeSpan.FromSeconds(20)
  1091. };
  1092. var cancellationToken = new CancellationTokenSource().Token;
  1093. if (async)
  1094. {
  1095. subject.EstimatedDocumentCountAsync(options, cancellationToken).GetAwaiter().GetResult();
  1096. }
  1097. else
  1098. {
  1099. subject.EstimatedDocumentCount(options, cancellationToken);
  1100. }
  1101. var call = _operationExecutor.GetReadCall<long>();
  1102. VerifySessionAndCancellationToken(call, null, cancellationToken);
  1103. var operation = call.Operation.Should().BeOfType<CountOperation>().Subject;
  1104. operation.Collation.Should().BeNull();
  1105. operation.CollectionNamespace.Should().Be(subject.CollectionNamespace);
  1106. operation.Filter.Should().BeNull();
  1107. operation.Hint.Should().BeNull();
  1108. operation.Limit.Should().NotHaveValue();
  1109. operation.MaxTime.Should().Be(options.MaxTime);
  1110. operation.ReadConcern.Should().Be(ReadConcern.Default);
  1111. operation.RetryRequested.Should().BeTrue();
  1112. operation.Skip.Should().NotHaveValue();
  1113. }
  1114. [Theory]
  1115. [ParameterAttributeData]
  1116. public void Find_should_execute_a_FindOperation(
  1117. [Values(false, true)] bool usingSession,
  1118. [Values(false, true)] bool async)
  1119. {
  1120. var subject = CreateSubject<BsonDocument>();
  1121. var session = CreateSession(usingSession);
  1122. var filterDocument = BsonDocument.Parse("{ x : 1 }");
  1123. var filterDefinition = (FilterDefinition<BsonDocument>)filterDocument;
  1124. var projectionDocument = BsonDocument.Parse("{ y : 1 }");
  1125. var projectionDefinition = (ProjectionDefinition<BsonDocument, BsonDocument>)projectionDocument;
  1126. var sortDocument = BsonDocument.Parse("{ a : 1 }");
  1127. var sortDefinition = (SortDefinition<BsonDocument>)sortDocument;
  1128. var options = new FindOptions<BsonDocument, BsonDocument>
  1129. {
  1130. AllowDiskUse = true,
  1131. AllowPartialResults = true,
  1132. BatchSize = 20,
  1133. Collation = new Collation("en_US"),
  1134. Comment = "funny",
  1135. CursorType = CursorType.TailableAwait,
  1136. Limit = 30,
  1137. MaxAwaitTime = TimeSpan.FromSeconds(4),
  1138. MaxTime = TimeSpan.FromSeconds(3),
  1139. #pragma warning disable 618
  1140. Modifiers = BsonDocument.Parse("{ $snapshot : true }"),
  1141. #pragma warning restore 618
  1142. NoCursorTimeout = true,
  1143. #pragma warning disable 618
  1144. OplogReplay = true,
  1145. #pragma warning restore 618
  1146. Projection = projectionDefinition,
  1147. Skip = 40,
  1148. Sort = sortDefinition
  1149. };
  1150. var cancellationToken = new CancellationTokenSource().Token;
  1151. if (usingSession)
  1152. {
  1153. if (async)
  1154. {
  1155. subject.FindAsync(session, filterDefinition, options, cancellationToken).GetAwaiter().GetResult();
  1156. }
  1157. else
  1158. {
  1159. subject.FindSync(session, filterDefinition, options, cancellationToken);
  1160. }
  1161. }
  1162. else
  1163. {
  1164. if (async)
  1165. {
  1166. subject.FindAsync(filterDefinition, options, cancellationToken).GetAwaiter().GetResult();
  1167. }
  1168. else
  1169. {
  1170. subject.FindSync(filterDefinition, options, cancellationToken);
  1171. }
  1172. }
  1173. var call = _operationExecutor.GetReadCall<IAsyncCursor<BsonDocument>>();
  1174. VerifySessionAndCancellationToken(call, session, cancellationToken);
  1175. call.Operation.Should().BeOfType<FindOperation<BsonDocument>>();
  1176. var operation = (FindOperation<BsonDocument>)call.Operation;
  1177. operation.AllowDiskUse.Should().Be(options.AllowDiskUse);
  1178. operation.AllowPartialResults.Should().Be(options.AllowPartialResults);
  1179. operation.BatchSize.Should().Be(options.BatchSize);
  1180. operation.Collation.Should().BeSameAs(options.Collation);
  1181. operation.CollectionNamespace.Should().Be(subject.CollectionNamespace);
  1182. operation.Comment.Should().Be("funny");
  1183. operation.CursorType.Should().Be(MongoDB.Driver.Core.Operations.CursorType.TailableAwait);
  1184. operation.Filter.Should().Be(filterDocument);
  1185. operation.Limit.Should().Be(options.Limit);
  1186. operation.MaxAwaitTime.Should().Be(options.MaxAwaitTime);
  1187. operation.MaxTime.Should().Be(options.MaxTime);
  1188. #pragma warning disable 618
  1189. operation.Modifiers.Should().Be(options.Modifiers);
  1190. #pragma warning restore 618
  1191. operation.NoCursorTimeout.Should().Be(options.NoCursorTimeout);
  1192. #pragma warning disable 618
  1193. operation.OplogReplay.Should().Be(options.OplogReplay);
  1194. #pragma warning restore 618
  1195. operation.Projection.Should().Be(projectionDocument);
  1196. operation.ReadConcern.Should().Be(_readConcern);
  1197. operation.ResultSerializer.ValueType.Should().Be(typeof(BsonDocument));
  1198. operation.RetryRequested.Should().BeTrue();
  1199. operation.Skip.Should().Be(options.Skip);
  1200. operation.Sort.Should().Be(sortDocument);
  1201. }
  1202. [Theory]
  1203. [ParameterAttributeData]
  1204. public void Find_with_an_expression_execute_a_FindOperation(
  1205. [Values(false, true)] bool usingSession,
  1206. [Values(false, true)] bool async)
  1207. {
  1208. var subject = CreateSubject<BsonDocument>();
  1209. var session = CreateSession(usingSession);
  1210. var filterExpression = (Expression<Func<BsonDocument, bool>>)(doc => doc["x"] == 1);
  1211. var projectionDocument = BsonDocument.Parse("{ y : 1 }");
  1212. var projectionDefinition = (ProjectionDefinition<BsonDocument, BsonDocument>)projectionDocument;
  1213. var sortDocument = BsonDocument.Parse("{ a : 1 }");
  1214. var sortDefinition = (SortDefinition<BsonDocument>)sortDocument;
  1215. var options = new FindOptions<BsonDocument, BsonDocument>
  1216. {
  1217. AllowDiskUse = true,
  1218. AllowPartialResults = true,
  1219. BatchSize = 20,
  1220. Collation = new Collation("en_US"),
  1221. Comment = "funny",
  1222. CursorType = CursorType.TailableAwait,
  1223. Limit = 30,
  1224. MaxAwaitTime = TimeSpan.FromSeconds(4),
  1225. MaxTime = TimeSpan.FromSeconds(3),
  1226. #pragma warning disable 618
  1227. Modifiers = BsonDocument.Parse("{ $snapshot : true }"),
  1228. #pragma warning restore 618
  1229. NoCursorTimeout = true,
  1230. #pragma warning disable 618
  1231. OplogReplay = true,
  1232. #pragma warning restore 618
  1233. Projection = projectionDefinition,
  1234. Skip = 40,
  1235. Sort = sortDefinition
  1236. };
  1237. var cancellationToken = new CancellationTokenSource().Token;
  1238. if (usingSession)
  1239. {
  1240. if (async)
  1241. {
  1242. subject.FindAsync(session, filterExpression, options, cancellationToken).GetAwaiter().GetResult();
  1243. }
  1244. else
  1245. {
  1246. subject.FindSync(session, filterExpression, options, cancellationToken);
  1247. }
  1248. }
  1249. else
  1250. {
  1251. if (async)
  1252. {
  1253. subject.FindAsync(filterExpression, options, cancellationToken).GetAwaiter().GetResult();
  1254. }
  1255. else
  1256. {
  1257. subject.FindSync(filterExpression, options, cancellationToken);
  1258. }
  1259. }
  1260. var call = _operationExecutor.GetReadCall<IAsyncCursor<BsonDocument>>();
  1261. VerifySessionAndCancellationToken(call, session, cancellationToken);
  1262. var operation = call.Operation.Should().BeOfType<FindOperation<BsonDocument>>().Subject;
  1263. operation.AllowDiskUse.Should().Be(options.AllowDiskUse);
  1264. operation.AllowPartialResults.Should().Be(options.AllowPartialResults);
  1265. operation.BatchSize.Should().Be(options.BatchSize);
  1266. operation.Collation.Should().BeSameAs(options.Collation);
  1267. operation.CollectionNamespace.Should().Be(subject.CollectionNamespace);
  1268. operation.Comment.Should().Be("funny");
  1269. operation.CursorType.Should().Be(MongoDB.Driver.Core.Operations.CursorType.TailableAwait);
  1270. operation.Filter.Should().Be(new BsonDocument("x", 1));
  1271. operation.Limit.Should().Be(options.Limit);
  1272. operation.MaxAwaitTime.Should().Be(options.MaxAwaitTime);
  1273. operation.MaxTime.Should().Be(options.MaxTime);
  1274. #pragma warning disable 618
  1275. operation.Modifiers.Should().Be(options.Modifiers);
  1276. #pragma warning restore 618
  1277. operation.NoCursorTimeout.Should().Be(options.NoCursorTimeout);
  1278. #pragma warning disable 618
  1279. operation.OplogReplay.Should().Be(options.OplogReplay);
  1280. #pragma warning restore 618
  1281. operation.Projection.Should().Be(projectionDocument);
  1282. operation.ReadConcern.Should().Be(_readConcern);
  1283. operation.ResultSerializer.ValueType.Should().Be(typeof(BsonDocument));
  1284. operation.RetryRequested.Should().BeTrue();
  1285. operation.Skip.Should().Be(options.Skip);
  1286. operation.Sort.Should().Be(sortDocument);
  1287. }
  1288. [Theory]
  1289. [ParameterAttributeData]
  1290. public void Find_with_Projection_As_should_execute_correctly(
  1291. [Values(false, true)] bool usingSession,
  1292. [Values(false, true)] bool async)
  1293. {
  1294. var subject = CreateSubject<A>();
  1295. var session = CreateSession(usingSession);
  1296. var filterDefinition = Builders<A>.Filter.Empty;
  1297. var options = new FindOptions<A, BsonDocument>
  1298. {
  1299. Projection = Builders<A>.Projection.As<BsonDocument>()
  1300. };
  1301. var cancellationToken = new CancellationTokenSource().Token;
  1302. if (usingSession)
  1303. {
  1304. if (async)
  1305. {
  1306. subject.FindAsync(session, filterDefinition, options, cancellationToken).GetAwaiter().GetResult();
  1307. }
  1308. else
  1309. {
  1310. subject.FindSync(session, filterDefinition, options, cancellationToken);
  1311. }
  1312. }
  1313. else
  1314. {
  1315. if (async)
  1316. {
  1317. subject.FindAsync(filterDefinition, options, cancellationToken).GetAwaiter().GetResult();
  1318. }
  1319. else
  1320. {
  1321. subject.FindSync(filterDefinition, options, cancellationToken);
  1322. }
  1323. }
  1324. var call = _operationExecutor.GetReadCall<IAsyncCursor<BsonDocument>>();
  1325. VerifySessionAndCancellationToken(call, session, cancellationToken);
  1326. var operation = call.Operation.Should().BeOfType<FindOperation<BsonDocument>>().Subject;
  1327. operation.Projection.Should().BeNull();
  1328. operation.ResultSerializer.Should().BeOfType<BsonDocumentSerializer>();
  1329. operation.ReadConcern.Should().Be(_readConcern);
  1330. }
  1331. [Theory]
  1332. [ParameterAttributeData]
  1333. public void FindOneAndDelete_should_execute_a_FindOneAndDeleteOperation(
  1334. [Values(false, true)] bool usingSession,
  1335. [Values(false, true)] bool async)
  1336. {
  1337. var subject = CreateSubject<BsonDocument>();
  1338. var session = CreateSession(usingSession);
  1339. var filterDocument = BsonDocument.Parse("{ x : 1 }");
  1340. var filterDefinition = (FilterDefinition<BsonDocument>)filterDocument;
  1341. var projectionDocument = BsonDocument.Parse("{ x : 1 }");
  1342. var projectionDefinition = (ProjectionDefinition<BsonDocument, BsonDocument>)projectionDocument;
  1343. var sortDocument = BsonDocument.Parse("{ a : -1 } ");
  1344. var sortDefinition = (SortDefinition<BsonDocument>)sortDocument;
  1345. var options = new FindOneAndDeleteOptions<BsonDocument, BsonDocument>
  1346. {
  1347. Collation = new Collation("en_US"),
  1348. Projection = projectionDefinition,
  1349. Sort = sortDefinition,
  1350. MaxTime = TimeSpan.FromSeconds(2)
  1351. };
  1352. var cancellationToken = new CancellationTokenSource().Token;
  1353. if (usingSession)
  1354. {
  1355. if (async)
  1356. {
  1357. subject.FindOneAndDeleteAsync(session, filterDefinition, options, cancellationToken).GetAwaiter().GetResult();
  1358. }
  1359. else
  1360. {
  1361. subject.FindOneAndDelete(session, filterDefinition, options, cancellationToken);
  1362. }
  1363. }
  1364. else
  1365. {
  1366. if (async)
  1367. {
  1368. subject.FindOneAndDeleteAsync(filterDefinition, options, cancellationToken).GetAwaiter().GetResult();
  1369. }
  1370. else
  1371. {
  1372. subject.FindOneAndDelete(filterDefinition, options, cancellationToken);
  1373. }
  1374. }
  1375. var call = _operationExecutor.GetWriteCall<BsonDocument>();
  1376. VerifySessionAndCancellationToken(call, session, cancellationToken);
  1377. var operation = call.Operation.Should().BeOfType<FindOneAndDeleteOperation<BsonDocument>>().Subject;
  1378. operation.Collation.Should().BeSameAs(options.Collation);
  1379. operation.CollectionNamespace.Should().Be(subject.CollectionNamespace);
  1380. operation.Filter.Should().Be(filterDocument);
  1381. operation.MaxTime.Should().Be(options.MaxTime);
  1382. operation.Projection.Should().Be(projectionDocument);
  1383. operation.ResultSerializer.Should().BeOfType<FindAndModifyValueDeserializer<BsonDocument>>();
  1384. operation.Sort.Should().Be(sortDocument);
  1385. operation.WriteConcern.Should().BeSameAs(subject.Settings.WriteConcern);
  1386. }
  1387. [Theory]
  1388. [ParameterAttributeData]
  1389. public void FindOneAndDelete_with_Projection_As_should_execute_correctly(
  1390. [Values(false, true)] bool usingSession,
  1391. [Values(false, true)] bool async)
  1392. {
  1393. var subject = CreateSubject<A>();
  1394. var session = CreateSession(usingSession);
  1395. var filterDefinition = Builders<A>.Filter.Empty;
  1396. var options = new FindOneAndDeleteOptions<A, BsonDocument>
  1397. {
  1398. Collation = new Collation("en_US"),
  1399. Hint = new BsonDocument("_id", 1),
  1400. Projection = Builders<A>.Projection.As<BsonDocument>()
  1401. };
  1402. var cancellationToken = new CancellationTokenSource().Token;
  1403. if (usingSession)
  1404. {
  1405. if (async)
  1406. {
  1407. subject.FindOneAndDeleteAsync(session, filterDefinition, options, cancellationToken).GetAwaiter().GetResult();
  1408. }
  1409. else
  1410. {
  1411. subject.FindOneAndDelete(session, filterDefinition, options, cancellationToken);
  1412. }
  1413. }
  1414. else
  1415. {
  1416. if (async)
  1417. {
  1418. subject.FindOneAndDeleteAsync(filterDefinition, options, cancellationToken).GetAwaiter().GetResult();
  1419. }
  1420. else
  1421. {
  1422. subject.FindOneAndDelete(filterDefinition, options, cancellationToken);
  1423. }
  1424. }
  1425. var call = _operationExecutor.GetWriteCall<BsonDocument>();
  1426. VerifySessionAndCancellationToken(call, session, cancellationToken);
  1427. var operation = call.Operation.Should().BeOfType<FindOneAndDeleteOperation<BsonDocument>>().Subject;
  1428. operation.Collation.Should().BeSameAs(options.Collation);
  1429. operation.Hint.Should().Be(operation.Hint);
  1430. operation.Projection.Should().BeNull();
  1431. operation.ResultSerializer.Should().BeOfType<FindAndModifyValueDeserializer<BsonDocument>>();
  1432. }
  1433. [Theory]
  1434. [ParameterAttributeData]
  1435. public void FindOneAndReplace_should_execute_a_FindOneAndReplaceOperation(
  1436. [Values(false, true)] bool usingSession,
  1437. [Values(null, false, true)] bool? bypassDocumentValidation,
  1438. [Values(false, true)] bool isUpsert,
  1439. [Values(ReturnDocument.After, ReturnDocument.Before)] ReturnDocument returnDocument,
  1440. [Values(false, true)] bool async)
  1441. {
  1442. var subject = CreateSubject<BsonDocument>();
  1443. var session = CreateSession(usingSession);
  1444. var filterDocument = BsonDocument.Parse("{ x : 1 }");
  1445. var filterDefinition = (FilterDefinition<BsonDocument>)filterDocument;
  1446. var replacement = BsonDocument.Parse("{ a : 2 }");
  1447. var projectionDocument = BsonDocument.Parse("{ x : 1 }");
  1448. var projectionDefinition = (ProjectionDefinition<BsonDocument, BsonDocument>)projectionDocument;
  1449. var sortDocument = BsonDocument.Parse("{ a : -1 }");
  1450. var sortDefinition = (SortDefinition<BsonDocument>)sortDocument;
  1451. var options = new FindOneAndReplaceOptions<BsonDocument, BsonDocument>()
  1452. {
  1453. BypassDocumentValidation = bypassDocumentValidation,
  1454. Collation = new Collation("en_US"),
  1455. Hint = new BsonDocument("_id", 1),
  1456. IsUpsert = isUpsert,
  1457. MaxTime = TimeSpan.FromSeconds(2),
  1458. Projection = projectionDefinition,
  1459. ReturnDocument = returnDocument,
  1460. Sort = sortDefinition
  1461. };
  1462. var cancellationToken = new CancellationTokenSource().Token;
  1463. if (usingSession)
  1464. {
  1465. if (async)
  1466. {
  1467. subject.FindOneAndReplaceAsync(session, filterDefinition, replacement, options, cancellationToken).GetAwaiter().GetResult();
  1468. }
  1469. else
  1470. {
  1471. subject.FindOneAndReplace(session, filterDefinition, replacement, options, cancellationToken);
  1472. }
  1473. }
  1474. else
  1475. {
  1476. if (async)
  1477. {
  1478. subject.FindOneAndReplaceAsync(filterDefinition, replacement, options, cancellationToken).GetAwaiter().GetResult();
  1479. }
  1480. else
  1481. {
  1482. subject.FindOneAndReplace(filterDefinition, replacement, options, cancellationToken);
  1483. }
  1484. }
  1485. var call = _operationExecutor.GetWriteCall<BsonDocument>();
  1486. VerifySessionAndCancellationToken(call, session, cancellationToken);
  1487. var operation = call.Operation.Should().BeOfType<FindOneAndReplaceOperation<BsonDocument>>().Subject;
  1488. operation.BypassDocumentValidation.Should().Be(bypassDocumentValidation);
  1489. operation.Collation.Should().BeSameAs(options.Collation);
  1490. operation.CollectionNamespace.Should().Be(subject.CollectionNamespace);
  1491. operation.Hint.Should().Be(options.Hint);
  1492. operation.Filter.Should().Be(filterDocument);
  1493. operation.IsUpsert.Should().Be(isUpsert);
  1494. operation.MaxTime.Should().Be(options.MaxTime);
  1495. operation.Projection.Should().Be(projectionDocument);
  1496. operation.Replacement.Should().Be(replacement);
  1497. operation.ResultSerializer.Should().BeOfType<FindAndModifyValueDeserializer<BsonDocument>>();
  1498. operation.ReturnDocument.Should().Be((Core.Operations.ReturnDocument)returnDocument);
  1499. operation.Sort.Should().Be(sortDocument);
  1500. operation.WriteConcern.Should().BeSameAs(subject.Settings.WriteConcern);
  1501. }
  1502. [Theory]
  1503. [ParameterAttributeData]
  1504. public void FindOneAndReplace_with_Projection_As_should_execute_correctly(
  1505. [Values(false, true)] bool usingSession,
  1506. [Values(false, true)] bool async)
  1507. {
  1508. var subject = CreateSubject<A>();
  1509. var session = CreateSession(usingSession);
  1510. var filterDefinition = Builders<A>.Filter.Empty;
  1511. var replacement = new A();
  1512. var options = new FindOneAndReplaceOptions<A, BsonDocument>
  1513. {
  1514. Collation = new Collation("en_US"),
  1515. Projection = Builders<A>.Projection.As<BsonDocument>()
  1516. };
  1517. var cancellationToken = new CancellationTokenSource().Token;
  1518. if (usingSession)
  1519. {
  1520. if (async)
  1521. {
  1522. subject.FindOneAndReplaceAsync(session, filterDefinition, replacement, options, cancellationToken).GetAwaiter().GetResult();
  1523. }
  1524. else
  1525. {
  1526. subject.FindOneAndReplace(session, filterDefinition, replacement, options, cancellationToken);
  1527. }
  1528. }
  1529. else
  1530. {
  1531. if (async)
  1532. {
  1533. subject.FindOneAndReplaceAsync(filterDefinition, replacement, options, cancellationToken).GetAwaiter().GetResult();
  1534. }
  1535. else
  1536. {
  1537. subject.FindOneAndReplace(filterDefinition, replacement, options, cancellationToken);
  1538. }
  1539. }
  1540. var call = _operationExecutor.GetWriteCall<BsonDocument>();
  1541. VerifySessionAndCancellationToken(call, session, cancellationToken);
  1542. var operation = call.Operation.Should().BeOfType<FindOneAndReplaceOperation<BsonDocument>>().Subject;
  1543. operation.Collation.Should().BeSameAs(options.Collation);
  1544. operation.Projection.Should().BeNull();
  1545. operation.ResultSerializer.Should().BeOfType<FindAndModifyValueDeserializer<BsonDocument>>();
  1546. }
  1547. [Theory]
  1548. [ParameterAttributeData]
  1549. public void FindOneAndUpdate_should_execute_a_FindOneAndUpdateOperation(
  1550. [Values(false, true)] bool usingSession,
  1551. [Values(null, false, true)] bool? bypassDocumentValidation,
  1552. [Values(false, true)] bool isUpsert,
  1553. [Values(ReturnDocument.After, ReturnDocument.Before)] ReturnDocument returnDocument,
  1554. [Values(false, true)] bool async)
  1555. {
  1556. var subject = CreateSubject<BsonDocument>();
  1557. var session = CreateSession(usingSession);
  1558. var filterDocument = BsonDocument.Parse("{ x : 1 }");
  1559. var filterDefinition = (FilterDefinition<BsonDocument>)filterDocument;
  1560. var updateDocument = BsonDocument.Parse("{ $set : { a : 2 } }");
  1561. var updateDefinition = (UpdateDefinition<BsonDocument>)updateDocument;
  1562. var arrayFilterDocument = BsonDocument.Parse("{ b : 1 }");
  1563. var arrayFilterDefinition = (ArrayFilterDefinition<BsonDocument>)arrayFilterDocument;
  1564. var projectionDocument = BsonDocument.Parse("{ x : 1 }");
  1565. var projectionDefinition = (ProjectionDefinition<BsonDocument, BsonDocument>)projectionDocument;
  1566. var sortDocument = BsonDocument.Parse("{ a : -1 }");
  1567. var sortDefinition = (SortDefinition<BsonDocument>)sortDocument;
  1568. var options = new FindOneAndUpdateOptions<BsonDocument, BsonDocument>()
  1569. {
  1570. ArrayFilters = new[] { arrayFilterDefinition },
  1571. BypassDocumentValidation = bypassDocumentValidation,
  1572. Collation = new Collation("en_US"),
  1573. Hint = new BsonDocument("_id", 1),
  1574. IsUpsert = isUpsert,
  1575. MaxTime = TimeSpan.FromSeconds(2),
  1576. Projection = projectionDefinition,
  1577. ReturnDocument = returnDocument,
  1578. Sort = sortDefinition,
  1579. };
  1580. var cancellationToken = new CancellationTokenSource().Token;
  1581. if (usingSession)
  1582. {
  1583. if (async)
  1584. {
  1585. subject.FindOneAndUpdateAsync(session, filterDefinition, updateDefinition, options, cancellationToken).GetAwaiter().GetResult();
  1586. }
  1587. else
  1588. {
  1589. subject.FindOneAndUpdate(session, filterDefinition, updateDefinition, options, cancellationToken);
  1590. }
  1591. }
  1592. else
  1593. {
  1594. if (async)
  1595. {
  1596. subject.FindOneAndUpdateAsync(filterDefinition, updateDefinition, options, cancellationToken).GetAwaiter().GetResult();
  1597. }
  1598. else
  1599. {
  1600. subject.FindOneAndUpdate(filterDefinition, updateDefinition, options, cancellationToken);
  1601. }
  1602. }
  1603. var call = _operationExecutor.GetWriteCall<BsonDocument>();
  1604. VerifySessionAndCancellationToken(call, session, cancellationToken);
  1605. var operation = call.Operation.Should().BeOfType<FindOneAndUpdateOperation<BsonDocument>>().Subject;
  1606. operation.ArrayFilters.Should().Equal(new[] { arrayFilterDocument });
  1607. operation.BypassDocumentValidation.Should().Be(bypassDocumentValidation);
  1608. operation.Collation.Should().BeSameAs(options.Collation);
  1609. operation.CollectionNamespace.Should().Be(subject.CollectionNamespace);
  1610. operation.Hint.Should().Be(options.Hint);
  1611. operation.Filter.Should().Be(filterDocument);
  1612. operation.IsUpsert.Should().Be(isUpsert);
  1613. operation.MaxTime.Should().Be(options.MaxTime);
  1614. operation.Projection.Should().Be(projectionDocument);
  1615. operation.ResultSerializer.Should().BeOfType<FindAndModifyValueDeserializer<BsonDocument>>();
  1616. operation.ReturnDocument.Should().Be((Core.Operations.ReturnDocument)returnDocument);
  1617. operation.Sort.Should().Be(sortDocument);
  1618. operation.Update.Should().Be(updateDocument);
  1619. operation.WriteConcern.Should().BeSameAs(subject.Settings.WriteConcern);
  1620. }
  1621. [Theory]
  1622. [ParameterAttributeData]
  1623. public void FindOneAndUpdate_should_throw_if_parameters_are_invalid(
  1624. [Values(false)] bool async)
  1625. {
  1626. var subject = CreateSubject<BsonDocument>();
  1627. var pipeline = new BsonDocumentStagePipelineDefinition<BsonDocument, BsonDocument>(new[] { new BsonDocument("$project", "{ value : 1 }") });
  1628. var update = new PipelineUpdateDefinition<BsonDocument>(pipeline);
  1629. var filterDocument = BsonDocument.Parse("{ x : 1 }");
  1630. var filterDefinition = (FilterDefinition<BsonDocument>)filterDocument;
  1631. var arrayFilterDocument = BsonDocument.Parse("{ b : 1 }");
  1632. var arrayFilterDefinition = (ArrayFilterDefinition<BsonDocument>)arrayFilterDocument;
  1633. var options = new FindOneAndUpdateOptions<BsonDocument, BsonDocument>()
  1634. {
  1635. ArrayFilters = new[] { arrayFilterDefinition },
  1636. };
  1637. var cancellationToken = new CancellationTokenSource().Token;
  1638. Exception exception;
  1639. if (async)
  1640. {
  1641. exception = Record.ExceptionAsync(async () => { await subject.FindOneAndUpdateAsync(filterDefinition, update, options, cancellationToken); })
  1642. .GetAwaiter()
  1643. .GetResult();
  1644. }
  1645. else
  1646. {
  1647. exception = Record.Exception(() => { subject.FindOneAndUpdate(filterDefinition, update, options, cancellationToken); });
  1648. }
  1649. exception.Should().BeOfType<NotSupportedException>();
  1650. }
  1651. [Theory]
  1652. [ParameterAttributeData]
  1653. public void FindOneAndUpdate_with_Projection_As_should_execute_correctly(
  1654. [Values(false, true)] bool usingSession,
  1655. [Values(false, true)] bool async)
  1656. {
  1657. var subject = CreateSubject<A>();
  1658. var session = CreateSession(usingSession);
  1659. var filterDefinition = Builders<A>.Filter.Empty;
  1660. var updateDefinition = Builders<A>.Update.Inc(x => x.PropA, 1);
  1661. var options = new FindOneAndUpdateOptions<A, BsonDocument>
  1662. {
  1663. Projection = Builders<A>.Projection.As<BsonDocument>()
  1664. };
  1665. var cancellationToken = new CancellationTokenSource().Token;
  1666. if (usingSession)
  1667. {
  1668. if (async)
  1669. {
  1670. subject.FindOneAndUpdateAsync(session, filterDefinition, updateDefinition, options, cancellationToken).GetAwaiter().GetResult();
  1671. }
  1672. else
  1673. {
  1674. subject.FindOneAndUpdate(session, filterDefinition, updateDefinition, options, cancellationToken);
  1675. }
  1676. }
  1677. else
  1678. {
  1679. if (async)
  1680. {
  1681. subject.FindOneAndUpdateAsync(filterDefinition, updateDefinition, options, cancellationToken).GetAwaiter().GetResult();
  1682. }
  1683. else
  1684. {
  1685. subject.FindOneAndUpdate(filterDefinition, updateDefinition, options, cancellationToken);
  1686. }
  1687. }
  1688. var call = _operationExecutor.GetWriteCall<BsonDocument>();
  1689. VerifySessionAndCancellationToken(call, session, cancellationToken);
  1690. var operation = call.Operation.Should().BeOfType<FindOneAndUpdateOperation<BsonDocument>>().Subject;
  1691. operation.Projection.Should().BeNull();
  1692. operation.ResultSerializer.Should().BeOfType<FindAndModifyValueDeserializer<BsonDocument>>();
  1693. }
  1694. [Theory]
  1695. [ParameterAttributeData]
  1696. public void Indexes_CreateOne_should_execute_a_CreateIndexesOperation(
  1697. [Values(false, true)] bool usingSession,
  1698. [Values(false, true)] bool usingWildcardIndex,
  1699. [Values(null, 1, 2)] int? commitQuorumW,
  1700. [Values(null, -1, 0, 42, 9000)] int? milliseconds,
  1701. [Values(false, true)] bool usingCreateOneIndexOptions,
  1702. [Values(false, true)] bool async)
  1703. {
  1704. var writeConcern = new WriteConcern(1);
  1705. var subject = CreateSubject<BsonDocument>().WithWriteConcern(writeConcern);
  1706. var session = CreateSession(usingSession);
  1707. var keysDocument = new BsonDocument("x", 1);
  1708. var keysDefinition =
  1709. usingWildcardIndex
  1710. ? Builders<BsonDocument>.IndexKeys.Wildcard()
  1711. : keysDocument;
  1712. var partialFilterDocument = BsonDocument.Parse("{ x : { $gt : 0 } }");
  1713. var partialFilterDefinition = (FilterDefinition<BsonDocument>)partialFilterDocument;
  1714. var weights = new BsonDocument("y", 1);
  1715. var storageEngine = new BsonDocument("awesome", true);
  1716. var commitQuorum = commitQuorumW.HasValue ? CreateIndexCommitQuorum.Create(commitQuorumW.Value) : null;
  1717. var maxTime = milliseconds != null ? TimeSpan.FromMilliseconds(milliseconds.Value) : (TimeSpan?)null;
  1718. var createOneIndexOptions = usingCreateOneIndexOptions ? new CreateOneIndexOptions { CommitQuorum = commitQuorum, MaxTime = maxTime } : null;
  1719. var wildcardProjectionDefinition = Builders<BsonDocument>.Projection.Include("w");
  1720. var options = new CreateIndexOptions<BsonDocument>
  1721. {
  1722. Background = true,
  1723. Bits = 10,
  1724. #pragma warning disable 618
  1725. BucketSize = 20,
  1726. #pragma warning restore 618
  1727. Collation = new Collation("en_US"),
  1728. DefaultLanguage = "en",
  1729. ExpireAfter = TimeSpan.FromSeconds(20),
  1730. LanguageOverride = "es",
  1731. Max = 30,
  1732. Min = 40,
  1733. Name = "awesome",
  1734. PartialFilterExpression = partialFilterDefinition,
  1735. Sparse = false,
  1736. SphereIndexVersion = 50,
  1737. StorageEngine = storageEngine,
  1738. TextIndexVersion = 60,
  1739. Unique = true,
  1740. Version = 70,
  1741. Weights = weights
  1742. };
  1743. if (usingWildcardIndex)
  1744. {
  1745. options.WildcardProjection = wildcardProjectionDefinition;
  1746. }
  1747. var cancellationToken = new CancellationTokenSource().Token;
  1748. var model = new CreateIndexModel<BsonDocument>(keysDefinition, options);
  1749. if (usingSession)
  1750. {
  1751. if (async)
  1752. {
  1753. subject.Indexes.CreateOneAsync(session, model, createOneIndexOptions, cancellationToken).GetAwaiter().GetResult();
  1754. }
  1755. else
  1756. {
  1757. subject.Indexes.CreateOne(session, model, createOneIndexOptions, cancellationToken);
  1758. }
  1759. }
  1760. else
  1761. {
  1762. if (async)
  1763. {
  1764. subject.Indexes.CreateOneAsync(model, createOneIndexOptions, cancellationToken).GetAwaiter().GetResult();
  1765. }
  1766. else
  1767. {
  1768. subject.Indexes.CreateOne(model, createOneIndexOptions, cancellationToken);
  1769. }
  1770. }
  1771. var call = _operationExecutor.GetWriteCall<BsonDocument>();
  1772. VerifySessionAndCancellationToken(call, session, cancellationToken);
  1773. var operation = call.Operation.Should().BeOfType<CreateIndexesOperation>().Subject;
  1774. operation.CollectionNamespace.FullName.Should().Be("foo.bar");
  1775. operation.CommitQuorum.Should().BeSameAs(createOneIndexOptions?.CommitQuorum);
  1776. operation.MaxTime.Should().Be(createOneIndexOptions?.MaxTime);
  1777. operation.Requests.Count().Should().Be(1);
  1778. operation.WriteConcern.Should().BeSameAs(writeConcern);
  1779. var request = operation.Requests.Single();
  1780. request.AdditionalOptions.Should().BeNull();
  1781. request.Background.Should().Be(options.Background);
  1782. request.Bits.Should().Be(options.Bits);
  1783. #pragma warning disable 618
  1784. request.BucketSize.Should().Be(options.BucketSize);
  1785. #pragma warning restore 618
  1786. request.Collation.Should().BeSameAs(options.Collation);
  1787. request.DefaultLanguage.Should().Be(options.DefaultLanguage);
  1788. request.ExpireAfter.Should().Be(options.ExpireAfter);
  1789. var expectedKeysResult =
  1790. usingWildcardIndex
  1791. ? new BsonDocument("$**", 1)
  1792. : keysDocument;
  1793. request.Keys.Should().Be(expectedKeysResult);
  1794. request.LanguageOverride.Should().Be(options.LanguageOverride);
  1795. request.Max.Should().Be(options.Max);
  1796. request.Min.Should().Be(options.Min);
  1797. request.Name.Should().Be(options.Name);
  1798. request.PartialFilterExpression.Should().Be(partialFilterDocument);
  1799. request.Sparse.Should().Be(options.Sparse);
  1800. request.SphereIndexVersion.Should().Be(options.SphereIndexVersion);
  1801. request.StorageEngine.Should().Be(options.StorageEngine);
  1802. request.TextIndexVersion.Should().Be(options.TextIndexVersion);
  1803. request.Unique.Should().Be(options.Unique);
  1804. request.Version.Should().Be(options.Version);
  1805. request.Weights.Should().Be(options.Weights);
  1806. if (usingWildcardIndex)
  1807. {
  1808. var wildcardProjection = wildcardProjectionDefinition.Render(BsonDocumentSerializer.Instance, BsonSerializer.SerializerRegistry);
  1809. request.WildcardProjection.Should().Be(wildcardProjection);
  1810. }
  1811. else
  1812. {
  1813. request.WildcardProjection.Should().BeNull();
  1814. }
  1815. request.GetIndexName().Should().Be(options.Name);
  1816. }
  1817. [Theory]
  1818. [ParameterAttributeData]
  1819. public void Indexes_CreateMany_should_execute_a_CreateIndexesOperation(
  1820. [Values(false, true)] bool usingSession,
  1821. [Values(false, true)] bool usingWildcardIndex,
  1822. [Values(null, 1, 2)] int? commitQuorumW,
  1823. [Values(null, -1, 0, 42, 9000)] int? milliseconds,
  1824. [Values(false, true)] bool usingCreateManyIndexesOptions,
  1825. [Values(false, true)] bool async)
  1826. {
  1827. var writeConcern = new WriteConcern(1);
  1828. var subject = CreateSubject<BsonDocument>().WithWriteConcern(writeConcern);
  1829. var session = CreateSession(usingSession);
  1830. var keysDocument1 = new BsonDocument("x", 1);
  1831. var keysDocument2 = new BsonDocument("z", 1);
  1832. var keysDefinition1 =
  1833. usingWildcardIndex
  1834. ? Builders<BsonDocument>.IndexKeys.Wildcard()
  1835. : keysDocument1;
  1836. var keysDefinition2 = (IndexKeysDefinition<BsonDocument>)keysDocument2;
  1837. var partialFilterDocument = BsonDocument.Parse("{ x : { $gt : 0 } }");
  1838. var partialFilterDefinition = (FilterDefinition<BsonDocument>)partialFilterDocument;
  1839. var weights = new BsonDocument("y", 1);
  1840. var wildcardProjectionDefinition = Builders<BsonDocument>.Projection.Include("w");
  1841. var storageEngine = new BsonDocument("awesome", true);
  1842. var commitQuorum = commitQuorumW.HasValue ? CreateIndexCommitQuorum.Create(commitQuorumW.Value) : null;
  1843. var maxTime = milliseconds != null ? TimeSpan.FromMilliseconds(milliseconds.Value) : (TimeSpan?)null;
  1844. var createManyIndexesOptions = usingCreateManyIndexesOptions ? new CreateManyIndexesOptions { CommitQuorum = commitQuorum, MaxTime = maxTime } : null;
  1845. var options = new CreateIndexOptions<BsonDocument>
  1846. {
  1847. Background = true,
  1848. Bits = 10,
  1849. #pragma warning disable 618
  1850. BucketSize = 20,
  1851. #pragma warning restore 618
  1852. Collation = new Collation("en_US"),
  1853. DefaultLanguage = "en",
  1854. ExpireAfter = TimeSpan.FromSeconds(20),
  1855. LanguageOverride = "es",
  1856. Max = 30,
  1857. Min = 40,
  1858. Name = "awesome",
  1859. PartialFilterExpression = partialFilterDefinition,
  1860. Sparse = false,
  1861. SphereIndexVersion = 50,
  1862. StorageEngine = storageEngine,
  1863. TextIndexVersion = 60,
  1864. Unique = true,
  1865. Version = 70,
  1866. Weights = weights
  1867. };
  1868. if (usingWildcardIndex)
  1869. {
  1870. options.WildcardProjection = wildcardProjectionDefinition;
  1871. }
  1872. var model1 = new CreateIndexModel<BsonDocument>(keysDefinition1, options);
  1873. var model2 = new CreateIndexModel<BsonDocument>(keysDefinition2);
  1874. var cancellationToken = new CancellationTokenSource().Token;
  1875. if (usingSession)
  1876. {
  1877. if (async)
  1878. {
  1879. subject.Indexes.CreateManyAsync(session, new[] { model1, model2 }, createManyIndexesOptions, cancellationToken).GetAwaiter().GetResult();
  1880. }
  1881. else
  1882. {
  1883. subject.Indexes.CreateMany(session, new[] { model1, model2 }, createManyIndexesOptions, cancellationToken);
  1884. }
  1885. }
  1886. else
  1887. {
  1888. if (async)
  1889. {
  1890. subject.Indexes.CreateManyAsync(new[] { model1, model2 }, createManyIndexesOptions, cancellationToken).GetAwaiter().GetResult();
  1891. }
  1892. else
  1893. {
  1894. subject.Indexes.CreateMany(new[] { model1, model2 }, createManyIndexesOptions, cancellationToken);
  1895. }
  1896. }
  1897. var call = _operationExecutor.GetWriteCall<BsonDocument>();
  1898. VerifySessionAndCancellationToken(call, session, cancellationToken);
  1899. var operation = call.Operation.Should().BeOfType<CreateIndexesOperation>().Subject;
  1900. operation.CollectionNamespace.Should().Be(subject.CollectionNamespace);
  1901. operation.CommitQuorum.Should().BeSameAs(createManyIndexesOptions?.CommitQuorum);
  1902. operation.MaxTime.Should().Be(createManyIndexesOptions?.MaxTime);
  1903. operation.Requests.Count().Should().Be(2);
  1904. operation.WriteConcern.Should().BeSameAs(writeConcern);
  1905. var request1 = operation.Requests.ElementAt(0);
  1906. request1.AdditionalOptions.Should().BeNull();
  1907. request1.Background.Should().Be(options.Background);
  1908. request1.Bits.Should().Be(options.Bits);
  1909. #pragma warning disable 618
  1910. request1.BucketSize.Should().Be(options.BucketSize);
  1911. #pragma warning restore 618
  1912. request1.Collation.Should().BeSameAs(options.Collation);
  1913. request1.DefaultLanguage.Should().Be(options.DefaultLanguage);
  1914. request1.ExpireAfter.Should().Be(options.ExpireAfter);
  1915. var expectedKeysResult =
  1916. usingWildcardIndex
  1917. ? new BsonDocument("$**", 1)
  1918. : keysDocument1;
  1919. request1.Keys.Should().Be(expectedKeysResult);
  1920. request1.LanguageOverride.Should().Be(options.LanguageOverride);
  1921. request1.Max.Should().Be(options.Max);
  1922. request1.Min.Should().Be(options.Min);
  1923. request1.Name.Should().Be(options.Name);
  1924. request1.PartialFilterExpression.Should().Be(partialFilterDocument);
  1925. request1.Sparse.Should().Be(options.Sparse);
  1926. request1.SphereIndexVersion.Should().Be(options.SphereIndexVersion);
  1927. request1.StorageEngine.Should().Be(storageEngine);
  1928. request1.TextIndexVersion.Should().Be(options.TextIndexVersion);
  1929. request1.Unique.Should().Be(options.Unique);
  1930. request1.Version.Should().Be(options.Version);
  1931. request1.Weights.Should().Be(weights);
  1932. if (usingWildcardIndex)
  1933. {
  1934. var wildcardProjection = wildcardProjectionDefinition.Render(BsonDocumentSerializer.Instance, BsonSerializer.SerializerRegistry);
  1935. request1.WildcardProjection.Should().Be(wildcardProjection);
  1936. }
  1937. else
  1938. {
  1939. request1.WildcardProjection.Should().BeNull();
  1940. }
  1941. request1.GetIndexName().Should().Be(options.Name);
  1942. var request2 = operation.Requests.ElementAt(1);
  1943. request2.AdditionalOptions.Should().BeNull();
  1944. request2.Background.Should().NotHaveValue();
  1945. request2.Bits.Should().NotHaveValue();
  1946. #pragma warning disable 618
  1947. request2.BucketSize.Should().NotHaveValue();
  1948. #pragma warning restore 618
  1949. request2.Collation.Should().BeNull();
  1950. request2.DefaultLanguage.Should().BeNull();
  1951. request2.ExpireAfter.Should().NotHaveValue();
  1952. request2.Keys.Should().Be(keysDocument2);
  1953. request2.LanguageOverride.Should().BeNull();
  1954. request2.Max.Should().NotHaveValue();
  1955. request2.Min.Should().NotHaveValue(); ;
  1956. request2.Name.Should().BeNull();
  1957. request2.PartialFilterExpression.Should().BeNull();
  1958. request2.Sparse.Should().NotHaveValue(); ;
  1959. request2.SphereIndexVersion.Should().NotHaveValue();
  1960. request2.StorageEngine.Should().BeNull();
  1961. request2.TextIndexVersion.Should().NotHaveValue();
  1962. request2.Unique.Should().NotHaveValue();
  1963. request2.Version.Should().NotHaveValue();
  1964. request2.Weights.Should().BeNull();
  1965. request2.WildcardProjection.Should().BeNull();
  1966. request2.GetIndexName().Should().Be("z_1");
  1967. }
  1968. [Theory]
  1969. [ParameterAttributeData]
  1970. public void Indexes_DropAll_should_execute_a_DropIndexOperation(
  1971. [Values(false, true)] bool usingSession,
  1972. [Values(false, true)] bool async)
  1973. {
  1974. var writeConcern = new WriteConcern(1);
  1975. var subject = CreateSubject<BsonDocument>().WithWriteConcern(writeConcern);
  1976. var session = CreateSession(usingSession);
  1977. var cancellationToken = new CancellationTokenSource().Token;
  1978. var maxTime = TimeSpan.FromMilliseconds(42);
  1979. var options = new DropIndexOptions { MaxTime = maxTime };
  1980. if (usingSession)
  1981. {
  1982. if (async)
  1983. {
  1984. subject.Indexes.DropAllAsync(session, options, cancellationToken).GetAwaiter().GetResult();
  1985. }
  1986. else
  1987. {
  1988. subject.Indexes.DropAll(session, options, cancellationToken);
  1989. }
  1990. }
  1991. else
  1992. {
  1993. if (async)
  1994. {
  1995. subject.Indexes.DropAllAsync(options, cancellationToken).GetAwaiter().GetResult();
  1996. }
  1997. else
  1998. {
  1999. subject.Indexes.DropAll(options, cancellationToken);
  2000. }
  2001. }
  2002. var call = _operationExecutor.GetWriteCall<BsonDocument>();
  2003. VerifySessionAndCancellationToken(call, session, cancellationToken);
  2004. var operation = call.Operation.Should().BeOfType<DropIndexOperation>().Subject;
  2005. operation.CollectionNamespace.Should().Be(subject.CollectionNamespace);
  2006. operation.IndexName.Should().Be("*");
  2007. operation.MaxTime.Should().Be(options.MaxTime);
  2008. operation.WriteConcern.Should().BeSameAs(writeConcern);
  2009. }
  2010. [Theory]
  2011. [ParameterAttributeData]
  2012. public void Indexes_DropOne_should_execute_a_DropIndexOperation(
  2013. [Values(false, true)] bool usingSession,
  2014. [Values(false, true)] bool async)
  2015. {
  2016. var writeConcern = new WriteConcern(1);
  2017. var subject = CreateSubject<BsonDocument>().WithWriteConcern(writeConcern);
  2018. var session = CreateSession(usingSession);
  2019. var cancellationToken = new CancellationTokenSource().Token;
  2020. var maxTime = TimeSpan.FromMilliseconds(42);
  2021. var options = new DropIndexOptions { MaxTime = maxTime };
  2022. if (usingSession)
  2023. {
  2024. if (async)
  2025. {
  2026. subject.Indexes.DropOneAsync(session, "name", options, cancellationToken).GetAwaiter().GetResult();
  2027. }
  2028. else
  2029. {
  2030. subject.Indexes.DropOne(session, "name", options, cancellationToken);
  2031. }
  2032. }
  2033. else
  2034. {
  2035. if (async)
  2036. {
  2037. subject.Indexes.DropOneAsync("name", options, cancellationToken).GetAwaiter().GetResult();
  2038. }
  2039. else
  2040. {
  2041. subject.Indexes.DropOne("name", options, cancellationToken);
  2042. }
  2043. }
  2044. var call = _operationExecutor.GetWriteCall<BsonDocument>();
  2045. VerifySessionAndCancellationToken(call, session, cancellationToken);
  2046. var operation = call.Operation.Should().BeOfType<DropIndexOperation>().Subject;
  2047. operation.CollectionNamespace.Should().Be(subject.CollectionNamespace);
  2048. operation.IndexName.Should().Be("name");
  2049. operation.WriteConcern.Should().BeSameAs(writeConcern);
  2050. operation.MaxTime.Should().Be(maxTime);
  2051. }
  2052. [Theory]
  2053. [ParameterAttributeData]
  2054. public void Indexes_DropOne_should_throw_an_exception_if_an_asterisk_is_used(
  2055. [Values(false, true)] bool usingSession,
  2056. [Values(false, true)] bool async)
  2057. {
  2058. var subject = CreateSubject<BsonDocument>();
  2059. var session = CreateSession(usingSession);
  2060. var cancellationToken = new CancellationTokenSource().Token;
  2061. Exception exception;
  2062. if (async)
  2063. {
  2064. exception = Record.Exception(() => subject.Indexes.DropOneAsync("*", cancellationToken).GetAwaiter().GetResult());
  2065. }
  2066. else
  2067. {
  2068. exception = Record.Exception(() => subject.Indexes.DropOne("*", cancellationToken));
  2069. }
  2070. var e = exception.Should().BeOfType<ArgumentException>().Subject;
  2071. e.ParamName.Should().Be("name");
  2072. }
  2073. [Theory]
  2074. [ParameterAttributeData]
  2075. public void Indexes_List_should_execute_a_ListIndexesOperation(
  2076. [Values(false, true)] bool usingSession,
  2077. [Values(false, true)] bool async)
  2078. {
  2079. var subject = CreateSubject<BsonDocument>();
  2080. var session = CreateSession(usingSession);
  2081. var cancellationToken = new CancellationTokenSource().Token;
  2082. if (usingSession)
  2083. {
  2084. if (async)
  2085. {
  2086. subject.Indexes.ListAsync(session, cancellationToken).GetAwaiter().GetResult();
  2087. }
  2088. else
  2089. {
  2090. subject.Indexes.List(session, cancellationToken);
  2091. }
  2092. }
  2093. else
  2094. {
  2095. if (async)
  2096. {
  2097. subject.Indexes.ListAsync(cancellationToken).GetAwaiter().GetResult();
  2098. }
  2099. else
  2100. {
  2101. subject.Indexes.List(cancellationToken);
  2102. }
  2103. }
  2104. var call = _operationExecutor.GetReadCall<IAsyncCursor<BsonDocument>>();
  2105. VerifySessionAndCancellationToken(call, session, cancellationToken);
  2106. var operation = call.Operation.Should().BeOfType<ListIndexesOperation>().Subject;
  2107. operation.CollectionNamespace.Should().Be(subject.CollectionNamespace);
  2108. operation.RetryRequested.Should().BeTrue();
  2109. }
  2110. [Theory]
  2111. [ParameterAttributeData]
  2112. public void InsertOne_should_execute_a_BulkMixedOperation(
  2113. [Values(false, true)] bool usingSession,
  2114. [Values(false, true)] bool async)
  2115. {
  2116. var subject = CreateSubject<BsonDocument>();
  2117. var session = CreateSession(usingSession);
  2118. var document = BsonDocument.Parse("{ _id : 1, a : 1 }");
  2119. var options = new InsertOneOptions();
  2120. var cancellationToken = new CancellationTokenSource().Token;
  2121. var processedRequest = new InsertRequest(document) { CorrelationId = 0 };
  2122. var operationResult = new BulkWriteOperationResult.Unacknowledged(1, new[] { processedRequest });
  2123. _operationExecutor.EnqueueResult<BulkWriteOperationResult>(operationResult);
  2124. if (usingSession)
  2125. {
  2126. if (async)
  2127. {
  2128. subject.InsertOneAsync(session, document, options, cancellationToken).GetAwaiter().GetResult();
  2129. }
  2130. else
  2131. {
  2132. subject.InsertOne(session, document, options, cancellationToken);
  2133. }
  2134. }
  2135. else
  2136. {
  2137. if (async)
  2138. {
  2139. subject.InsertOneAsync(document, options, cancellationToken).GetAwaiter().GetResult();
  2140. }
  2141. else
  2142. {
  2143. subject.InsertOne(document, options, cancellationToken);
  2144. }
  2145. }
  2146. var call = _operationExecutor.GetWriteCall<BulkWriteOperationResult>();
  2147. VerifySessionAndCancellationToken(call, session, cancellationToken);
  2148. VerifySingleWrite(call, null, true, processedRequest);
  2149. }
  2150. [Theory]
  2151. [ParameterAttributeData]
  2152. public void InsertOne_should_throw_a_WriteException_when_an_error_occurs(
  2153. [Values(false, true)] bool usingSession,
  2154. [Values(false, true)] bool async)
  2155. {
  2156. var subject = CreateSubject<BsonDocument>();
  2157. var session = CreateSession(usingSession);
  2158. var document = BsonDocument.Parse("{ _id : 1, a : 1 }");
  2159. var options = new InsertOneOptions();
  2160. var cancellationToken = new CancellationTokenSource().Token;
  2161. var processedRequest = new InsertRequest(document) { CorrelationId = 0 };
  2162. var operationException = new MongoBulkWriteOperationException(
  2163. _connectionId,
  2164. new BulkWriteOperationResult.Acknowledged(
  2165. requestCount: 1,
  2166. matchedCount: 0,
  2167. deletedCount: 0,
  2168. insertedCount: 0,
  2169. modifiedCount: 0,
  2170. processedRequests: new[] { processedRequest },
  2171. upserts: new List<BulkWriteOperationUpsert>()),
  2172. new[] { new BulkWriteOperationError(0, 1, "blah", new BsonDocument()) },
  2173. null,
  2174. new List<WriteRequest>());
  2175. _operationExecutor.EnqueueException<BulkWriteOperationResult>(operationException);
  2176. Exception exception;
  2177. if (usingSession)
  2178. {
  2179. if (async)
  2180. {
  2181. exception = Record.Exception(() => subject.InsertOneAsync(session, document, options, cancellationToken).GetAwaiter().GetResult());
  2182. }
  2183. else
  2184. {
  2185. exception = Record.Exception(() => subject.InsertOne(session, document, options, cancellationToken));
  2186. }
  2187. }
  2188. else
  2189. {
  2190. if (async)
  2191. {
  2192. exception = Record.Exception(() => subject.InsertOneAsync(document, options, cancellationToken).GetAwaiter().GetResult());
  2193. }
  2194. else
  2195. {
  2196. exception = Record.Exception(() => subject.InsertOne(document, options, cancellationToken));
  2197. }
  2198. }
  2199. var call = _operationExecutor.GetWriteCall<BulkWriteOperationResult>();
  2200. VerifySessionAndCancellationToken(call, session, cancellationToken);
  2201. exception.Should().BeOfType<MongoWriteException>();
  2202. }
  2203. [Theory]
  2204. [ParameterAttributeData]
  2205. public void InsertOne_should_respect_AssignIdOnInsert(
  2206. [Values(false, true)] bool usingSession,
  2207. [Values(false, true)] bool assignIdOnInsert,
  2208. [Values(false, true)] bool async)
  2209. {
  2210. var settings = new MongoCollectionSettings { AssignIdOnInsert = assignIdOnInsert };
  2211. var subject = CreateSubject<BsonDocument>(settings: settings);
  2212. var session = CreateSession(usingSession);
  2213. var document = BsonDocument.Parse("{ a : 1 }");
  2214. var options = new InsertOneOptions();
  2215. var cancellationToken = new CancellationTokenSource().Token;
  2216. var processedRequest = new InsertRequest(document) { CorrelationId = 0 };
  2217. var operationResult = new BulkWriteOperationResult.Unacknowledged(1, new[] { processedRequest });
  2218. _operationExecutor.EnqueueResult<BulkWriteOperationResult>(operationResult);
  2219. if (usingSession)
  2220. {
  2221. if (async)
  2222. {
  2223. subject.InsertOneAsync(session, document, options, cancellationToken).GetAwaiter().GetResult();
  2224. }
  2225. else
  2226. {
  2227. subject.InsertOne(session, document, options, cancellationToken);
  2228. }
  2229. }
  2230. else
  2231. {
  2232. if (async)
  2233. {
  2234. subject.InsertOneAsync(document, options, cancellationToken).GetAwaiter().GetResult();
  2235. }
  2236. else
  2237. {
  2238. subject.InsertOne(document, options, cancellationToken);
  2239. }
  2240. }
  2241. var call = _operationExecutor.GetWriteCall<BulkWriteOperationResult>();
  2242. VerifySessionAndCancellationToken(call, session, cancellationToken);
  2243. VerifySingleWrite(call, null, true, processedRequest);
  2244. var operation = call.Operation.Should().BeOfType<BulkMixedWriteOperation>().Subject;
  2245. var requests = operation.Requests.ToList(); // call ToList to force evaluation
  2246. document.Contains("_id").Should().Be(assignIdOnInsert);
  2247. }
  2248. [Theory]
  2249. [ParameterAttributeData]
  2250. public void InsertMany_should_execute_a_BulkMixedOperation(
  2251. [Values(false, true)] bool usingSession,
  2252. [Values(null, false, true)] bool? bypassDocumentValidation,
  2253. [Values(false, true)] bool isOrdered,
  2254. [Values(false, true)] bool async)
  2255. {
  2256. var subject = CreateSubject<BsonDocument>();
  2257. var session = CreateSession(usingSession);
  2258. var documents = new[]
  2259. {
  2260. BsonDocument.Parse("{ _id : 1, a : 1 }"),
  2261. BsonDocument.Parse("{ _id : 2, a : 2 }")
  2262. };
  2263. var options = new InsertManyOptions
  2264. {
  2265. BypassDocumentValidation = bypassDocumentValidation,
  2266. IsOrdered = isOrdered
  2267. };
  2268. var cancellationToken = new CancellationTokenSource().Token;
  2269. var processedRequests = new[]
  2270. {
  2271. new InsertRequest(documents[0]) { CorrelationId = 0 },
  2272. new InsertRequest(documents[1]) { CorrelationId = 1 }
  2273. };
  2274. var operationResult = new BulkWriteOperationResult.Unacknowledged(2, processedRequests);
  2275. _operationExecutor.EnqueueResult<BulkWriteOperationResult>(operationResult);
  2276. if (usingSession)
  2277. {
  2278. if (async)
  2279. {
  2280. subject.InsertManyAsync(session, documents, options, cancellationToken).GetAwaiter().GetResult();
  2281. }
  2282. else
  2283. {
  2284. subject.InsertMany(session, documents, options, cancellationToken);
  2285. }
  2286. }
  2287. else
  2288. {
  2289. if (async)
  2290. {
  2291. subject.InsertManyAsync(documents, options, cancellationToken).GetAwaiter().GetResult();
  2292. }
  2293. else
  2294. {
  2295. subject.InsertMany(documents, options, cancellationToken);
  2296. }
  2297. }
  2298. var call = _operationExecutor.GetWriteCall<BulkWriteOperationResult>();
  2299. VerifySessionAndCancellationToken(call, session, cancellationToken);
  2300. VerifyBulkWrite(call, bypassDocumentValidation, isOrdered, processedRequests);
  2301. }
  2302. [Theory]
  2303. [ParameterAttributeData]
  2304. public void InsertMany_should_respect_AssignIdOnInsert(
  2305. [Values(false, true)] bool usingSession,
  2306. [Values(false, true)] bool assignIdOnInsert,
  2307. [Values(false, true)] bool async)
  2308. {
  2309. var settings = new MongoCollectionSettings { AssignIdOnInsert = assignIdOnInsert };
  2310. var subject = CreateSubject<BsonDocument>(settings: settings);
  2311. var session = CreateSession(usingSession);
  2312. var document = BsonDocument.Parse("{ a : 1 }");
  2313. var options = new InsertManyOptions();
  2314. var cancellationToken = new CancellationTokenSource().Token;
  2315. var processedRequest = new InsertRequest(document) { CorrelationId = 0 };
  2316. var operationResult = new BulkWriteOperationResult.Unacknowledged(1, new[] { processedRequest });
  2317. _operationExecutor.EnqueueResult<BulkWriteOperationResult>(operationResult);
  2318. if (usingSession)
  2319. {
  2320. if (async)
  2321. {
  2322. subject.InsertManyAsync(session, new[] { document }, options, cancellationToken).GetAwaiter().GetResult();
  2323. }
  2324. else
  2325. {
  2326. subject.InsertMany(session, new[] { document }, options, cancellationToken);
  2327. }
  2328. }
  2329. else
  2330. {
  2331. if (async)
  2332. {
  2333. subject.InsertManyAsync(new[] { document }, options, cancellationToken).GetAwaiter().GetResult();
  2334. }
  2335. else
  2336. {
  2337. subject.InsertMany(new[] { document }, options, cancellationToken);
  2338. }
  2339. }
  2340. var call = _operationExecutor.GetWriteCall<BulkWriteOperationResult>();
  2341. VerifySessionAndCancellationToken(call, session, cancellationToken);
  2342. VerifySingleWrite(call, null, true, processedRequest);
  2343. var operation = (BulkMixedWriteOperation)call.Operation;
  2344. var requests = operation.Requests.ToList(); // call ToList to force evaluation
  2345. document.Contains("_id").Should().Be(assignIdOnInsert);
  2346. }
  2347. [Theory]
  2348. [ParameterAttributeData]
  2349. public void MapReduce_with_inline_output_mode_should_execute_a_MapReduceOperation(
  2350. [Values(false, true)] bool usingSession,
  2351. [Values(false, true)] bool async)
  2352. {
  2353. var subject = CreateSubject<BsonDocument>();
  2354. var session = CreateSession(usingSession);
  2355. var map = new BsonJavaScript("map");
  2356. var reduce = new BsonJavaScript("reduce");
  2357. var filterDocument = new BsonDocument("filter", 1);
  2358. var filterDefinition = (FilterDefinition<BsonDocument>)filterDocument;
  2359. var sortDocument = new BsonDocument("sort", 1);
  2360. var sortDefinition = (SortDefinition<BsonDocument>)sortDocument;
  2361. var options = new MapReduceOptions<BsonDocument, BsonDocument>
  2362. {
  2363. Collation = new Collation("en_US"),
  2364. Filter = filterDefinition,
  2365. Finalize = new BsonJavaScript("finalizer"),
  2366. #pragma warning disable 618
  2367. JavaScriptMode = true,
  2368. #pragma warning restore 618
  2369. Limit = 10,
  2370. MaxTime = TimeSpan.FromMinutes(2),
  2371. OutputOptions = MapReduceOutputOptions.Inline,
  2372. Scope = new BsonDocument("test", 3),
  2373. Sort = sortDefinition,
  2374. Verbose = true
  2375. };
  2376. var cancellationToken = new CancellationTokenSource().Token;
  2377. if (usingSession)
  2378. {
  2379. if (async)
  2380. {
  2381. subject.MapReduceAsync(session, map, reduce, options, cancellationToken).GetAwaiter().GetResult();
  2382. }
  2383. else
  2384. {
  2385. subject.MapReduce(session, map, reduce, options, cancellationToken);
  2386. }
  2387. }
  2388. else
  2389. {
  2390. if (async)
  2391. {
  2392. subject.MapReduceAsync(map, reduce, options, cancellationToken).GetAwaiter().GetResult();
  2393. }
  2394. else
  2395. {
  2396. subject.MapReduce(map, reduce, options, cancellationToken);
  2397. }
  2398. }
  2399. var call = _operationExecutor.GetReadCall<IAsyncCursor<BsonDocument>>();
  2400. VerifySessionAndCancellationToken(call, session, cancellationToken);
  2401. var operation = call.Operation.Should().BeOfType<MapReduceOperation<BsonDocument>>().Subject;
  2402. operation.Collation.Should().BeSameAs(options.Collation);
  2403. operation.CollectionNamespace.Should().Be(subject.CollectionNamespace);
  2404. operation.Filter.Should().Be(filterDocument);
  2405. operation.FinalizeFunction.Should().Be(options.Finalize);
  2406. #pragma warning disable 618
  2407. operation.JavaScriptMode.Should().Be(options.JavaScriptMode);
  2408. #pragma warning restore 618
  2409. operation.Limit.Should().Be(options.Limit);
  2410. operation.MapFunction.Should().Be(map);
  2411. operation.MaxTime.Should().Be(options.MaxTime);
  2412. operation.ReadConcern.Should().Be(subject.Settings.ReadConcern);
  2413. operation.ReduceFunction.Should().Be(reduce);
  2414. operation.ResultSerializer.Should().Be(BsonDocumentSerializer.Instance);
  2415. operation.Scope.Should().Be(options.Scope);
  2416. operation.Sort.Should().Be(sortDocument);
  2417. operation.Verbose.Should().Be(options.Verbose);
  2418. }
  2419. [Theory]
  2420. [ParameterAttributeData]
  2421. public void MapReduce_with_collection_output_mode_should_execute_a_MapReduceOutputToCollectionOperation(
  2422. [Values(false, true)] bool usingSession,
  2423. [Values(false, true)] bool async)
  2424. {
  2425. var writeConcern = new WriteConcern(1);
  2426. var subject = CreateSubject<BsonDocument>().WithWriteConcern(writeConcern);
  2427. var session = CreateSession(usingSession);
  2428. var map = new BsonJavaScript("map");
  2429. var reduce = new BsonJavaScript("reduce");
  2430. var filterDocument = new BsonDocument("filter", 1);
  2431. var filterDefinition = (FilterDefinition<BsonDocument>)filterDocument;
  2432. var sortDocument = new BsonDocument("sort", 1);
  2433. var sortDefinition = (SortDefinition<BsonDocument>)sortDocument;
  2434. var options = new MapReduceOptions<BsonDocument, BsonDocument>
  2435. {
  2436. BypassDocumentValidation = true,
  2437. Collation = new Collation("en_US"),
  2438. Filter = filterDefinition,
  2439. Finalize = new BsonJavaScript("finalizer"),
  2440. #pragma warning disable 618
  2441. JavaScriptMode = true,
  2442. #pragma warning restore 618
  2443. Limit = 10,
  2444. MaxTime = TimeSpan.FromMinutes(2),
  2445. #pragma warning disable 618
  2446. OutputOptions = MapReduceOutputOptions.Replace("awesome", "otherDB", true),
  2447. #pragma warning restore 618
  2448. Scope = new BsonDocument("test", 3),
  2449. Sort = sortDefinition,
  2450. Verbose = true
  2451. };
  2452. var cancellationToken = new CancellationTokenSource().Token;
  2453. if (usingSession)
  2454. {
  2455. if (async)
  2456. {
  2457. subject.MapReduceAsync(session, map, reduce, options, cancellationToken).GetAwaiter().GetResult();
  2458. }
  2459. else
  2460. {
  2461. subject.MapReduce(session, map, reduce, options, cancellationToken);
  2462. }
  2463. }
  2464. else
  2465. {
  2466. if (async)
  2467. {
  2468. subject.MapReduceAsync(map, reduce, options, cancellationToken).GetAwaiter().GetResult();
  2469. }
  2470. else
  2471. {
  2472. subject.MapReduce(map, reduce, options, cancellationToken);
  2473. }
  2474. }
  2475. var call = _operationExecutor.GetWriteCall<BsonDocument>();
  2476. VerifySessionAndCancellationToken(call, session, cancellationToken);
  2477. var operation = call.Operation.Should().BeOfType<MapReduceOutputToCollectionOperation>().Subject;
  2478. operation.BypassDocumentValidation.Should().Be(options.BypassDocumentValidation);
  2479. operation.Collation.Should().BeSameAs(options.Collation);
  2480. operation.CollectionNamespace.Should().Be(subject.CollectionNamespace);
  2481. operation.Filter.Should().Be(filterDocument);
  2482. operation.FinalizeFunction.Should().Be(options.Finalize);
  2483. #pragma warning disable 618
  2484. operation.JavaScriptMode.Should().Be(options.JavaScriptMode);
  2485. #pragma warning restore 618
  2486. operation.Limit.Should().Be(options.Limit);
  2487. operation.MapFunction.Should().Be(map);
  2488. operation.MaxTime.Should().Be(options.MaxTime);
  2489. #pragma warning disable 618
  2490. operation.NonAtomicOutput.Should().NotHaveValue();
  2491. #pragma warning restore 618
  2492. operation.OutputCollectionNamespace.Should().Be(CollectionNamespace.FromFullName("otherDB.awesome"));
  2493. operation.OutputMode.Should().Be(Core.Operations.MapReduceOutputMode.Replace);
  2494. operation.ReduceFunction.Should().Be(reduce);
  2495. operation.Scope.Should().Be(options.Scope);
  2496. #pragma warning disable 618
  2497. operation.ShardedOutput.Should().Be(true);
  2498. #pragma warning restore 618
  2499. operation.Sort.Should().Be(sortDocument);
  2500. operation.Verbose.Should().Be(options.Verbose);
  2501. operation.WriteConcern.Should().BeSameAs(writeConcern);
  2502. }
  2503. [Theory]
  2504. [ParameterAttributeData]
  2505. public void ReplaceOne_should_execute_a_BulkMixedOperation(
  2506. [Values(false, true)] bool usingSession,
  2507. [Values(null, false, true)] bool? bypassDocumentValidation,
  2508. [Values(false, true)] bool isUpsert,
  2509. [Values(false, true)] bool async)
  2510. {
  2511. var subject = CreateSubject<BsonDocument>();
  2512. var session = CreateSession(usingSession);
  2513. var filterDocument = BsonDocument.Parse("{ a : 1 }");
  2514. var filterDefinition = (FilterDefinition<BsonDocument>)filterDocument;
  2515. var replacement = BsonDocument.Parse("{ a : 2 }");
  2516. var collation = new Collation("en_US");
  2517. var hint = new BsonDocument("x", 1);
  2518. var cancellationToken = new CancellationTokenSource().Token;
  2519. var processedRequest = new UpdateRequest(UpdateType.Replacement, filterDocument, replacement)
  2520. {
  2521. Collation = collation,
  2522. Hint = hint,
  2523. CorrelationId = 0,
  2524. IsUpsert = isUpsert,
  2525. IsMulti = false
  2526. };
  2527. var operationResult = new BulkWriteOperationResult.Unacknowledged(9, new[] { processedRequest });
  2528. _operationExecutor.EnqueueResult<BulkWriteOperationResult>(operationResult);
  2529. _operationExecutor.EnqueueResult<BulkWriteOperationResult>(operationResult);
  2530. _operationExecutor.EnqueueResult<BulkWriteOperationResult>(operationResult);
  2531. assertReplaceOne();
  2532. var replaceOptions = new ReplaceOptions()
  2533. {
  2534. BypassDocumentValidation = bypassDocumentValidation,
  2535. Collation = collation,
  2536. Hint = hint,
  2537. IsUpsert = isUpsert
  2538. };
  2539. assertReplaceOneWithReplaceOptions(replaceOptions);
  2540. var updateOptions = new UpdateOptions
  2541. {
  2542. BypassDocumentValidation = bypassDocumentValidation,
  2543. Hint = hint,
  2544. Collation = collation,
  2545. IsUpsert = isUpsert
  2546. };
  2547. assertReplaceOneWithUpdateOptions(updateOptions);
  2548. void assertReplaceOne()
  2549. {
  2550. if (usingSession)
  2551. {
  2552. if (async)
  2553. {
  2554. subject.ReplaceOneAsync(session, filterDefinition, replacement, cancellationToken: cancellationToken).GetAwaiter().GetResult();
  2555. }
  2556. else
  2557. {
  2558. subject.ReplaceOne(session, filterDefinition, replacement, cancellationToken: cancellationToken);
  2559. }
  2560. }
  2561. else
  2562. {
  2563. if (async)
  2564. {
  2565. subject.ReplaceOneAsync(filterDefinition, replacement, cancellationToken: cancellationToken).GetAwaiter().GetResult();
  2566. }
  2567. else
  2568. {
  2569. subject.ReplaceOne(filterDefinition, replacement, cancellationToken: cancellationToken);
  2570. }
  2571. }
  2572. assertOperationResult(null);
  2573. }
  2574. void assertReplaceOneWithReplaceOptions(ReplaceOptions options)
  2575. {
  2576. if (usingSession)
  2577. {
  2578. if (async)
  2579. {
  2580. subject.ReplaceOneAsync(session, filterDefinition, replacement, options, cancellationToken).GetAwaiter().GetResult();
  2581. }
  2582. else
  2583. {
  2584. subject.ReplaceOne(session, filterDefinition, replacement, options, cancellationToken);
  2585. }
  2586. }
  2587. else
  2588. {
  2589. if (async)
  2590. {
  2591. subject.ReplaceOneAsync(filterDefinition, replacement, options, cancellationToken).GetAwaiter().GetResult();
  2592. }
  2593. else
  2594. {
  2595. subject.ReplaceOne(filterDefinition, replacement, options, cancellationToken);
  2596. }
  2597. }
  2598. assertOperationResult(bypassDocumentValidation);
  2599. }
  2600. void assertReplaceOneWithUpdateOptions(UpdateOptions options)
  2601. {
  2602. if (usingSession)
  2603. {
  2604. if (async)
  2605. {
  2606. #pragma warning disable 618
  2607. subject.ReplaceOneAsync(session, filterDefinition, replacement, options, cancellationToken).GetAwaiter().GetResult();
  2608. #pragma warning restore 618
  2609. }
  2610. else
  2611. {
  2612. #pragma warning disable 618
  2613. subject.ReplaceOne(session, filterDefinition, replacement, options, cancellationToken);
  2614. #pragma warning restore 618
  2615. }
  2616. }
  2617. else
  2618. {
  2619. if (async)
  2620. {
  2621. #pragma warning disable 618
  2622. subject.ReplaceOneAsync(filterDefinition, replacement, options, cancellationToken).GetAwaiter().GetResult();
  2623. #pragma warning restore 618
  2624. }
  2625. else
  2626. {
  2627. #pragma warning disable 618
  2628. subject.ReplaceOne(filterDefinition, replacement, options, cancellationToken);
  2629. #pragma warning restore 618
  2630. }
  2631. }
  2632. assertOperationResult(bypassDocumentValidation);
  2633. }
  2634. void assertOperationResult(bool? expectedBypassDocumentValidation)
  2635. {
  2636. var call = _operationExecutor.GetWriteCall<BulkWriteOperationResult>();
  2637. VerifySessionAndCancellationToken(call, session, cancellationToken);
  2638. VerifySingleWrite(call, expectedBypassDocumentValidation, true, processedRequest);
  2639. }
  2640. }
  2641. [Theory]
  2642. [ParameterAttributeData]
  2643. public void ReplaceOne_should_throw_a_WriteException_when_an_error_occurs(
  2644. [Values(false, true)] bool usingSession,
  2645. [Values(null, false, true)] bool? bypassDocumentValidation,
  2646. [Values(false, true)] bool isUpsert,
  2647. [Values(false, true)] bool async)
  2648. {
  2649. var subject = CreateSubject<BsonDocument>();
  2650. var session = CreateSession(usingSession);
  2651. var filterDocument = BsonDocument.Parse("{ a : 1 }");
  2652. var filterDefinition = (FilterDefinition<BsonDocument>)filterDocument;
  2653. var replacement = BsonDocument.Parse("{ a : 2 }");
  2654. var collation = new Collation("en_US");
  2655. var hint = new BsonDocument("x", 1);
  2656. var cancellationToken = new CancellationTokenSource().Token;
  2657. var processedRequest = new UpdateRequest(UpdateType.Replacement, filterDocument, replacement)
  2658. {
  2659. Collation = collation,
  2660. CorrelationId = 0,
  2661. Hint = hint,
  2662. IsUpsert = isUpsert,
  2663. IsMulti = false
  2664. };
  2665. var operationException = new MongoBulkWriteOperationException(
  2666. _connectionId,
  2667. new BulkWriteOperationResult.Acknowledged(
  2668. requestCount: 1,
  2669. matchedCount: 1,
  2670. deletedCount: 0,
  2671. insertedCount: 0,
  2672. modifiedCount: 0,
  2673. processedRequests: new[] { processedRequest },
  2674. upserts: new List<BulkWriteOperationUpsert>()),
  2675. new[] { new BulkWriteOperationError(0, 1, "blah", new BsonDocument()) },
  2676. null,
  2677. new List<WriteRequest>());
  2678. _operationExecutor.EnqueueException<BulkWriteOperationResult>(operationException);
  2679. _operationExecutor.EnqueueException<BulkWriteOperationResult>(operationException);
  2680. _operationExecutor.EnqueueException<BulkWriteOperationResult>(operationException);
  2681. assertReplaceOne();
  2682. var replaceOptions = new ReplaceOptions
  2683. {
  2684. Collation = collation,
  2685. Hint = hint,
  2686. BypassDocumentValidation = bypassDocumentValidation,
  2687. IsUpsert = isUpsert
  2688. };
  2689. assertReplaceOneWithReplaceOptions(replaceOptions);
  2690. var updateOptions = new UpdateOptions
  2691. {
  2692. Collation = collation,
  2693. Hint = hint,
  2694. BypassDocumentValidation = bypassDocumentValidation,
  2695. IsUpsert = isUpsert
  2696. };
  2697. assertReplaceOneWithUpdateOptions(updateOptions);
  2698. void assertReplaceOne()
  2699. {
  2700. Exception exception;
  2701. if (usingSession)
  2702. {
  2703. if (async)
  2704. {
  2705. exception = Record.Exception(() => subject.ReplaceOneAsync(session, filterDefinition, replacement, cancellationToken: cancellationToken).GetAwaiter().GetResult());
  2706. }
  2707. else
  2708. {
  2709. exception = Record.Exception(() => subject.ReplaceOne(session, filterDefinition, replacement, cancellationToken: cancellationToken));
  2710. }
  2711. }
  2712. else
  2713. {
  2714. if (async)
  2715. {
  2716. exception = Record.Exception(() => subject.ReplaceOneAsync(filterDefinition, replacement, cancellationToken: cancellationToken).GetAwaiter().GetResult());
  2717. }
  2718. else
  2719. {
  2720. exception = Record.Exception(() => subject.ReplaceOne(filterDefinition, replacement, cancellationToken: cancellationToken));
  2721. }
  2722. }
  2723. assertException(exception);
  2724. }
  2725. void assertReplaceOneWithReplaceOptions(ReplaceOptions options)
  2726. {
  2727. Exception exception;
  2728. if (usingSession)
  2729. {
  2730. if (async)
  2731. {
  2732. exception = Record.Exception(() => subject.ReplaceOneAsync(session, filterDefinition, replacement, options, cancellationToken).GetAwaiter().GetResult());
  2733. }
  2734. else
  2735. {
  2736. exception = Record.Exception(() => subject.ReplaceOne(session, filterDefinition, replacement, options, cancellationToken));
  2737. }
  2738. }
  2739. else
  2740. {
  2741. if (async)
  2742. {
  2743. exception = Record.Exception(() => subject.ReplaceOneAsync(filterDefinition, replacement, options, cancellationToken).GetAwaiter().GetResult());
  2744. }
  2745. else
  2746. {
  2747. exception = Record.Exception(() => subject.ReplaceOne(filterDefinition, replacement, options, cancellationToken));
  2748. }
  2749. }
  2750. assertException(exception);
  2751. }
  2752. void assertReplaceOneWithUpdateOptions(UpdateOptions options)
  2753. {
  2754. Exception exception;
  2755. if (usingSession)
  2756. {
  2757. if (async)
  2758. {
  2759. #pragma warning disable 618
  2760. exception = Record.Exception(() => subject.ReplaceOneAsync(session, filterDefinition, replacement, options, cancellationToken).GetAwaiter().GetResult());
  2761. #pragma warning restore 618
  2762. }
  2763. else
  2764. {
  2765. #pragma warning disable 618
  2766. exception = Record.Exception(() => subject.ReplaceOne(session, filterDefinition, replacement, options, cancellationToken));
  2767. #pragma warning restore 618
  2768. }
  2769. }
  2770. else
  2771. {
  2772. if (async)
  2773. {
  2774. #pragma warning disable 618
  2775. exception = Record.Exception(() => subject.ReplaceOneAsync(filterDefinition, replacement, options, cancellationToken).GetAwaiter().GetResult());
  2776. #pragma warning restore 618
  2777. }
  2778. else
  2779. {
  2780. #pragma warning disable 618
  2781. exception = Record.Exception(() => subject.ReplaceOne(filterDefinition, replacement, options, cancellationToken));
  2782. #pragma warning restore 618
  2783. }
  2784. }
  2785. assertException(exception);
  2786. }
  2787. void assertException(Exception exception)
  2788. {
  2789. var call = _operationExecutor.GetWriteCall<BulkWriteOperationResult>();
  2790. VerifySessionAndCancellationToken(call, session, cancellationToken);
  2791. exception.Should().BeOfType<MongoWriteException>();
  2792. }
  2793. }
  2794. [Theory]
  2795. [ParameterAttributeData]
  2796. public void UpdateMany_should_execute_a_BulkMixedOperation(
  2797. [Values(false, true)] bool usingSession,
  2798. [Values(null, false, true)] bool? bypassDocumentValidation,
  2799. [Values(false, true)] bool isUpsert,
  2800. [Values(false, true)] bool async)
  2801. {
  2802. var subject = CreateSubject<BsonDocument>();
  2803. var session = CreateSession(usingSession);
  2804. var filterDocument = BsonDocument.Parse("{ a : 1 }");
  2805. var filterDefinition = (FilterDefinition<BsonDocument>)filterDocument;
  2806. var updateDocument = BsonDocument.Parse("{ $set : { a : 1 } }");
  2807. var updateDefinition = (UpdateDefinition<BsonDocument>)updateDocument;
  2808. var arrayFilterDocument = BsonDocument.Parse("{ b : 1 }");
  2809. var arrayFilterDefinition = (ArrayFilterDefinition<BsonDocument>)arrayFilterDocument;
  2810. var collation = new Collation("en_US");
  2811. var hint = new BsonDocument("x", 1);
  2812. var options = new UpdateOptions
  2813. {
  2814. ArrayFilters = new[] { arrayFilterDefinition },
  2815. BypassDocumentValidation = bypassDocumentValidation,
  2816. Collation = collation,
  2817. Hint = hint,
  2818. IsUpsert = isUpsert
  2819. };
  2820. var cancellationToken = new CancellationTokenSource().Token;
  2821. var processedRequest = new UpdateRequest(UpdateType.Update, filterDocument, updateDocument)
  2822. {
  2823. ArrayFilters = new[] { arrayFilterDocument },
  2824. Collation = collation,
  2825. Hint = hint,
  2826. CorrelationId = 0,
  2827. IsUpsert = isUpsert,
  2828. IsMulti = true
  2829. };
  2830. var operationResult = new BulkWriteOperationResult.Unacknowledged(9, new[] { processedRequest });
  2831. _operationExecutor.EnqueueResult<BulkWriteOperationResult>(operationResult);
  2832. if (usingSession)
  2833. {
  2834. if (async)
  2835. {
  2836. subject.UpdateManyAsync(session, filterDefinition, updateDefinition, options, cancellationToken).GetAwaiter().GetResult();
  2837. }
  2838. else
  2839. {
  2840. subject.UpdateMany(session, filterDefinition, updateDefinition, options, cancellationToken);
  2841. }
  2842. }
  2843. else
  2844. {
  2845. if (async)
  2846. {
  2847. subject.UpdateManyAsync(filterDefinition, updateDefinition, options, cancellationToken).GetAwaiter().GetResult();
  2848. }
  2849. else
  2850. {
  2851. subject.UpdateMany(filterDefinition, updateDefinition, options, cancellationToken);
  2852. }
  2853. }
  2854. var call = _operationExecutor.GetWriteCall<BulkWriteOperationResult>();
  2855. VerifySessionAndCancellationToken(call, session, cancellationToken);
  2856. VerifySingleWrite(call, bypassDocumentValidation, true, processedRequest);
  2857. }
  2858. [Theory]
  2859. [ParameterAttributeData]
  2860. public void UpdateMany_should_throw_a_WriteException_when_an_error_occurs(
  2861. [Values(false, true)] bool usingSession,
  2862. [Values(null, false, true)] bool? bypassDocumentValidation,
  2863. [Values(false, true)] bool isUpsert,
  2864. [Values(false, true)] bool async)
  2865. {
  2866. var subject = CreateSubject<BsonDocument>();
  2867. var session = CreateSession(usingSession);
  2868. var filterDocument = BsonDocument.Parse("{ a : 1 }");
  2869. var filterDefinition = (FilterDefinition<BsonDocument>)filterDocument;
  2870. var updateDocument = BsonDocument.Parse("{ $set : { a : 1 } }");
  2871. var updateDefinition = (UpdateDefinition<BsonDocument>)updateDocument;
  2872. var arrayFilterDocument = BsonDocument.Parse("{ b : 1 }");
  2873. var arrayFilterDefinition = (ArrayFilterDefinition<BsonDocument>)arrayFilterDocument;
  2874. var collation = new Collation("en_US");
  2875. var hint = new BsonDocument("x", 1);
  2876. var updateOptions = new UpdateOptions
  2877. {
  2878. ArrayFilters = new[] { arrayFilterDefinition },
  2879. BypassDocumentValidation = bypassDocumentValidation,
  2880. Collation = collation,
  2881. Hint = hint,
  2882. IsUpsert = isUpsert
  2883. };
  2884. var cancellationToken = new CancellationTokenSource().Token;
  2885. var processedRequest = new UpdateRequest(UpdateType.Update, filterDocument, updateDocument)
  2886. {
  2887. ArrayFilters = new[] { arrayFilterDocument },
  2888. Collation = collation,
  2889. CorrelationId = 0,
  2890. Hint = hint,
  2891. IsUpsert = isUpsert,
  2892. IsMulti = true
  2893. };
  2894. var operationException = new MongoBulkWriteOperationException(
  2895. _connectionId,
  2896. new BulkWriteOperationResult.Acknowledged(
  2897. requestCount: 1,
  2898. matchedCount: 1,
  2899. deletedCount: 0,
  2900. insertedCount: 0,
  2901. modifiedCount: 0,
  2902. processedRequests: new[] { processedRequest },
  2903. upserts: new List<BulkWriteOperationUpsert>()),
  2904. new[] { new BulkWriteOperationError(0, 1, "blah", new BsonDocument()) },
  2905. null,
  2906. new List<WriteRequest>());
  2907. _operationExecutor.EnqueueException<BulkWriteOperationResult>(operationException);
  2908. Exception exception;
  2909. if (usingSession)
  2910. {
  2911. if (async)
  2912. {
  2913. exception = Record.Exception(() => subject.UpdateManyAsync(session, filterDefinition, updateDefinition, updateOptions, cancellationToken).GetAwaiter().GetResult());
  2914. }
  2915. else
  2916. {
  2917. exception = Record.Exception(() => subject.UpdateMany(session, filterDefinition, updateDefinition, updateOptions, cancellationToken));
  2918. }
  2919. }
  2920. else
  2921. {
  2922. if (async)
  2923. {
  2924. exception = Record.Exception(() => subject.UpdateManyAsync(filterDefinition, updateDefinition, updateOptions, cancellationToken).GetAwaiter().GetResult());
  2925. }
  2926. else
  2927. {
  2928. exception = Record.Exception(() => subject.UpdateMany(filterDefinition, updateDefinition, updateOptions, cancellationToken));
  2929. }
  2930. }
  2931. var call = _operationExecutor.GetWriteCall<BulkWriteOperationResult>();
  2932. VerifySessionAndCancellationToken(call, session, cancellationToken);
  2933. exception.Should().BeOfType<MongoWriteException>();
  2934. }
  2935. [Theory]
  2936. [ParameterAttributeData]
  2937. public void UpdateOne_should_execute_a_BulkMixedOperation(
  2938. [Values(false, true)] bool usingSession,
  2939. [Values(null, false, true)] bool? bypassDocumentValidation,
  2940. [Values(false, true)] bool isUpsert,
  2941. [Values(false, true)] bool async)
  2942. {
  2943. var subject = CreateSubject<BsonDocument>();
  2944. var session = CreateSession(usingSession);
  2945. var filterDocument = BsonDocument.Parse("{ a : 1 }");
  2946. var filterDefinition = (FilterDefinition<BsonDocument>)filterDocument;
  2947. var updateDocument = BsonDocument.Parse("{ $set : { a : 1 } }");
  2948. var updateDefinition = (UpdateDefinition<BsonDocument>)updateDocument;
  2949. var arrayFilterDocument = BsonDocument.Parse("{ b : 1 }");
  2950. var arrayFilterDefinition = (ArrayFilterDefinition<BsonDocument>)arrayFilterDocument;
  2951. var collation = new Collation("en_US");
  2952. var hint = new BsonDocument("x", 1);
  2953. var options = new UpdateOptions
  2954. {
  2955. ArrayFilters = new[] { arrayFilterDefinition },
  2956. BypassDocumentValidation = bypassDocumentValidation,
  2957. Collation = collation,
  2958. Hint = hint,
  2959. IsUpsert = isUpsert
  2960. };
  2961. var cancellationToken = new CancellationTokenSource().Token;
  2962. var processedRequest = new UpdateRequest(UpdateType.Update, filterDocument, updateDocument)
  2963. {
  2964. ArrayFilters = new[] { arrayFilterDocument },
  2965. Collation = collation,
  2966. CorrelationId = 0,
  2967. Hint = hint,
  2968. IsUpsert = isUpsert,
  2969. IsMulti = false
  2970. };
  2971. var operationResult = new BulkWriteOperationResult.Unacknowledged(9, new[] { processedRequest });
  2972. _operationExecutor.EnqueueResult<BulkWriteOperationResult>(operationResult);
  2973. if (usingSession)
  2974. {
  2975. if (async)
  2976. {
  2977. subject.UpdateOneAsync(session, filterDefinition, updateDefinition, options, cancellationToken).GetAwaiter().GetResult();
  2978. }
  2979. else
  2980. {
  2981. subject.UpdateOne(session, filterDefinition, updateDefinition, options, cancellationToken);
  2982. }
  2983. }
  2984. else
  2985. {
  2986. if (async)
  2987. {
  2988. subject.UpdateOneAsync(filterDefinition, updateDefinition, options, cancellationToken).GetAwaiter().GetResult();
  2989. }
  2990. else
  2991. {
  2992. subject.UpdateOne(filterDefinition, updateDefinition, options, cancellationToken);
  2993. }
  2994. }
  2995. var call = _operationExecutor.GetWriteCall<BulkWriteOperationResult>();
  2996. VerifySessionAndCancellationToken(call, session, cancellationToken);
  2997. VerifySingleWrite(call, bypassDocumentValidation, true, processedRequest);
  2998. }
  2999. [Theory]
  3000. [ParameterAttributeData]
  3001. public void UpdateOne_should_throw_a_WriteException_when_an_error_occurs(
  3002. [Values(false, true)] bool usingSession,
  3003. [Values(false, true)] bool? bypassDocumentValidation,
  3004. [Values(false, true)] bool isUpsert,
  3005. [Values(false, true)] bool async)
  3006. {
  3007. var subject = CreateSubject<BsonDocument>();
  3008. var session = CreateSession(usingSession);
  3009. var filterDocument = BsonDocument.Parse("{ a : 1 }");
  3010. var filterDefinition = (FilterDefinition<BsonDocument>)filterDocument;
  3011. var updateDocument = BsonDocument.Parse("{ $set : { a : 1 } }");
  3012. var updateDefinition = (UpdateDefinition<BsonDocument>)updateDocument;
  3013. var arrayFilterDocument = BsonDocument.Parse("{ b : 1 }");
  3014. var arrayFilterDefinition = (ArrayFilterDefinition<BsonDocument>)arrayFilterDocument;
  3015. var collation = new Collation("en_US");
  3016. var hint = new BsonDocument("x", 1);
  3017. var options = new UpdateOptions
  3018. {
  3019. ArrayFilters = new[] { arrayFilterDefinition },
  3020. BypassDocumentValidation = bypassDocumentValidation,
  3021. Collation = collation,
  3022. Hint = hint,
  3023. IsUpsert = isUpsert
  3024. };
  3025. var cancellationToken = new CancellationTokenSource().Token;
  3026. var processedRequest = new UpdateRequest(UpdateType.Update, filterDocument, updateDocument)
  3027. {
  3028. ArrayFilters = new[] { arrayFilterDocument },
  3029. Collation = collation,
  3030. CorrelationId = 0,
  3031. Hint = hint,
  3032. IsUpsert = isUpsert,
  3033. IsMulti = false
  3034. };
  3035. var operationException = new MongoBulkWriteOperationException(
  3036. _connectionId,
  3037. new BulkWriteOperationResult.Acknowledged(
  3038. requestCount: 1,
  3039. matchedCount: 1,
  3040. deletedCount: 0,
  3041. insertedCount: 0,
  3042. modifiedCount: 0,
  3043. processedRequests: new[] { processedRequest },
  3044. upserts: new List<BulkWriteOperationUpsert>()),
  3045. new[] { new BulkWriteOperationError(0, 1, "blah", new BsonDocument()) },
  3046. null,
  3047. new List<WriteRequest>());
  3048. _operationExecutor.EnqueueException<BulkWriteOperationResult>(operationException);
  3049. Exception exception;
  3050. if (usingSession)
  3051. {
  3052. if (async)
  3053. {
  3054. exception = Record.Exception(() => subject.UpdateOneAsync(session, filterDefinition, updateDefinition, options, cancellationToken).GetAwaiter().GetResult());
  3055. }
  3056. else
  3057. {
  3058. exception = Record.Exception(() => subject.UpdateOne(session, filterDefinition, updateDefinition, options, cancellationToken));
  3059. }
  3060. }
  3061. else
  3062. {
  3063. if (async)
  3064. {
  3065. exception = Record.Exception(() => subject.UpdateOneAsync(filterDefinition, updateDefinition, options, cancellationToken).GetAwaiter().GetResult());
  3066. }
  3067. else
  3068. {
  3069. exception = Record.Exception(() => subject.UpdateOne(filterDefinition, updateDefinition, options, cancellationToken));
  3070. }
  3071. }
  3072. var call = _operationExecutor.GetWriteCall<BulkWriteOperationResult>();
  3073. VerifySessionAndCancellationToken(call, session, cancellationToken);
  3074. exception.Should().BeOfType<MongoWriteException>();
  3075. }
  3076. [Theory]
  3077. [ParameterAttributeData]
  3078. public void Watch_should_execute_a_ChangeStreamOperation(
  3079. [Values(false, true)] bool usingSession,
  3080. [Values(null, 1)] int? batchSize,
  3081. [Values(null, "a")] string locale,
  3082. [Values(ChangeStreamFullDocumentOption.Default, ChangeStreamFullDocumentOption.UpdateLookup)] ChangeStreamFullDocumentOption fullDocument,
  3083. [Values(null, 1)] int? maxAwaitTimeMS,
  3084. [Values(null, ReadConcernLevel.Local)] ReadConcernLevel? readConcernLevel,
  3085. [Values(null, "{ a : 1 }")] string resumeAferString,
  3086. [Values(false, true)] bool async)
  3087. {
  3088. var collation = locale == null ? null : new Collation(locale);
  3089. var maxAwaitTime = maxAwaitTimeMS == null ? (TimeSpan?)null : TimeSpan.FromMilliseconds(maxAwaitTimeMS.Value);
  3090. var readConcern = readConcernLevel == null ? null : new ReadConcern(readConcernLevel);
  3091. var resumeAfter = resumeAferString == null ? null : BsonDocument.Parse(resumeAferString);
  3092. var startAfter = new BsonDocument();
  3093. var startAtOperationTime = new BsonTimestamp(1, 2);
  3094. var subject = CreateSubject<BsonDocument>();
  3095. if (readConcern != null)
  3096. {
  3097. subject = subject.WithReadConcern(readConcern);
  3098. }
  3099. var session = CreateSession(usingSession);
  3100. var stageDocument = BsonDocument.Parse("{ $match : { operationType : \"insert\" } }");
  3101. var pipeline = (PipelineDefinition<ChangeStreamDocument<BsonDocument>, ChangeStreamDocument<BsonDocument>>)(new[] { stageDocument });
  3102. var options = new ChangeStreamOptions
  3103. {
  3104. BatchSize = batchSize,
  3105. Collation = collation,
  3106. FullDocument = fullDocument,
  3107. MaxAwaitTime = maxAwaitTime,
  3108. ResumeAfter = resumeAfter,
  3109. StartAfter = startAfter,
  3110. StartAtOperationTime = startAtOperationTime
  3111. };
  3112. var cancellationToken = new CancellationTokenSource().Token;
  3113. if (usingSession)
  3114. {
  3115. if (async)
  3116. {
  3117. subject.WatchAsync(session, pipeline, options, cancellationToken).GetAwaiter().GetResult();
  3118. }
  3119. else
  3120. {
  3121. subject.Watch(session, pipeline, options, cancellationToken);
  3122. }
  3123. }
  3124. else
  3125. {
  3126. if (async)
  3127. {
  3128. subject.WatchAsync(pipeline, options, cancellationToken).GetAwaiter().GetResult();
  3129. }
  3130. else
  3131. {
  3132. subject.Watch(pipeline, options, cancellationToken);
  3133. }
  3134. }
  3135. var call = _operationExecutor.GetReadCall<IChangeStreamCursor<ChangeStreamDocument<BsonDocument>>>();
  3136. VerifySessionAndCancellationToken(call, session, cancellationToken);
  3137. var operation = call.Operation.Should().BeOfType<ChangeStreamOperation<ChangeStreamDocument<BsonDocument>>>().Subject;
  3138. operation.BatchSize.Should().Be(options.BatchSize);
  3139. operation.Collation.Should().Be(options.Collation);
  3140. operation.CollectionNamespace.Should().Be(subject.CollectionNamespace);
  3141. operation.DatabaseNamespace.Should().BeNull();
  3142. operation.FullDocument.Should().Be(options.FullDocument);
  3143. operation.MaxAwaitTime.Should().Be(options.MaxAwaitTime);
  3144. operation.MessageEncoderSettings.Should().NotBeNull();
  3145. operation.Pipeline.Should().HaveCount(1);
  3146. operation.Pipeline[0].Should().Be(stageDocument);
  3147. operation.ReadConcern.Should().Be(subject.Settings.ReadConcern);
  3148. operation.ResultSerializer.ValueType.Should().Be(typeof(ChangeStreamDocument<BsonDocument>));
  3149. operation.ResumeAfter.Should().Be(options.ResumeAfter);
  3150. operation.RetryRequested.Should().BeTrue();
  3151. operation.StartAfter.Should().Be(options.StartAfter);
  3152. operation.StartAtOperationTime.Should().Be(options.StartAtOperationTime);
  3153. }
  3154. [Theory]
  3155. [ParameterAttributeData]
  3156. public void Watch_should_execute_a_ChangeStreamOperation_with_default_options_when_options_is_null(
  3157. [Values(false, true)] bool usingSession,
  3158. [Values(false, true)] bool async)
  3159. {
  3160. var subject = CreateSubject<BsonDocument>();
  3161. var session = CreateSession(usingSession);
  3162. var stageDocument = BsonDocument.Parse("{ $match : { operationType : \"insert\" } }");
  3163. var pipeline = (PipelineDefinition<ChangeStreamDocument<BsonDocument>, ChangeStreamDocument<BsonDocument>>)(new[] { stageDocument });
  3164. ChangeStreamOptions options = null;
  3165. var cancellationToken = new CancellationTokenSource().Token;
  3166. if (usingSession)
  3167. {
  3168. if (async)
  3169. {
  3170. subject.WatchAsync(session, pipeline, options, cancellationToken).GetAwaiter().GetResult();
  3171. }
  3172. else
  3173. {
  3174. subject.Watch(session, pipeline, options, cancellationToken);
  3175. }
  3176. }
  3177. else
  3178. {
  3179. if (async)
  3180. {
  3181. subject.WatchAsync(pipeline, options, cancellationToken).GetAwaiter().GetResult();
  3182. }
  3183. else
  3184. {
  3185. subject.Watch(pipeline, options, cancellationToken);
  3186. }
  3187. }
  3188. var call = _operationExecutor.GetReadCall<IChangeStreamCursor<ChangeStreamDocument<BsonDocument>>>();
  3189. VerifySessionAndCancellationToken(call, session, cancellationToken);
  3190. var operation = call.Operation.Should().BeOfType<ChangeStreamOperation<ChangeStreamDocument<BsonDocument>>>().Subject;
  3191. var defaultOptions = new ChangeStreamOptions();
  3192. operation.BatchSize.Should().Be(defaultOptions.BatchSize);
  3193. operation.Collation.Should().Be(defaultOptions.Collation);
  3194. operation.CollectionNamespace.Should().Be(subject.CollectionNamespace);
  3195. operation.FullDocument.Should().Be(defaultOptions.FullDocument);
  3196. operation.MaxAwaitTime.Should().Be(defaultOptions.MaxAwaitTime);
  3197. operation.MessageEncoderSettings.Should().NotBeNull();
  3198. operation.Pipeline.Should().HaveCount(1);
  3199. operation.Pipeline[0].Should().Be(stageDocument);
  3200. operation.ReadConcern.Should().Be(subject.Settings.ReadConcern);
  3201. operation.ResultSerializer.ValueType.Should().Be(typeof(ChangeStreamDocument<BsonDocument>));
  3202. operation.ResumeAfter.Should().Be(defaultOptions.ResumeAfter);
  3203. operation.RetryRequested.Should().BeTrue();
  3204. }
  3205. [Theory]
  3206. [ParameterAttributeData]
  3207. public void Watch_should_throw_when_pipeline_is_null(
  3208. [Values(false, true)] bool async)
  3209. {
  3210. var subject = CreateSubject<BsonDocument>();
  3211. PipelineDefinition<ChangeStreamDocument<BsonDocument>, ChangeStreamDocument<BsonDocument>> pipeline = null;
  3212. Exception exception;
  3213. if (async)
  3214. {
  3215. exception = Record.Exception(() => subject.WatchAsync(pipeline).GetAwaiter().GetResult());
  3216. }
  3217. else
  3218. {
  3219. exception = Record.Exception(() => subject.Watch(pipeline));
  3220. }
  3221. var e = exception.Should().BeOfType<ArgumentNullException>().Subject;
  3222. e.ParamName.Should().Be("pipeline");
  3223. }
  3224. [SkippableFact]
  3225. public void Watch_should_support_full_document_with_duplicate_elements()
  3226. {
  3227. RequireServer.Check().Supports(Feature.ChangeStreamStage).ClusterTypes(ClusterType.ReplicaSet, ClusterType.Sharded);
  3228. var client = DriverTestConfiguration.Client;
  3229. var database = client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName);
  3230. var collection = database.GetCollection<BsonDocument>(DriverTestConfiguration.CollectionNamespace.CollectionName);
  3231. database.DropCollection(collection.CollectionNamespace.CollectionName); // ensure a clean collection state (e.g. no indexes)
  3232. collection.InsertOne(new BsonDocument("_id", 1)); // ensure database exists
  3233. database.DropCollection(collection.CollectionNamespace.CollectionName);
  3234. try
  3235. {
  3236. var cursor = collection.Watch();
  3237. ChangeStreamDocument<BsonDocument> changeStreamDocument = null;
  3238. var document = new BsonDocument(allowDuplicateNames: true) { { "_id", 1 }, { "x", 2 }, { "x", 3 } };
  3239. collection.InsertOne(document);
  3240. SpinWait.SpinUntil(() => cursor.MoveNext() && (changeStreamDocument = cursor.Current.FirstOrDefault()) != null, TimeSpan.FromSeconds(5)).Should().BeTrue();
  3241. var fullDocument = changeStreamDocument.FullDocument;
  3242. fullDocument.ElementCount.Should().Be(3);
  3243. var firstElement = fullDocument.GetElement(0);
  3244. firstElement.Name.Should().Be("_id");
  3245. firstElement.Value.Should().Be(1);
  3246. var secondElement = fullDocument.GetElement(1);
  3247. secondElement.Name.Should().Be("x");
  3248. secondElement.Value.Should().Be(2);
  3249. var thirdElement = fullDocument.GetElement(2);
  3250. thirdElement.Name.Should().Be("x");
  3251. thirdElement.Value.Should().Be(3);
  3252. }
  3253. finally
  3254. {
  3255. database.DropCollection(collection.CollectionNamespace.CollectionName);
  3256. }
  3257. }
  3258. [Fact]
  3259. public void WithReadPreference_should_return_a_new_collection_with_the_read_preference_changed()
  3260. {
  3261. var subject = CreateSubject<BsonDocument>();
  3262. var newSubject = subject.WithReadPreference(ReadPreference.Nearest);
  3263. newSubject.Settings.ReadPreference.Should().Be(ReadPreference.Nearest);
  3264. }
  3265. [Fact]
  3266. public void WithWriteConcern_should_return_a_new_collection_with_the_write_concern_changed()
  3267. {
  3268. var subject = CreateSubject<BsonDocument>();
  3269. var newSubject = subject.WithWriteConcern(WriteConcern.WMajority);
  3270. newSubject.Settings.WriteConcern.Should().Be(WriteConcern.WMajority);
  3271. }
  3272. // private methods
  3273. private IMongoDatabase CreateDatabase(string databaseName = "foo")
  3274. {
  3275. var mockClient = CreateMockClient();
  3276. return mockClient.Object.GetDatabase(databaseName);
  3277. }
  3278. private Mock<IMongoClient> CreateMockClient()
  3279. {
  3280. var mockClient = new Mock<IMongoClient>();
  3281. mockClient.SetupGet(m => m.Settings).Returns(new MongoClientSettings());
  3282. mockClient
  3283. .Setup(m => m.GetDatabase(It.IsAny<string>(), It.IsAny<MongoDatabaseSettings>()))
  3284. .Returns((string databaseName, MongoDatabaseSettings databaseSettings) =>
  3285. {
  3286. var databaseNamespace = new DatabaseNamespace(databaseName);
  3287. var settings = new MongoDatabaseSettings();
  3288. settings.ApplyDefaultValues(mockClient.Object.Settings);
  3289. var cluster = new Mock<ICluster>().Object;
  3290. return new MongoDatabaseImpl(mockClient.Object, databaseNamespace, settings, cluster, _operationExecutor);
  3291. });
  3292. return mockClient;
  3293. }
  3294. private IClientSessionHandle CreateSession(bool usingSession)
  3295. {
  3296. if (usingSession)
  3297. {
  3298. var client = new Mock<IMongoClient>().Object;
  3299. var cluster = Mock.Of<ICluster>();
  3300. var options = new ClientSessionOptions();
  3301. var coreServerSession = new CoreServerSession();
  3302. var coreSession = new CoreSession(cluster, coreServerSession, options.ToCore());
  3303. var coreSessionHandle = new CoreSessionHandle(coreSession);
  3304. return new ClientSessionHandle(client, options, coreSessionHandle);
  3305. }
  3306. else
  3307. {
  3308. return null;
  3309. }
  3310. }
  3311. private IMongoCollection<TDocument> CreateSubject<TDocument>(IMongoDatabase database = null, string collectionName = "bar", MongoCollectionSettings settings = null)
  3312. {
  3313. database = database ?? CreateDatabase();
  3314. settings = settings ?? new MongoCollectionSettings();
  3315. settings.ReadConcern = _readConcern;
  3316. return database.GetCollection<TDocument>(collectionName, settings);
  3317. }
  3318. private RenderedPipelineDefinition<TOutput> RenderPipeline<TInput, TOutput>(IMongoCollection<TInput> collection, PipelineDefinition<TInput, TOutput> pipeline)
  3319. {
  3320. var inputSerializer = collection.DocumentSerializer;
  3321. var serializerRegistry = BsonSerializer.SerializerRegistry;
  3322. return pipeline.Render(inputSerializer, serializerRegistry);
  3323. }
  3324. private static void VerifyBulkWrite(MockOperationExecutor.WriteCall<BulkWriteOperationResult> call, bool? bypassDocumentValidation, bool isOrdered, WriteRequest[] expectedRequests)
  3325. {
  3326. var operation = call.Operation.Should().BeOfType<BulkMixedWriteOperation>().Subject;
  3327. operation.BypassDocumentValidation.Should().Be(bypassDocumentValidation);
  3328. operation.CollectionNamespace.FullName.Should().Be("foo.bar");
  3329. operation.IsOrdered.Should().Be(isOrdered);
  3330. var actualRequests = operation.Requests.ToList();
  3331. actualRequests.Count.Should().Be(expectedRequests.Length);
  3332. for (int i = 0; i < expectedRequests.Length; i++)
  3333. {
  3334. actualRequests[i].ShouldBeEquivalentTo(expectedRequests[i]);
  3335. }
  3336. }
  3337. private static void VerifySessionAndCancellationToken<TDocument>(MockOperationExecutor.ReadCall<TDocument> call, IClientSessionHandle session, CancellationToken cancellationToken)
  3338. {
  3339. call.CancellationToken.Should().Be(cancellationToken);
  3340. if (session == null)
  3341. {
  3342. call.UsedImplicitSession.Should().BeTrue();
  3343. }
  3344. else
  3345. {
  3346. call.SessionId.Should().Be(session.ServerSession.Id);
  3347. }
  3348. }
  3349. private static void VerifySessionAndCancellationToken<TDocument>(MockOperationExecutor.WriteCall<TDocument> call, IClientSessionHandle session, CancellationToken cancellationToken)
  3350. {
  3351. call.CancellationToken.Should().Be(cancellationToken);
  3352. if (session == null)
  3353. {
  3354. call.UsedImplicitSession.Should().BeTrue();
  3355. }
  3356. else
  3357. {
  3358. call.SessionId.Should().Be(session.ServerSession.Id);
  3359. }
  3360. }
  3361. private static void VerifySingleWrite<TRequest>(MockOperationExecutor.WriteCall<BulkWriteOperationResult> call, bool? bypassDocumentValidation, bool isOrdered, TRequest expectedRequest)
  3362. where TRequest : WriteRequest
  3363. {
  3364. VerifyBulkWrite(call, bypassDocumentValidation, isOrdered, new[] { expectedRequest });
  3365. }
  3366. private class A
  3367. {
  3368. public int PropA = 0;
  3369. }
  3370. private class B : A
  3371. {
  3372. public int PropB = 0;
  3373. }
  3374. }
  3375. }