Skip to content

Commit b9b4d66

Browse files
committed
CSHARP-2596: Refactoring and more tests.
1 parent 97434a2 commit b9b4d66

8 files changed

+208
-28
lines changed

src/MongoDB.Driver/IMongoCollectionExtensions.cs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,7 @@ public static IMongoQueryable<TDocument> AsQueryable<TDocument>(this IMongoColle
7272
{
7373
Ensure.IsNotNull(collection, nameof(collection));
7474

75-
aggregateOptions = aggregateOptions ?? new AggregateOptions();
76-
var provider = new MongoQueryProviderImpl<TDocument>(null, collection, aggregateOptions);
77-
return new MongoQueryableImpl<TDocument, TDocument>(provider);
75+
return AsQueryableHelper(collection, session: null, aggregateOptions);
7876
}
7977

8078
/// <summary>
@@ -88,10 +86,9 @@ public static IMongoQueryable<TDocument> AsQueryable<TDocument>(this IMongoColle
8886
public static IMongoQueryable<TDocument> AsQueryable<TDocument>(this IMongoCollection<TDocument> collection, IClientSessionHandle session, AggregateOptions aggregateOptions = null)
8987
{
9088
Ensure.IsNotNull(collection, nameof(collection));
89+
Ensure.IsNotNull(session, nameof(session));
9190

92-
aggregateOptions = aggregateOptions ?? new AggregateOptions();
93-
var provider = new MongoQueryProviderImpl<TDocument>(session, collection, aggregateOptions);
94-
return new MongoQueryableImpl<TDocument, TDocument>(provider);
91+
return AsQueryableHelper(collection, session, aggregateOptions);
9592
}
9693

9794
/// <summary>
@@ -2326,5 +2323,13 @@ public static Task<IChangeStreamCursor<ChangeStreamDocument<TDocument>>> WatchAs
23262323
var emptyPipeline = new EmptyPipelineDefinition<ChangeStreamDocument<TDocument>>();
23272324
return collection.WatchAsync(session, emptyPipeline, options, cancellationToken);
23282325
}
2326+
2327+
// private static methods
2328+
private static IMongoQueryable<TDocument> AsQueryableHelper<TDocument>(IMongoCollection<TDocument> collection, IClientSessionHandle session, AggregateOptions aggregateOptions)
2329+
{
2330+
aggregateOptions = aggregateOptions ?? new AggregateOptions();
2331+
var provider = new MongoQueryProviderImpl<TDocument>(collection, session, aggregateOptions);
2332+
return new MongoQueryableImpl<TDocument, TDocument>(provider);
2333+
}
23292334
}
23302335
}

src/MongoDB.Driver/Linq/AggregateQueryableExecutionModel.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ public override string ToString()
8181
return sb.ToString();
8282
}
8383

84-
internal override object Execute<TInput>(IClientSessionHandle session, IMongoCollection<TInput> collection, AggregateOptions options)
84+
internal override object Execute<TInput>(IMongoCollection<TInput> collection, IClientSessionHandle session, AggregateOptions options)
8585
{
8686
var pipeline = CreatePipeline<TInput>();
8787

@@ -95,7 +95,7 @@ internal override object Execute<TInput>(IClientSessionHandle session, IMongoCol
9595
}
9696
}
9797

