/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

Large files are truncated click here to view the full file

  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, cancellation