PageRenderTime 62ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/tests/MongoDB.Driver.Tests/OfTypeMongoCollectionTests.cs

http://github.com/mongodb/mongo-csharp-driver
C# | 1078 lines | 940 code | 123 blank | 15 comment | 66 complexity | ba5f2845f19c2f2d82d587b1058597d4 MD5 | raw file
Possible License(s): Apache-2.0
  1. /* Copyright 2013-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.Threading;
  19. using FluentAssertions;
  20. using MongoDB.Bson;
  21. using MongoDB.Bson.Serialization;
  22. using MongoDB.Bson.Serialization.Attributes;
  23. using MongoDB.Bson.TestHelpers.XunitExtensions;
  24. using MongoDB.Driver.Core.Bindings;
  25. using Moq;
  26. using Xunit;
  27. namespace MongoDB.Driver.Tests
  28. {
  29. public class OfTypeMongoCollectionTests
  30. {
  31. private BsonDocument _ofTypeFilter;
  32. private BsonDocument _expectedFilter;
  33. private BsonDocument _providedFilter;
  34. private Mock<IMongoCollection<A>> _mockRootCollection;
  35. private Mock<IMongoCollection<B>> _mockDerivedCollection;
  36. private IMongoCollection<A> _rootCollection;
  37. private IMongoCollection<B> _derivedCollection;
  38. public OfTypeMongoCollectionTests()
  39. {
  40. _ofTypeFilter = new BsonDocument("_t", "B");
  41. _providedFilter = new BsonDocument("PropB", 4);
  42. _expectedFilter = new BsonDocument("_t", "B").Add("PropB", 4);
  43. _mockRootCollection = new Mock<IMongoCollection<A>>();
  44. _mockRootCollection.SetupGet(c => c.CollectionNamespace).Returns(CollectionNamespace.FromFullName("foo.bar"));
  45. _mockRootCollection.SetupGet(c => c.Settings).Returns(new MongoCollectionSettings());
  46. _mockDerivedCollection = new Mock<IMongoCollection<B>>();
  47. _mockDerivedCollection.SetupGet(c => c.CollectionNamespace).Returns(CollectionNamespace.FromFullName("foo.bar"));
  48. _mockDerivedCollection.SetupGet(c => c.Settings).Returns(new MongoCollectionSettings());
  49. _rootCollection = _mockRootCollection.Object;
  50. _derivedCollection = _mockDerivedCollection.Object;
  51. }
  52. [Theory]
  53. [ParameterAttributeData]
  54. public void Aggregate_should_add_match_to_beginning_of_pipeline(
  55. [Values(false, true)] bool async)
  56. {
  57. var subject = CreateSubject();
  58. var options = new AggregateOptions();
  59. if (async)
  60. {
  61. subject.AggregateAsync<B>(new[] { new BsonDocument("$skip", 10) }, options, CancellationToken.None);
  62. _mockDerivedCollection.Verify(
  63. c => c.AggregateAsync(
  64. It.Is<PipelineDefinition<B, B>>(p => RenderPipeline(p)[0].Equals(new BsonDocument("$match", _ofTypeFilter))),
  65. options,
  66. CancellationToken.None),
  67. Times.Once);
  68. }
  69. else
  70. {
  71. subject.Aggregate<B>(new[] { new BsonDocument("$skip", 10) }, options, CancellationToken.None);
  72. _mockDerivedCollection.Verify(
  73. c => c.Aggregate(
  74. It.Is<PipelineDefinition<B, B>>(p => RenderPipeline(p)[0].Equals(new BsonDocument("$match", _ofTypeFilter))),
  75. options,
  76. CancellationToken.None),
  77. Times.Once);
  78. }
  79. }
  80. [Theory]
  81. [ParameterAttributeData]
  82. public void Aggregate_should_combine_match_statements_at_the_beginning_of_a_pipeline(
  83. [Values(false, true)] bool async)
  84. {
  85. var subject = CreateSubject();
  86. var options = new AggregateOptions();
  87. if (async)
  88. {
  89. subject.AggregateAsync<B>(new[] { new BsonDocument("$match", new BsonDocument("x", 1)) }, options, CancellationToken.None);
  90. var expectedFilter = new BsonDocument(_ofTypeFilter).Add("x", 1);
  91. _mockDerivedCollection.Verify(
  92. c => c.AggregateAsync(
  93. It.Is<PipelineDefinition<B, B>>(p => RenderPipeline(p)[0].Equals(new BsonDocument("$match", expectedFilter))),
  94. options,
  95. CancellationToken.None),
  96. Times.Once);
  97. }
  98. else
  99. {
  100. subject.Aggregate<B>(new[] { new BsonDocument("$match", new BsonDocument("x", 1)) }, options, CancellationToken.None);
  101. var expectedFilter = new BsonDocument(_ofTypeFilter).Add("x", 1);
  102. _mockDerivedCollection.Verify(
  103. c => c.Aggregate(
  104. It.Is<PipelineDefinition<B, B>>(p => RenderPipeline(p)[0].Equals(new BsonDocument("$match", expectedFilter))),
  105. options,
  106. CancellationToken.None),
  107. Times.Once);
  108. }
  109. }
  110. [Theory]
  111. [ParameterAttributeData]
  112. public void AggregateToCollection_should_add_match_to_beginning_of_pipeline(
  113. [Values(false, true)] bool usingSession,
  114. [Values(false, true)] bool async)
  115. {
  116. var subject = CreateSubject();
  117. var session = CreateSession(usingSession);
  118. var options = new AggregateOptions();
  119. if (async)
  120. {
  121. if (usingSession)
  122. {
  123. subject.AggregateToCollectionAsync<B>(session, new[] { new BsonDocument("$skip", 10) }, options, CancellationToken.None);
  124. _mockDerivedCollection.Verify(
  125. c => c.AggregateToCollectionAsync(
  126. session,
  127. It.Is<PipelineDefinition<B, B>>(p => RenderPipeline(p)[0].Equals(new BsonDocument("$match", _ofTypeFilter))),
  128. options,
  129. CancellationToken.None),
  130. Times.Once);
  131. }
  132. else
  133. {
  134. subject.AggregateToCollectionAsync<B>(new[] { new BsonDocument("$skip", 10) }, options, CancellationToken.None);
  135. _mockDerivedCollection.Verify(
  136. c => c.AggregateToCollectionAsync(
  137. It.Is<PipelineDefinition<B, B>>(p => RenderPipeline(p)[0].Equals(new BsonDocument("$match", _ofTypeFilter))),
  138. options,
  139. CancellationToken.None),
  140. Times.Once);
  141. }
  142. }
  143. else
  144. {
  145. if (usingSession)
  146. {
  147. subject.AggregateToCollection<B>(session, new[] { new BsonDocument("$skip", 10) }, options, CancellationToken.None);
  148. _mockDerivedCollection.Verify(
  149. c => c.AggregateToCollection(
  150. session,
  151. It.Is<PipelineDefinition<B, B>>(p => RenderPipeline(p)[0].Equals(new BsonDocument("$match", _ofTypeFilter))),
  152. options,
  153. CancellationToken.None),
  154. Times.Once);
  155. }
  156. else
  157. {
  158. subject.AggregateToCollection<B>(new[] { new BsonDocument("$skip", 10) }, options, CancellationToken.None);
  159. _mockDerivedCollection.Verify(
  160. c => c.AggregateToCollection(
  161. It.Is<PipelineDefinition<B, B>>(p => RenderPipeline(p)[0].Equals(new BsonDocument("$match", _ofTypeFilter))),
  162. options,
  163. CancellationToken.None),
  164. Times.Once);
  165. }
  166. }
  167. }
  168. [Theory]
  169. [ParameterAttributeData]
  170. public void AggregateToCollection_should_combine_match_statements_at_the_beginning_of_a_pipeline(
  171. [Values(false, true)] bool async)
  172. {
  173. var subject = CreateSubject();
  174. var options = new AggregateOptions();
  175. if (async)
  176. {
  177. subject.AggregateToCollectionAsync<B>(new[] { new BsonDocument("$match", new BsonDocument("x", 1)) }, options, CancellationToken.None);
  178. var expectedFilter = new BsonDocument(_ofTypeFilter).Add("x", 1);
  179. _mockDerivedCollection.Verify(
  180. c => c.AggregateToCollectionAsync(
  181. It.Is<PipelineDefinition<B, B>>(p => RenderPipeline(p)[0].Equals(new BsonDocument("$match", expectedFilter))),
  182. options,
  183. CancellationToken.None),
  184. Times.Once);
  185. }
  186. else
  187. {
  188. subject.AggregateToCollection<B>(new[] { new BsonDocument("$match", new BsonDocument("x", 1)) }, options, CancellationToken.None);
  189. var expectedFilter = new BsonDocument(_ofTypeFilter).Add("x", 1);
  190. _mockDerivedCollection.Verify(
  191. c => c.AggregateToCollection(
  192. It.Is<PipelineDefinition<B, B>>(p => RenderPipeline(p)[0].Equals(new BsonDocument("$match", expectedFilter))),
  193. options,
  194. CancellationToken.None),
  195. Times.Once);
  196. }
  197. }
  198. [Theory]
  199. [ParameterAttributeData]
  200. public void BulkWrite_with_DeleteOne(
  201. [Values(false, true)] bool async)
  202. {
  203. var subject = CreateSubject();
  204. var model = new DeleteOneModel<B>(_providedFilter)
  205. {
  206. Collation = new Collation("en_US"),
  207. Hint = new BsonDocument("_id", 1)
  208. };
  209. var options = new BulkWriteOptions();
  210. if (async)
  211. {
  212. subject.BulkWriteAsync(new[] { model }, options, CancellationToken.None);
  213. _mockDerivedCollection.Verify(
  214. c => c.BulkWriteAsync(
  215. It.Is<IEnumerable<WriteModel<B>>>(v => v.OfType<DeleteOneModel<B>>()
  216. .Where(m => RenderFilter(m.Filter).Equals(_expectedFilter) &&
  217. m.Collation == model.Collation &&
  218. m.Hint == model.Hint).Count() == 1),
  219. options,
  220. CancellationToken.None),
  221. Times.Once);
  222. }
  223. else
  224. {
  225. subject.BulkWrite(new[] { model }, options, CancellationToken.None);
  226. _mockDerivedCollection.Verify(
  227. c => c.BulkWrite(
  228. It.Is<IEnumerable<WriteModel<B>>>(v => v.OfType<DeleteOneModel<B>>()
  229. .Where(m => RenderFilter(m.Filter).Equals(_expectedFilter) &&
  230. m.Collation == model.Collation &&
  231. m.Hint == model.Hint).Count() == 1),
  232. options,
  233. CancellationToken.None),
  234. Times.Once);
  235. }
  236. }
  237. [Theory]
  238. [ParameterAttributeData]
  239. public void BulkWrite_with_DeleteMany(
  240. [Values(false, true)] bool async)
  241. {
  242. var subject = CreateSubject();
  243. var model = new DeleteManyModel<B>(_providedFilter)
  244. {
  245. Hint = new BsonDocument("_id", 1)
  246. };
  247. var options = new BulkWriteOptions();
  248. if (async)
  249. {
  250. subject.BulkWriteAsync(new[] { model }, options, CancellationToken.None);
  251. _mockDerivedCollection.Verify(
  252. c => c.BulkWriteAsync(
  253. It.Is<IEnumerable<WriteModel<B>>>(v => v.OfType<DeleteManyModel<B>>()
  254. .Where(m => m.Hint == model.Hint && RenderFilter(m.Filter).Equals(_expectedFilter)).Count() == 1),
  255. options,
  256. CancellationToken.None),
  257. Times.Once);
  258. }
  259. else
  260. {
  261. subject.BulkWrite(new[] { model }, options, CancellationToken.None);
  262. _mockDerivedCollection.Verify(
  263. c => c.BulkWrite(
  264. It.Is<IEnumerable<WriteModel<B>>>(v => v.OfType<DeleteManyModel<B>>()
  265. .Where(m => m.Hint == model.Hint && RenderFilter(m.Filter).Equals(_expectedFilter)).Count() == 1),
  266. options,
  267. CancellationToken.None),
  268. Times.Once);
  269. }
  270. }
  271. [Theory]
  272. [ParameterAttributeData]
  273. public void BulkWrite_with_ReplaceOne(
  274. [Values(false, true)] bool async)
  275. {
  276. var subject = CreateSubject();
  277. var replacement = new B();
  278. var model = new ReplaceOneModel<B>(_providedFilter, replacement)
  279. {
  280. Hint = new BsonDocument("_id", 1),
  281. IsUpsert = true
  282. };
  283. var options = new BulkWriteOptions();
  284. if (async)
  285. {
  286. subject.BulkWriteAsync(new[] { model }, options, CancellationToken.None);
  287. _mockDerivedCollection.Verify(
  288. c => c.BulkWriteAsync(
  289. It.Is<IEnumerable<WriteModel<B>>>(v => v.OfType<ReplaceOneModel<B>>()
  290. .Where(m => RenderFilter(m.Filter).Equals(_expectedFilter) &&
  291. m.Replacement == model.Replacement &&
  292. m.Hint == model.Hint &&
  293. m.IsUpsert == model.IsUpsert).Count() == 1),
  294. options,
  295. CancellationToken.None),
  296. Times.Once);
  297. }
  298. else
  299. {
  300. subject.BulkWrite(new[] { model }, options, CancellationToken.None);
  301. _mockDerivedCollection.Verify(
  302. c => c.BulkWrite(
  303. It.Is<IEnumerable<WriteModel<B>>>(v => v.OfType<ReplaceOneModel<B>>()
  304. .Where(m => RenderFilter(m.Filter).Equals(_expectedFilter) &&
  305. m.Replacement == model.Replacement &&
  306. m.Hint == model.Hint &&
  307. m.IsUpsert == model.IsUpsert).Count() == 1),
  308. options,
  309. CancellationToken.None),
  310. Times.Once);
  311. }
  312. }
  313. [Theory]
  314. [ParameterAttributeData]
  315. public void BulkWrite_with_UpdateMany(
  316. [Values(false, true)] bool async)
  317. {
  318. var subject = CreateSubject();
  319. var collation = new Collation("en_US");
  320. var model = new UpdateManyModel<B>(_providedFilter, "{$set: {x: 1}}")
  321. {
  322. Collation = collation,
  323. Hint = new BsonDocument("_id", 1),
  324. IsUpsert = true
  325. };
  326. var options = new BulkWriteOptions();
  327. if (async)
  328. {
  329. subject.BulkWriteAsync(new[] { model }, options, CancellationToken.None);
  330. _mockDerivedCollection.Verify(
  331. c => c.BulkWriteAsync(
  332. It.Is<IEnumerable<WriteModel<B>>>(v => v.OfType<UpdateManyModel<B>>()
  333. .Where(m => RenderFilter(m.Filter).Equals(_expectedFilter) &&
  334. RenderUpdate(m.Update).Equals(BsonDocument.Parse("{$set: {x: 1}}")) &&
  335. m.Collation == model.Collation &&
  336. m.Hint == model.Hint &&
  337. m.IsUpsert == model.IsUpsert).Count() == 1),
  338. options,
  339. CancellationToken.None),
  340. Times.Once);
  341. }
  342. else
  343. {
  344. subject.BulkWrite(new[] { model }, options, CancellationToken.None);
  345. _mockDerivedCollection.Verify(
  346. c => c.BulkWrite(
  347. It.Is<IEnumerable<WriteModel<B>>>(v => v.OfType<UpdateManyModel<B>>()
  348. .Where(m => RenderFilter(m.Filter).Equals(_expectedFilter) &&
  349. RenderUpdate(m.Update).Equals(BsonDocument.Parse("{$set: {x: 1}}")) &&
  350. m.Collation == model.Collation &&
  351. m.Hint == model.Hint &&
  352. m.IsUpsert == model.IsUpsert).Count() == 1),
  353. options,
  354. CancellationToken.None),
  355. Times.Once);
  356. }
  357. }
  358. [Theory]
  359. [ParameterAttributeData]
  360. public void BulkWrite_with_UpdateOne(
  361. [Values(false, true)] bool async)
  362. {
  363. var subject = CreateSubject();
  364. var collation = new Collation("en_US");
  365. var model = new UpdateOneModel<B>(_providedFilter, "{$set: {x: 1}}")
  366. {
  367. Collation = collation,
  368. Hint = new BsonDocument("_id", 1),
  369. IsUpsert = true
  370. };
  371. var options = new BulkWriteOptions();
  372. if (async)
  373. {
  374. subject.BulkWriteAsync(new[] { model }, options, CancellationToken.None);
  375. _mockDerivedCollection.Verify(
  376. c => c.BulkWriteAsync(
  377. It.Is<IEnumerable<WriteModel<B>>>(v => v.OfType<UpdateOneModel<B>>()
  378. .Where(m => RenderFilter(m.Filter).Equals(_expectedFilter) &&
  379. RenderUpdate(m.Update).Equals(BsonDocument.Parse("{$set: {x: 1}}")) &&
  380. m.Collation == model.Collation &&
  381. m.Hint == model.Hint &&
  382. m.IsUpsert == model.IsUpsert).Count() == 1),
  383. options,
  384. CancellationToken.None),
  385. Times.Once);
  386. }
  387. else
  388. {
  389. subject.BulkWrite(new[] { model }, options, CancellationToken.None);
  390. _mockDerivedCollection.Verify(
  391. c => c.BulkWrite(
  392. It.Is<IEnumerable<WriteModel<B>>>(v => v.OfType<UpdateOneModel<B>>()
  393. .Where(m => RenderFilter(m.Filter).Equals(_expectedFilter) &&
  394. RenderUpdate(m.Update).Equals(BsonDocument.Parse("{$set: {x: 1}}")) &&
  395. m.Collation == model.Collation &&
  396. m.Hint == model.Hint &&
  397. m.IsUpsert == model.IsUpsert).Count() == 1),
  398. options,
  399. CancellationToken.None),
  400. Times.Once);
  401. }
  402. }
  403. [Theory]
  404. [ParameterAttributeData]
  405. public void Count_should_include_the_filter(
  406. [Values(false, true)] bool async)
  407. {
  408. var subject = CreateSubject();
  409. var options = new CountOptions();
  410. if (async)
  411. {
  412. #pragma warning disable 618
  413. subject.CountAsync(_providedFilter, options, CancellationToken.None);
  414. _mockDerivedCollection.Verify(
  415. c => c.CountAsync(
  416. It.Is<FilterDefinition<B>>(f => RenderFilter(f).Equals(_expectedFilter)),
  417. options,
  418. CancellationToken.None),
  419. Times.Once);
  420. #pragma warning restore
  421. }
  422. else
  423. {
  424. #pragma warning disable 618
  425. subject.Count(_providedFilter, options, CancellationToken.None);
  426. _mockDerivedCollection.Verify(
  427. c => c.Count(
  428. It.Is<FilterDefinition<B>>(f => RenderFilter(f).Equals(_expectedFilter)),
  429. options,
  430. CancellationToken.None),
  431. Times.Once);
  432. #pragma warning restore
  433. }
  434. }
  435. [Theory]
  436. [ParameterAttributeData]
  437. public void CountDocuments_should_include_the_filter(
  438. [Values(false, true)] bool usingSession,
  439. [Values(false, true)] bool async)
  440. {
  441. var subject = CreateSubject();
  442. var session = CreateSession(usingSession);
  443. var options = new CountOptions();
  444. if (async)
  445. {
  446. if (usingSession)
  447. {
  448. subject.CountDocumentsAsync(session, _providedFilter, options, CancellationToken.None);
  449. _mockDerivedCollection.Verify(
  450. c => c.CountDocumentsAsync(
  451. session,
  452. It.Is<FilterDefinition<B>>(f => RenderFilter(f).Equals(_expectedFilter)),
  453. options,
  454. CancellationToken.None),
  455. Times.Once);
  456. }
  457. else
  458. {
  459. subject.CountDocumentsAsync(_providedFilter, options, CancellationToken.None);
  460. _mockDerivedCollection.Verify(
  461. c => c.CountDocumentsAsync(
  462. It.Is<FilterDefinition<B>>(f => RenderFilter(f).Equals(_expectedFilter)),
  463. options,
  464. CancellationToken.None),
  465. Times.Once);
  466. }
  467. }
  468. else
  469. {
  470. if (usingSession)
  471. {
  472. subject.CountDocuments(session, _providedFilter, options, CancellationToken.None);
  473. _mockDerivedCollection.Verify(
  474. c => c.CountDocuments(
  475. session,
  476. It.Is<FilterDefinition<B>>(f => RenderFilter(f).Equals(_expectedFilter)),
  477. options,
  478. CancellationToken.None),
  479. Times.Once);
  480. }
  481. else
  482. {
  483. subject.CountDocuments(_providedFilter, options, CancellationToken.None);
  484. _mockDerivedCollection.Verify(
  485. c => c.CountDocuments(
  486. It.Is<FilterDefinition<B>>(f => RenderFilter(f).Equals(_expectedFilter)),
  487. options,
  488. CancellationToken.None),
  489. Times.Once);
  490. }
  491. }
  492. }
  493. [Theory]
  494. [ParameterAttributeData]
  495. public void Distinct_should_include_the_filter(
  496. [Values(false, true)] bool async)
  497. {
  498. var subject = CreateSubject();
  499. var options = new DistinctOptions();
  500. if (async)
  501. {
  502. subject.DistinctAsync(x => x.PropA, _providedFilter, options, CancellationToken.None);
  503. _mockDerivedCollection.Verify(
  504. c => c.DistinctAsync(
  505. It.Is<FieldDefinition<B, int>>(f => RenderField(f).Equals("PropA")),
  506. It.Is<FilterDefinition<B>>(f => RenderFilter(f).Equals(_expectedFilter)),
  507. options,
  508. CancellationToken.None),
  509. Times.Once);
  510. }
  511. else
  512. {
  513. subject.Distinct(x => x.PropA, _providedFilter, options, CancellationToken.None);
  514. _mockDerivedCollection.Verify(
  515. c => c.Distinct(
  516. It.Is<FieldDefinition<B, int>>(f => RenderField(f).Equals("PropA")),
  517. It.Is<FilterDefinition<B>>(f => RenderFilter(f).Equals(_expectedFilter)),
  518. options,
  519. CancellationToken.None),
  520. Times.Once);
  521. }
  522. }
  523. [Theory]
  524. [ParameterAttributeData]
  525. public void EstimatedDocumentCount_should_throw(
  526. [Values(false, true)] bool async)
  527. {
  528. var subject = CreateSubject();
  529. var cancellationToken = new CancellationTokenSource().Token;
  530. var exception = Record.Exception(() =>
  531. {
  532. if (async)
  533. {
  534. subject.EstimatedDocumentCountAsync(null, cancellationToken).GetAwaiter().GetResult();
  535. }
  536. else
  537. {
  538. subject.EstimatedDocumentCount(null, cancellationToken);
  539. }
  540. });
  541. exception.Should().BeOfType<NotSupportedException>();
  542. }
  543. [Theory]
  544. [ParameterAttributeData]
  545. public void Find_should_include_the_filter(
  546. [Values(false, true)] bool async)
  547. {
  548. var subject = CreateSubject();
  549. var options = new FindOptions<B>();
  550. if (async)
  551. {
  552. subject.FindAsync(_providedFilter, options, CancellationToken.None);
  553. _mockDerivedCollection.Verify(
  554. c => c.FindAsync(
  555. It.Is<FilterDefinition<B>>(f => RenderFilter(f).Equals(_expectedFilter)),
  556. options,
  557. CancellationToken.None),
  558. Times.Once);
  559. }
  560. else
  561. {
  562. subject.FindSync(_providedFilter, options, CancellationToken.None);
  563. _mockDerivedCollection.Verify(
  564. c => c.FindSync(
  565. It.Is<FilterDefinition<B>>(f => RenderFilter(f).Equals(_expectedFilter)),
  566. options,
  567. CancellationToken.None),
  568. Times.Once);
  569. }
  570. }
  571. [Theory]
  572. [ParameterAttributeData]
  573. public void FindOneAndDelete_should_include_the_filter(
  574. [Values(false, true)] bool async)
  575. {
  576. var subject = CreateSubject();
  577. var options = new FindOneAndDeleteOptions<B>();
  578. if (async)
  579. {
  580. subject.FindOneAndDeleteAsync(_providedFilter, options, CancellationToken.None);
  581. _mockDerivedCollection.Verify(
  582. c => c.FindOneAndDeleteAsync(
  583. It.Is<FilterDefinition<B>>(f => RenderFilter(f).Equals(_expectedFilter)),
  584. options,
  585. CancellationToken.None),
  586. Times.Once);
  587. }
  588. else
  589. {
  590. subject.FindOneAndDelete(_providedFilter, options, CancellationToken.None);
  591. _mockDerivedCollection.Verify(
  592. c => c.FindOneAndDelete(
  593. It.Is<FilterDefinition<B>>(f => RenderFilter(f).Equals(_expectedFilter)),
  594. options,
  595. CancellationToken.None),
  596. Times.Once);
  597. }
  598. }
  599. [Theory]
  600. [ParameterAttributeData]
  601. public void FindOneAndReplace_should_include_the_filter(
  602. [Values(false, true)] bool async)
  603. {
  604. var subject = CreateSubject();
  605. var replacement = new B();
  606. var options = new FindOneAndReplaceOptions<B>();
  607. if (async)
  608. {
  609. subject.FindOneAndReplaceAsync(_providedFilter, replacement, options, CancellationToken.None);
  610. _mockDerivedCollection.Verify(
  611. c => c.FindOneAndReplaceAsync(
  612. It.Is<FilterDefinition<B>>(f => RenderFilter(f).Equals(_expectedFilter)),
  613. replacement,
  614. options,
  615. CancellationToken.None),
  616. Times.Once);
  617. }
  618. else
  619. {
  620. subject.FindOneAndReplace(_providedFilter, replacement, options, CancellationToken.None);
  621. _mockDerivedCollection.Verify(
  622. c => c.FindOneAndReplace(
  623. It.Is<FilterDefinition<B>>(f => RenderFilter(f).Equals(_expectedFilter)),
  624. replacement,
  625. options,
  626. CancellationToken.None),
  627. Times.Once);
  628. }
  629. }
  630. [Theory]
  631. [ParameterAttributeData]
  632. public void FindOneAndUpdate_should_include_the_filter(
  633. [Values(false, true)] bool async)
  634. {
  635. var subject = CreateSubject();
  636. var update = new BsonDocument("$set", new BsonDocument("x", 5));
  637. var options = new FindOneAndUpdateOptions<B>();
  638. if (async)
  639. {
  640. subject.FindOneAndUpdateAsync(_providedFilter, update, options, CancellationToken.None);
  641. _mockDerivedCollection.Verify(
  642. c => c.FindOneAndUpdateAsync(
  643. It.Is<FilterDefinition<B>>(f => RenderFilter(f).Equals(_expectedFilter)),
  644. It.Is<UpdateDefinition<B>>(u => RenderUpdate(u).Equals(BsonDocument.Parse("{$set: {x: 5}}"))),
  645. options,
  646. CancellationToken.None),
  647. Times.Once);
  648. }
  649. else
  650. {
  651. subject.FindOneAndUpdate(_providedFilter, update, options, CancellationToken.None);
  652. _mockDerivedCollection.Verify(
  653. c => c.FindOneAndUpdate(
  654. It.Is<FilterDefinition<B>>(f => RenderFilter(f).Equals(_expectedFilter)),
  655. It.Is<UpdateDefinition<B>>(u => RenderUpdate(u).Equals(BsonDocument.Parse("{$set: {x: 5}}"))),
  656. options,
  657. CancellationToken.None),
  658. Times.Once);
  659. }
  660. }
  661. [Theory]
  662. [ParameterAttributeData]
  663. public void MapReduce_should_include_the_filter_when_one_was_not_provided(
  664. [Values(false, true)] bool async)
  665. {
  666. var subject = CreateSubject();
  667. if (async)
  668. {
  669. subject.MapReduceAsync<B>("map", "reduce", null, CancellationToken.None);
  670. _mockDerivedCollection.Verify(
  671. c => c.MapReduceAsync(
  672. "map",
  673. "reduce",
  674. It.Is<MapReduceOptions<B, B>>(o => RenderFilter(o.Filter).Equals(_ofTypeFilter)),
  675. CancellationToken.None),
  676. Times.Once);
  677. }
  678. else
  679. {
  680. subject.MapReduce<B>("map", "reduce", null, CancellationToken.None);
  681. _mockDerivedCollection.Verify(
  682. c => c.MapReduce(
  683. "map",
  684. "reduce",
  685. It.Is<MapReduceOptions<B, B>>(o => RenderFilter(o.Filter).Equals(_ofTypeFilter)),
  686. CancellationToken.None),
  687. Times.Once);
  688. }
  689. }
  690. [Theory]
  691. [ParameterAttributeData]
  692. public void MapReduce_should_include_the_filter(
  693. [Values(false, true)] bool async)
  694. {
  695. var subject = CreateSubject();
  696. var options = new MapReduceOptions<B, B>
  697. {
  698. Filter = _providedFilter
  699. };
  700. if (async)
  701. {
  702. subject.MapReduceAsync("map", "reduce", options, CancellationToken.None);
  703. _mockDerivedCollection.Verify(
  704. c => c.MapReduceAsync(
  705. "map",
  706. "reduce",
  707. It.Is<MapReduceOptions<B, B>>(o => RenderFilter(o.Filter).Equals(_expectedFilter)),
  708. CancellationToken.None),
  709. Times.Once);
  710. }
  711. else
  712. {
  713. subject.MapReduce("map", "reduce", options, CancellationToken.None);
  714. _mockDerivedCollection.Verify(
  715. c => c.MapReduce(
  716. "map",
  717. "reduce",
  718. It.Is<MapReduceOptions<B, B>>(o => RenderFilter(o.Filter).Equals(_expectedFilter)),
  719. CancellationToken.None),
  720. Times.Once);
  721. }
  722. }
  723. [Fact]
  724. public void OfType_should_resort_to_root_collections_OfType()
  725. {
  726. var subject = CreateSubject();
  727. subject.OfType<C>();
  728. _mockRootCollection.Verify(c => c.OfType<C>(), Times.Once);
  729. _mockDerivedCollection.Verify(c => c.OfType<C>(), Times.Never);
  730. }
  731. // private methods
  732. private IClientSessionHandle CreateSession(bool usingSession)
  733. {
  734. if (usingSession)
  735. {
  736. var client = Mock.Of<IMongoClient>();
  737. var options = new ClientSessionOptions();
  738. var coreSession = Mock.Of<ICoreSession>();
  739. var coreServerSession = Mock.Of<ICoreServerSession>();
  740. Mock.Get(coreSession).SetupGet(m => m.ServerSession).Returns(coreServerSession);
  741. var coreSessionHandle = new CoreSessionHandle(coreSession);
  742. return new ClientSessionHandle(client, options, coreSessionHandle);
  743. }
  744. else
  745. {
  746. return null;
  747. }
  748. }
  749. private OfTypeMongoCollection<A, B> CreateSubject()
  750. {
  751. return new OfTypeMongoCollection<A, B>(_rootCollection, _derivedCollection, _ofTypeFilter);
  752. }
  753. private string RenderField<TDocument, TField>(FieldDefinition<TDocument, TField> field)
  754. {
  755. var serializer = BsonSerializer.SerializerRegistry.GetSerializer<TDocument>();
  756. return field.Render(serializer, BsonSerializer.SerializerRegistry).FieldName;
  757. }
  758. private BsonDocument RenderFilter<TDocument>(FilterDefinition<TDocument> filter)
  759. {
  760. var serializer = BsonSerializer.SerializerRegistry.GetSerializer<TDocument>();
  761. var doc = filter.Render(serializer, BsonSerializer.SerializerRegistry);
  762. return doc;
  763. }
  764. private List<BsonDocument> RenderPipeline<TInput, TOutput>(PipelineDefinition<TInput, TOutput> pipeline)
  765. {
  766. var serializer = BsonSerializer.SerializerRegistry.GetSerializer<TInput>();
  767. return pipeline.Render(serializer, BsonSerializer.SerializerRegistry).Documents.ToList();
  768. }
  769. private BsonDocument RenderUpdate<TDocument>(UpdateDefinition<TDocument> update)
  770. {
  771. var serializer = BsonSerializer.SerializerRegistry.GetSerializer<TDocument>();
  772. return update.Render(serializer, BsonSerializer.SerializerRegistry).AsBsonDocument;
  773. }
  774. public class A
  775. {
  776. public int PropA;
  777. }
  778. public class B : A
  779. {
  780. public int PropB;
  781. }
  782. public class C : B
  783. {
  784. public int PropC;
  785. }
  786. }
  787. public class OfTypeCollectionIntegrationTests
  788. {
  789. private IMongoCollection<BsonDocument> _docsCollection;
  790. private IMongoCollection<A> _rootCollection;
  791. public OfTypeCollectionIntegrationTests()
  792. {
  793. var client = DriverTestConfiguration.Client;
  794. var db = client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName);
  795. db.DropCollection(DriverTestConfiguration.CollectionNamespace.CollectionName);
  796. _docsCollection = db.GetCollection<BsonDocument>(DriverTestConfiguration.CollectionNamespace.CollectionName);
  797. _rootCollection = db.GetCollection<A>(DriverTestConfiguration.CollectionNamespace.CollectionName);
  798. var docs = new List<A>();
  799. docs.Add(new A { PropA = 1 });
  800. docs.Add(new A { PropA = 2 });
  801. docs.Add(new A { PropA = 3 });
  802. docs.Add(new B { PropA = 4, PropB = 1 });
  803. docs.Add(new B { PropA = 5, PropB = 2 });
  804. docs.Add(new B { PropA = 6, PropB = 3 });
  805. docs.Add(new C { PropA = 7, PropB = 4, PropC = 1 });
  806. docs.Add(new C { PropA = 8, PropB = 5, PropC = 2 });
  807. docs.Add(new C { PropA = 9, PropB = 6, PropC = 3 });
  808. _rootCollection.InsertMany(docs);
  809. }
  810. [Theory]
  811. [ParameterAttributeData]
  812. public void Count_should_only_count_derived_types(
  813. [Values(false, true)] bool async)
  814. {
  815. var subject = CreateSubject();
  816. long result1, result2;
  817. if (async)
  818. {
  819. #pragma warning disable 618
  820. result1 = subject.CountAsync("{}").GetAwaiter().GetResult();
  821. result2 = subject.OfType<C>().CountAsync("{}").GetAwaiter().GetResult();
  822. #pragma warning restore
  823. }
  824. else
  825. {
  826. #pragma warning disable 618
  827. result1 = subject.Count("{}");
  828. result2 = subject.OfType<C>().Count("{}");
  829. #pragma warning restore
  830. }
  831. result1.Should().Be(6);
  832. result2.Should().Be(3);
  833. }
  834. [Theory]
  835. [ParameterAttributeData]
  836. public void Count_should_only_count_derived_types_with_a_filter(
  837. [Values(false, true)] bool async)
  838. {
  839. var subject = CreateSubject();
  840. long result;
  841. if (async)
  842. {
  843. #pragma warning disable 618
  844. result = subject.CountAsync(x => x.PropB > 2).GetAwaiter().GetResult();
  845. #pragma warning restore
  846. }
  847. else
  848. {
  849. #pragma warning disable 618
  850. result = subject.Count(x => x.PropB > 2);
  851. #pragma warning restore
  852. }
  853. result.Should().Be(4);
  854. }
  855. [Theory]
  856. [ParameterAttributeData]
  857. public void InsertOne_should_include_discriminator_when_document_is_of_type_B(
  858. [Values(false, true)] bool async)
  859. {
  860. var subject = CreateSubject();
  861. var document = new B { PropA = 10, PropB = 7 };
  862. if (async)
  863. {
  864. subject.InsertOneAsync(document).GetAwaiter().GetResult();
  865. }
  866. else
  867. {
  868. subject.InsertOne(document);
  869. }
  870. var insertedB = _docsCollection.FindSync("{PropA: 10}").Single();
  871. insertedB["_t"].Should().Be(new BsonArray(new[] { "A", "B" }));
  872. }
  873. [Theory]
  874. [ParameterAttributeData]
  875. public void InsertOne_should_include_discriminator_when_document_is_of_type_C(
  876. [Values(false, true)] bool async)
  877. {
  878. var subject = CreateSubject();
  879. var document = new C { PropA = 11, PropB = 8, PropC = 4 };
  880. if (async)
  881. {
  882. subject.InsertOneAsync(document).GetAwaiter().GetResult();
  883. }
  884. else
  885. {
  886. subject.InsertOne(document);
  887. }
  888. var insertedC = _docsCollection.FindSync("{PropA: 11}").Single();
  889. insertedC["_t"].Should().Be(new BsonArray(new[] { "A", "B", "C" }));
  890. }
  891. [Theory]
  892. [ParameterAttributeData]
  893. public void ReplaceOne_should_not_match_document_of_wrong_type(
  894. [Values(false, true)] bool async)
  895. {
  896. var subject = CreateSubject();
  897. var repacement = new B { PropA = 10, PropB = 7 };
  898. ReplaceOneResult result;
  899. if (async)
  900. {
  901. result = subject.ReplaceOneAsync("{PropA: 1}", repacement).GetAwaiter().GetResult();
  902. }
  903. else
  904. {
  905. result = subject.ReplaceOne("{PropA: 1}", repacement);
  906. }
  907. result.MatchedCount.Should().Be(0); // document matching { PropA : 1 } is not of type B
  908. }
  909. [Theory]
  910. [ParameterAttributeData]
  911. public void ReplaceOne_should_match_document_of_right_type(
  912. [Values(false, true)] bool async)
  913. {
  914. var subject = CreateSubject();
  915. var originalDocument = _docsCollection.FindSync("{ PropA : 4 }").Single();
  916. var replacement = new B { PropA = 10, PropB = 7 };
  917. ReplaceOneResult result;
  918. if (async)
  919. {
  920. result = subject.ReplaceOneAsync("{PropA: 4}", replacement).GetAwaiter().GetResult();
  921. }
  922. else
  923. {
  924. result = subject.ReplaceOne("{PropA: 4}", replacement);
  925. }
  926. result.MatchedCount.Should().Be(1); // document matching { PropA : 4 } is of type B
  927. var replacedB = _docsCollection.FindSync("{ PropA : 10 }").Single();
  928. replacedB.Should().Be(new BsonDocument
  929. {
  930. { "_id", originalDocument["_id"] },
  931. { "_t", new BsonArray { "A", "B" } },
  932. { "PropA", 10 },
  933. { "PropB", 7 }
  934. });
  935. }
  936. private IMongoCollection<B> CreateSubject()
  937. {
  938. return _rootCollection.OfType<B>();
  939. }
  940. [BsonDiscriminator(RootClass = true)]
  941. [BsonKnownTypes(typeof(B), typeof(C))]
  942. public class A
  943. {
  944. public int PropA;
  945. }
  946. public class B : A
  947. {
  948. public int PropB;
  949. }
  950. public class C : B
  951. {
  952. public int PropC;
  953. }
  954. }
  955. }