98-
internal override Task ExecuteAsync<TInput>(IClientSessionHandle session, IMongoCollection<TInput> collection, AggregateOptions options, CancellationToken cancellationToken)
98+
internal override Task ExecuteAsync<TInput>(IMongoCollection<TInput> collection, IClientSessionHandle session, AggregateOptions options, CancellationToken cancellationToken)
9999
{
100100
var pipeline = CreatePipeline<TInput>();
101101

src/MongoDB.Driver/Linq/MongoQueryProviderImpl.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,13 @@ namespace MongoDB.Driver.Linq
3131
internal sealed class MongoQueryProviderImpl<TDocument> : IMongoQueryProvider
3232
{
3333
private readonly IMongoCollection<TDocument> _collection;
34-
private readonly IClientSessionHandle _session;
3534
private readonly AggregateOptions _options;
35+
private readonly IClientSessionHandle _session;
3636

37-
public MongoQueryProviderImpl(IClientSessionHandle session, IMongoCollection<TDocument> collection, AggregateOptions options)
37+
public MongoQueryProviderImpl(IMongoCollection<TDocument> collection, IClientSessionHandle session, AggregateOptions options)
3838
{
39-
_session = session; // can be null
4039
_collection = Ensure.IsNotNull(collection, nameof(collection));
40+
_session = session; // can be null
4141
_options = Ensure.IsNotNull(options, nameof(options));
4242
}
4343

@@ -109,12 +109,12 @@ public QueryableExecutionModel GetExecutionModel(Expression expression)
109109

110110
internal object ExecuteModel(QueryableExecutionModel model)
111111
{
112-
return model.Execute(_session, _collection, _options);
112+
return model.Execute(_collection, _session, _options);
113113
}
114114

115-
private Task ExecuteModelAsync(QueryableExecutionModel model, CancellationToken cancellationToken)
115+
internal Task ExecuteModelAsync(QueryableExecutionModel model, CancellationToken cancellationToken)
116116
{
117-
return model.ExecuteAsync(_session, _collection, _options, cancellationToken);
117+
return model.ExecuteAsync(_collection, _session, _options, cancellationToken);
118118
}
119119

120120
private Expression Prepare(Expression expression)

src/MongoDB.Driver/Linq/QueryableExecutionModel.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ internal QueryableExecutionModel()
3535
{
3636
}
3737

38-
internal abstract Task ExecuteAsync<TInput>(IClientSessionHandle session, IMongoCollection<TInput> collection, AggregateOptions options, CancellationToken cancellationToken);
38+
internal abstract Task ExecuteAsync<TInput>(IMongoCollection<TInput> collection, IClientSessionHandle session, AggregateOptions options, CancellationToken cancellationToken);
3939

40-
internal abstract object Execute<TInput>(IClientSessionHandle session, IMongoCollection<TInput> collection, AggregateOptions options);
40+
internal abstract object Execute<TInput>(IMongoCollection<TInput> collection, IClientSessionHandle session, AggregateOptions options);
4141
}
4242
}

tests/MongoDB.Driver.Tests/IMongoCollectionExtensionsTests.cs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,18 +54,30 @@ public void Aggregate_should_return_expected_result(
5454
fluent._session().Should().BeSameAs(session);
5555
}
5656

57-
[Fact]
58-
public void AsQueryable_should_return_expected_result()
57+
[Theory]
58+
[ParameterAttributeData]
59+
public void AsQueryable_should_return_expected_result(
60+
[Values(false, true)] bool withSession)
5961
{
6062
var collection = CreateMockCollection().Object;
63+
var session = withSession ? Mock.Of<IClientSessionHandle>() : null;
6164
var options = new AggregateOptions();
6265

63-
var result = collection.AsQueryable(options);
66+
IMongoQueryable<Person> result;
67+
if (withSession)
68+
{
69+
result = collection.AsQueryable(session, options);
70+
}
71+
else
72+
{
73+
result = collection.AsQueryable(options);
74+
}
6475

6576
var queryable = result.Should().BeOfType<MongoQueryableImpl<Person, Person>>().Subject;
6677
var provider = queryable.Provider.Should().BeOfType<MongoQueryProviderImpl<Person>>().Subject;
6778
provider._collection().Should().BeSameAs(collection);
6879
provider._options().Should().BeSameAs(options);
80+
provider._session().Should().BeSameAs(session);
6981
}
7082

