Skip to content

Commit 6c0bb11

Browse files
committed
CSHARP-2145: Add integration tests for maxTimeMS in Core using failpoints
1 parent b768be4 commit 6c0bb11

20 files changed

+528
-0
lines changed
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
/* Copyright 2018-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;
17+
using System.Threading;
18+
using MongoDB.Bson;
19+
using MongoDB.Bson.Serialization.Serializers;
20+
using MongoDB.Driver.Core.Bindings;
21+
using MongoDB.Driver.Core.Clusters;
22+
using MongoDB.Driver.Core.Clusters.ServerSelectors;
23+
using MongoDB.Driver.Core.Misc;
24+
using MongoDB.Driver.Core.Operations;
25+
using MongoDB.Driver.Core.Servers;
26+
using MongoDB.Driver.Core.WireProtocol.Messages.Encoders;
27+
28+
namespace MongoDB.Driver.Core.TestHelpers
29+
{
30+
public static class FailPointName
31+
{
32+
// public constants
33+
public const string MaxTimeAlwaysTimeout = "maxTimeAlwaysTimeOut";
34+
public const string OnPrimaryTransactionalWrite = "onPrimaryTransactionalWrite";
35+
}
36+
37+
38+
public sealed class FailPoint : IDisposable
39+
{
40+
#region static
41+
// public static methods
42+
/// <summary>
43+
/// Create a FailPoint and executes a configureFailPoint command on the selected server.
44+
/// </summary>
45+
/// <param name="cluster">The cluster.</param>
46+
/// <param name="session">The session.</param>
47+
/// <param name="name">The name.</param>
48+
/// <param name="args">The arguments for the FailPoint.</param>
49+
/// <returns>A FailPoint containing the proper binding.</returns>
50+
public static FailPoint Configure(ICluster cluster, ICoreSessionHandle session, string name, BsonDocument args)
51+
{
52+
var server = GetWriteableServer(cluster);
53+
var binding = new SingleServerReadWriteBinding(server, session.Fork());
54+
var failpoint = new FailPoint(server, binding, name, args);
55+
try
56+
{
57+
failpoint.Configure();
58+
return failpoint;
59+
}
60+
catch
61+
{
62+
try { failpoint.Dispose(); } catch { }
63+
throw;
64+
}
65+
}
66+
67+
/// <summary>
68+
/// Creates a FailPoint that is always on.
69+
/// </summary>
70+
/// <param name="cluster">The cluster.</param>
71+
/// <param name="session">The session.</param>
72+
/// <param name="name">The name.</param>
73+
/// <returns>A FailPoint containing the proper binding.</returns>
74+
public static FailPoint ConfigureAlwaysOn(ICluster cluster, ICoreSessionHandle session, string name)
75+
{
76+
var args = new BsonDocument("mode", "alwaysOn");
77+
return Configure(cluster, session, name, args);
78+
}
79+
80+
/// <summary>
81+
/// Creates a FailPoint that fails <paramref name="n"/> times.
82+
/// </summary>
83+
/// <param name="cluster">The cluster.</param>
84+
/// <param name="session">The session.</param>
85+
/// <param name="name">The name.</param>
86+
/// <param name="n">The number of times to fail.</param>
87+
/// <returns>A FailPoint containing the proper binding.</returns>
88+
public static FailPoint ConfigureTimes(ICluster cluster, ICoreSessionHandle session, string name, int n)
89+
{
90+
var args = new BsonDocument("mode", new BsonDocument("times", n));
91+
return Configure(cluster, session, name, args);
92+
}
93+
94+
// private static methods
95+
private static IServer GetWriteableServer(ICluster cluster)
96+
{
97+
var selector = WritableServerSelector.Instance;
98+
return cluster.SelectServer(selector, CancellationToken.None);
99+
}
100+
#endregion
101+
102+
// private fields
103+
private readonly BsonDocument _args;
104+
private readonly IReadWriteBinding _binding;
105+
private bool _disposed;
106+
private readonly string _name;
107+
private readonly IServer _server;
108+
109+
// constructors
110+
/// <summary>
111+
/// Construct a new FailPoing with a specified name and custom arguments.
112+
/// </summary>
113+
/// <param name="server">The server.</param>
114+
/// <param name="binding">Must be a single server binding.</param>
115+
/// <param name="name">The name.</param>
116+
/// <param name="args">The arguments for the FailPoint.</param>
117+
public FailPoint(IServer server, IReadWriteBinding binding, string name, BsonDocument args)
118+
{
119+
_server = Ensure.IsNotNull(server, nameof(server));
120+
_binding = Ensure.IsNotNull(binding, nameof(binding));
121+
_name = Ensure.IsNotNull(name, nameof(name));
122+
_args = Ensure.IsNotNull(args, nameof(args));
123+
124+
}
125+
126+
// public properties
127+
/// <summary>
128+
/// The binding used by the FailPoint and associated commands.
129+
/// </summary>
130+
/// <value>The binding is a single server binding.</value>
131+
public IReadWriteBinding Binding => _binding;
132+
133+
// public methods
134+
/// <summary>
135+
/// Configures the failpoint on the server.
136+
/// </summary>
137+
public void Configure()
138+
{
139+
Configure(_args);
140+
}
141+
142+
public void Dispose()
143+
{
144+
if (!_disposed)
145+
{
146+
try { ConfigureOff(); } catch { }
147+
_binding.Dispose();
148+
_disposed = true;
149+
}
150+
}
151+
152+
// private methods
153+
private void Configure(BsonDocument args)
154+
{
155+
var command = new BsonDocument("configureFailPoint", _name);
156+
command.Merge(args);
157+
ExecuteCommand(command);
158+
}
159+
160+
private void ConfigureOff()
161+
{
162+
var args = new BsonDocument("mode", "off");
163+
Configure(args);
164+
}
165+
166+
private void ExecuteCommand(BsonDocument command)
167+
{
168+
var adminDatabase = new DatabaseNamespace("admin");
169+
var operation = new WriteCommandOperation<BsonDocument>(
170+
adminDatabase,
171+
command,
172+
BsonDocumentSerializer.Instance,
173+
new MessageEncoderSettings());
174+
operation.Execute(_binding, CancellationToken.None);
175+
}
176+
}
177+
}

tests/MongoDB.Driver.Core.TestHelpers/MongoDB.Driver.Core.TestHelpers.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
<ItemGroup>
6161
<Compile Include="CoreTestConfiguration.cs" />
6262
<Compile Include="EventCapturer.cs" />
63+
<Compile Include="FailPoint.cs" />
6364
<Compile Include="ICoreSessionHandleExtensions.cs" />
6465
<Compile Include="Properties\AssemblyInfo.cs" />
6566
<Compile Include="TestCoreSession.cs" />

tests/MongoDB.Driver.Core.Tests/Core/Operations/AggregateExplainOperationTests.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
using MongoDB.Bson.TestHelpers.XunitExtensions;
2121
using MongoDB.Driver.Core.Clusters;
2222
using MongoDB.Driver.Core.Misc;
23+
using MongoDB.Driver.Core.TestHelpers;
2324
using MongoDB.Driver.Core.TestHelpers.XunitExtensions;
2425
using Xunit;
2526

@@ -361,6 +362,23 @@ public void Execute_should_throw_when_Collation_is_set_but_not_supported(
361362
exception.Should().BeOfType<NotSupportedException>();
362363
}
363364

365+
[SkippableTheory]
366+
[ParameterAttributeData]
367+
public void Execute_should_throw_when_maxTime_is_exceeded(
368+
[Values(false, true)] bool async)
369+
{
370+
RequireServer.Check().Supports(Feature.AggregateExplain, Feature.FailPoints).ClusterTypes(ClusterType.Standalone, ClusterType.ReplicaSet);
371+
372+
var subject = new AggregateExplainOperation(_collectionNamespace, __pipeline, _messageEncoderSettings) { MaxTime = TimeSpan.FromSeconds(9001) };
373+
374+
using (var failPoint = FailPoint.ConfigureAlwaysOn(CoreTestConfiguration.Cluster, _session, FailPointName.MaxTimeAlwaysTimeout))
375+
{
376+
var exception = Record.Exception(() => ExecuteOperation(subject, failPoint.Binding, async));
377+
378+
exception.Should().BeOfType<MongoExecutionTimeoutException>();
379+
}
380+
}
381+
364382
[SkippableTheory]
365383
[ParameterAttributeData]
366384
public void Execute_should_return_expected_result_when_Comment_is_set(

tests/MongoDB.Driver.Core.Tests/Core/Operations/AggregateOperationTests.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
using Xunit;
2626
using System.Reflection;
2727
using MongoDB.Driver.Core.Clusters;
28+
using MongoDB.Driver.Core.TestHelpers;
2829

2930
namespace MongoDB.Driver.Core.Operations
3031
{
@@ -559,6 +560,23 @@ public void Execute_should_throw_when_binding_is_null(
559560
argumentNullException.ParamName.Should().Be("binding");
560561
}
561562

563+
[SkippableTheory]
564+
[ParameterAttributeData]
565+
public void Execute_should_throw_when_maxTime_is_exceeded(
566+
[Values(false, true)] bool async)
567+
{
568+
RequireServer.Check().Supports(Feature.Aggregate, Feature.FailPoints).ClusterTypes(ClusterType.Standalone, ClusterType.ReplicaSet);
569+
570+
var subject = new AggregateOperation<BsonDocument>(_collectionNamespace, __pipeline, __resultSerializer, _messageEncoderSettings) { MaxTime = TimeSpan.FromSeconds(9001) };
571+
572+
using (var failPoint = FailPoint.ConfigureAlwaysOn(CoreTestConfiguration.Cluster, _session, FailPointName.MaxTimeAlwaysTimeout))
573+
{
574+
var exception = Record.Exception(() => ExecuteOperation(subject, failPoint.Binding, async));
575+
576+
exception.Should().BeOfType<MongoExecutionTimeoutException>();
577+
}
578+
}
579+
562580
[Theory]
563581
[ParameterAttributeData]
564582
public void Execute_should_throw_when_pipeline_ends_with_out(

tests/MongoDB.Driver.Core.Tests/Core/Operations/AggregateToCollectionOperationTests.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
using MongoDB.Bson.TestHelpers.XunitExtensions;
2121
using MongoDB.Driver.Core.Clusters;
2222
using MongoDB.Driver.Core.Misc;
23+
using MongoDB.Driver.Core.TestHelpers;
2324
using MongoDB.Driver.Core.TestHelpers.XunitExtensions;
2425
using Xunit;
2526

@@ -496,6 +497,30 @@ public void Execute_should_throw_when_Collation_is_set(
496497
exception.Should().BeOfType<NotSupportedException>();
497498
}
498499

500+
[SkippableTheory]
501+
[ParameterAttributeData]
502+
public void Execute_should_throw_when_maxTime_is_exceeded(
503+
[Values(false, true)] bool async)
504+
{
505+
RequireServer.Check().Supports(Feature.FailPoints, Feature.AggregateOut).ClusterTypes(ClusterType.Standalone, ClusterType.ReplicaSet);
506+
var pipeline = new[]
507+
{
508+
BsonDocument.Parse("{ $match : { x : \"x\" } }"),
509+
BsonDocument.Parse("{ $out : \"awesome\" }")
510+
};
511+
var subject = new AggregateToCollectionOperation(_collectionNamespace, pipeline, _messageEncoderSettings)
512+
{
513+
MaxTime = TimeSpan.FromSeconds(9001)
514+
};
515+
516+
using (var failPoint = FailPoint.ConfigureAlwaysOn(CoreTestConfiguration.Cluster, _session, FailPointName.MaxTimeAlwaysTimeout))
517+
{
518+
var exception = Record.Exception(() => ExecuteOperation(subject, failPoint.Binding, async));
519+
520+
exception.Should().BeOfType<MongoExecutionTimeoutException>();
521+
}
522+
}
523+
499524
[SkippableTheory]
500525
[ParameterAttributeData]
501526
public void Execute_should_return_expected_result_when_Comment_is_set(

tests/MongoDB.Driver.Core.Tests/Core/Operations/CountOperationTests.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
using FluentAssertions;
1818
using MongoDB.Bson;
1919
using MongoDB.Bson.TestHelpers.XunitExtensions;
20+
using MongoDB.Driver.Core.Clusters;
2021
using MongoDB.Driver.Core.Misc;
22+
using MongoDB.Driver.Core.TestHelpers;
2123
using MongoDB.Driver.Core.TestHelpers.XunitExtensions;
2224
using Xunit;
2325

@@ -481,6 +483,22 @@ public void Execute_should_throw_when_Collation_is_set_but_not_supported(
481483
exception.Should().BeOfType<NotSupportedException>();
482484
}
483485

486+
[SkippableTheory]
487+
[ParameterAttributeData]
488+
public void Execute_should_throw_when_maxTime_is_exceeded(
489+
[Values(false, true)] bool async)
490+
{
491+
RequireServer.Check().Supports(Feature.FailPoints).ClusterTypes(ClusterType.Standalone, ClusterType.ReplicaSet);
492+
var subject = new CountOperation(_collectionNamespace, _messageEncoderSettings) { MaxTime = TimeSpan.FromSeconds(9001) };
493+
494+
using (var failPoint = FailPoint.ConfigureAlwaysOn(CoreTestConfiguration.Cluster, _session, FailPointName.MaxTimeAlwaysTimeout))
495+
{
496+
var exception = Record.Exception(() => ExecuteOperation(subject, failPoint.Binding, async));
497+
498+
exception.Should().BeOfType<MongoExecutionTimeoutException>();
499+
}
500+
}
501+
484502
[SkippableTheory]
485503
[ParameterAttributeData]
486504
public void Execute_should_return_expected_result_when_Filter_is_set(

tests/MongoDB.Driver.Core.Tests/Core/Operations/CreateIndexesUsingCommandOperationTests.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
using MongoDB.Bson.TestHelpers.XunitExtensions;
2222
using MongoDB.Driver.Core.Clusters;
2323
using MongoDB.Driver.Core.Misc;
24+
using MongoDB.Driver.Core.TestHelpers;
2425
using MongoDB.Driver.Core.TestHelpers.XunitExtensions;
2526
using Xunit;
2627

@@ -156,6 +157,23 @@ public void CreateCommand_should_return_expected_result_when_WriteConcern_is_set
156157
result.Should().Be(expectedResult);
157158
}
158159

160+
[SkippableTheory]
161+
[ParameterAttributeData]
162+
public void Execute_should_throw_when_maxTime_is_exceeded(
163+
[Values(false, true)] bool async)
164+
{
165+
RequireServer.Check().Supports(Feature.CreateIndexesCommand, Feature.FailPoints).ClusterTypes(ClusterType.Standalone, ClusterType.ReplicaSet);
166+
var requests = new[] { new CreateIndexRequest(new BsonDocument("x", 1)) };
167+
var subject = new CreateIndexesUsingCommandOperation(_collectionNamespace, requests, _messageEncoderSettings) { MaxTime = TimeSpan.FromSeconds(9001) };
168+
169+
using (var failPoint = FailPoint.ConfigureAlwaysOn(CoreTestConfiguration.Cluster, _session, FailPointName.MaxTimeAlwaysTimeout))
170+
{
171+
var exception = Record.Exception(() => ExecuteOperation(subject, failPoint.Binding, async));
172+
173+
exception.Should().BeOfType<MongoExecutionTimeoutException>();
174+
}
175+
}
176+
159177
[SkippableTheory]
160178
[ParameterAttributeData]
161179
public void Execute_should_work_when_background_is_true(

tests/MongoDB.Driver.Core.Tests/Core/Operations/DistinctOperationTests.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919
using MongoDB.Bson.Serialization;
2020
using MongoDB.Bson.Serialization.Serializers;
2121
using MongoDB.Bson.TestHelpers.XunitExtensions;
22+
using MongoDB.Driver.Core.Clusters;
2223
using MongoDB.Driver.Core.Misc;
24+
using MongoDB.Driver.Core.TestHelpers;
2325
using MongoDB.Driver.Core.TestHelpers.XunitExtensions;
2426
using Xunit;
2527

@@ -467,6 +469,22 @@ public void Execute_should_throw_when_Collation_is_set_and_not_supported(
467469
exception.Should().BeOfType<NotSupportedException>();
468470
}
469471

472+
[SkippableTheory]
473+
[ParameterAttributeData]
474+
public void Execute_should_throw_when_maxTime_is_exceeded(
475+
[Values(false, true)] bool async)
476+
{
477+
RequireServer.Check().Supports(Feature.FailPoints).ClusterTypes(ClusterType.Standalone, ClusterType.ReplicaSet);
478+
var subject = new DistinctOperation<int>(_collectionNamespace, _valueSerializer, _fieldName, _messageEncoderSettings) { MaxTime = TimeSpan.FromSeconds(9001) };
479+
480+
using (var failPoint = FailPoint.ConfigureAlwaysOn(CoreTestConfiguration.Cluster, _session, FailPointName.MaxTimeAlwaysTimeout))
481+
{
482+
var exception = Record.Exception(() => ExecuteOperation(subject, failPoint.Binding, async));
483+
484+
exception.Should().BeOfType<MongoExecutionTimeoutException>();
485+
}
486+
}
487+
470488
[SkippableTheory]
471489
[ParameterAttributeData]
472490
public void Execute_should_throw_when_ReadConcern_is_set_and_not_supported(

0 commit comments

Comments
 (0)