7183
[Theory]
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/* Copyright 2020-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+
16+
using System.Threading;
17+
using MongoDB.Bson;
18+
using MongoDB.Bson.Serialization.Serializers;
19+
using MongoDB.Bson.TestHelpers.XunitExtensions;
20+
using MongoDB.Driver.Linq;
21+
using Moq;
22+
using Xunit;
23+
24+
namespace MongoDB.Driver.Tests.Linq
25+
{
26+
public class AggregateQueryableExecutionModelTests
27+
{
28+
[Theory]
29+
[ParameterAttributeData]
30+
public void Execute_should_call_Aggregate_with_expected_arguments(
31+
[Values(false, true)] bool withSession,
32+
[Values(false, true)] bool async)
33+
{
34+
var subject = CreateSubject();
35+
var mockCollection = CreateMockCollection();
36+
var session = withSession ? Mock.Of<IClientSessionHandle>() : null;
37+
var options = new AggregateOptions();
38+
var cancellationToken = new CancellationToken();
39+
40+
if (async)
41+
{
42+
_ = subject.ExecuteAsync(mockCollection.Object, session, options, cancellationToken);
43+
44+
if (withSession)
45+
{
46+
mockCollection.Verify(m => m.AggregateAsync(session, It.IsAny<PipelineDefinition<BsonDocument, BsonDocument>>(), options, cancellationToken), Times.Once);
47+
}
48+
else
49+
{
50+
mockCollection.Verify(m => m.AggregateAsync(It.IsAny<PipelineDefinition<BsonDocument, BsonDocument>>(), options, cancellationToken), Times.Once);
51+
}
52+
}
53+
else
54+
{
55+
_ = subject.Execute(mockCollection.Object, session, options);
56+
57+
if (withSession)
58+
{
59+
mockCollection.Verify(m => m.Aggregate(session, It.IsAny<PipelineDefinition<BsonDocument, BsonDocument>>(), options, CancellationToken.None), Times.Once);
60+
}
61+
else
62+
{
63+
mockCollection.Verify(m => m.Aggregate(It.IsAny<PipelineDefinition<BsonDocument, BsonDocument>>(), options, CancellationToken.None), Times.Once);
64+
}
65+
}
66+
}
67+
68+
// private methods
69+
private Mock<IMongoCollection<BsonDocument>> CreateMockCollection()
70+
{
71+
return new Mock<IMongoCollection<BsonDocument>>();
72+
}
73+
74+
private AggregateQueryableExecutionModel<BsonDocument> CreateSubject()
75+
{
76+
var stages = new BsonDocument[0];
77+
var outputSerializer = BsonDocumentSerializer.Instance;
78+
return new AggregateQueryableExecutionModel<BsonDocument>(stages, outputSerializer);
79+
}
80+
}
81+
}

tests/MongoDB.Driver.Tests/Linq/MongoQueryProviderImplTests.cs

Lines changed: 82 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,23 +13,97 @@
1313
* limitations under the License.
1414
*/
1515

16-
using System.Reflection;
16+
using System;
17+
using System.Threading;
18+
using FluentAssertions;
19+
using MongoDB.Bson;
20+
using MongoDB.Bson.TestHelpers;
21+
using MongoDB.Bson.TestHelpers.XunitExtensions;
1722
using MongoDB.Driver.Linq;
23+
using Moq;
24+
using Xunit;
1825

1926
namespace MongoDB.Driver.Tests.Linq
2027
{
21-
internal static class MongoQueryProviderImplReflector
28+
public class MongoQueryProviderImplTests
2229
{
23-
public static IMongoCollection<TDocument> _collection<TDocument>(this MongoQueryProviderImpl<TDocument> obj)
30+
[Theory]
31+
[ParameterAttributeData]
32+
public void constructor_should_initialize_instance(
33+
[Values(false, true)] bool withSession)
2434
{
25-
var fieldInfo = typeof(MongoQueryProviderImpl<TDocument>).GetField("_collection", BindingFlags.NonPublic | BindingFlags.Instance);
26-
return (IMongoCollection<TDocument>)fieldInfo.GetValue(obj);
35+
var collection = Mock.Of<IMongoCollection<BsonDocument>>();
36+
var session = withSession ? Mock.Of<IClientSessionHandle>() : null;
37+
var options = new AggregateOptions();
38+
39+
var subject = new MongoQueryProviderImpl<BsonDocument>(collection, session, options);
40+
41+
subject._collection().Should().BeSameAs(collection);
42+
subject._session().Should().BeSameAs(session);
43+
subject._options().Should().BeSameAs(options);
2744
}
2845

29-
public static AggregateOptions _options<TDocument>(this MongoQueryProviderImpl<TDocument> obj)
46+
[Fact]
47+
public void constructor_should_throw_whe_collection_is_null()
48+
{
49+
var session = Mock.Of<IClientSessionHandle>();
50+
var options = new AggregateOptions();
51+
52+
var exception = Record.Exception(() => new MongoQueryProviderImpl<BsonDocument>(collection: null, session, options));
53+
54+
var e = exception.Should().BeOfType<ArgumentNullException>().Subject;
55+
e.ParamName.Should().Be("collection");
56+
}
57+
58+
[Fact]
59+
public void constructor_should_throw_whe_options_is_null()
60+
{
61+
var collection = Mock.Of<IMongoCollection<BsonDocument>>();
62+
var session = Mock.Of<IClientSessionHandle>();
63+
64+
var exception = Record.Exception(() => new MongoQueryProviderImpl<BsonDocument>(collection, session, options: null));
65+
66+
var e = exception.Should().BeOfType<ArgumentNullException>().Subject;
67+
e.ParamName.Should().Be("options");
68+
}
69+
70+
[Theory]
71+
[ParameterAttributeData]
72+
public void ExecuteModel_should_call_Execute_with_expected_arguments(
73+
[Values(false, true)] bool withSession,
74+
[Values(false, true)] bool async)
3075
{
31-
var fieldInfo = typeof(MongoQueryProviderImpl<TDocument>).GetField("_options", BindingFlags.NonPublic | BindingFlags.Instance);
32-
return (AggregateOptions)fieldInfo.GetValue(obj);
76+
var collection = Mock.Of<IMongoCollection<BsonDocument>>();
77+
var session = withSession ? Mock.Of<IClientSessionHandle>() : null;
78+
var options = new AggregateOptions();
79+
var subject = new MongoQueryProviderImpl<BsonDocument>(collection, session, options);
80+
var mockModel = new Mock<QueryableExecutionModel>();
81+
var cancellationToken = new CancellationToken();
82+
83+
if (async)
84+
{
85+
subject.ExecuteModelAsync(mockModel.Object, cancellationToken).GetAwaiter().GetResult();
86+
87+
mockModel.Verify(m => m.ExecuteAsync(collection, session, options, cancellationToken), Times.Once);
88+
}
89+
else
90+
{
91+
subject.ExecuteModel(mockModel.Object);
92+
93+
mockModel.Verify(m => m.Execute(collection, session, options), Times.Once);
94+
}
3395
}
3496
}
97+
98+
internal static class MongoQueryProviderImplReflector
99+
{
100+
public static IMongoCollection<TDocument> _collection<TDocument>(this MongoQueryProviderImpl<TDocument> obj)
101+
=> (IMongoCollection<TDocument>)Reflector.GetFieldValue(obj, nameof(_collection));
102+
103+
public static AggregateOptions _options<TDocument>(this MongoQueryProviderImpl<TDocument> obj)
104+
=> (AggregateOptions)Reflector.GetFieldValue(obj, nameof(_options));
105+
106+
public static IClientSessionHandle _session<TDocument>(this MongoQueryProviderImpl<TDocument> obj)
107+
=> (IClientSessionHandle)Reflector.GetFieldValue(obj, nameof(_session));
108+
}
35109
}

tests/MongoDB.Driver.Tests/Linq/MongoQueryableTests.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
using FluentAssertions;
2121
using MongoDB.Bson;
2222
using MongoDB.Driver;
23+
using MongoDB.Driver.Core.Clusters;
24+
using MongoDB.Driver.Core.Misc;
2325
using MongoDB.Driver.Core.TestHelpers.XunitExtensions;
2426
using MongoDB.Driver.Linq;
2527
using MongoDB.Driver.Tests;
@@ -1717,9 +1719,15 @@ public void Where_method_with_predicated_any()
17171719
"{ $match : { 'G' : { '$elemMatch' : { 'D' : \"Don't\" } } } }");
17181720
}
17191721

1720-
[Fact]
1722+
[SkippableFact]
17211723
public void AsQueryable_in_transaction()
17221724
{
1725+
RequireServer.Check().ClusterTypes(ClusterType.ReplicaSet, ClusterType.Sharded).Supports(Feature.Transactions);
1726+
if (CoreTestConfiguration.Cluster.Description.Type == ClusterType.Sharded)
1727+
{
1728+
RequireServer.Check().Supports(Feature.ShardedTransactions);
1729+
}
1730+
17231731
using (var session = DriverTestConfiguration.Client.StartSession())
17241732
{
17251733
session.StartTransaction();

0 commit comments

Comments
 (0)