public void Authenticate_should_throw_an_AuthenticationException_when_authentication_fails( [Values("MongoConnectionException", "MongoNotPrimaryException")] string exceptionName, [Values(false, true)] bool async) { var subject = new ScramSha256Authenticator(__credential, serverApi: null); var responseException = CoreExceptionHelper.CreateException(exceptionName); var connection = new MockConnection(__serverId); connection.EnqueueCommandResponseMessage(responseException); connection.Description = __descriptionCommandWireProtocol; Exception exception; if (async) { exception = Record.Exception(() => subject.AuthenticateAsync(connection, __descriptionCommandWireProtocol, CancellationToken.None).GetAwaiter().GetResult()); } else { exception = Record.Exception(() => subject.Authenticate(connection, __descriptionCommandWireProtocol, CancellationToken.None)); } exception.Should().BeOfType <MongoAuthenticationException>(); }
public void GetChannel_should_update_topology_and_clear_connection_pool_on_network_error_or_timeout( [Values("TimedOutSocketException", "NetworkUnreachableSocketException")] string errorType, [Values(false, true)] bool async) { var serverId = new ServerId(_clusterId, _endPoint); var connectionId = new ConnectionId(serverId); var innerMostException = CoreExceptionHelper.CreateException(errorType); var openConnectionException = new MongoConnectionException(connectionId, "Oops", new IOException("Cry", innerMostException)); var mockConnection = new Mock <IConnectionHandle>(); mockConnection.Setup(c => c.Open(It.IsAny <CancellationToken>())).Throws(openConnectionException); mockConnection.Setup(c => c.OpenAsync(It.IsAny <CancellationToken>())).ThrowsAsync(openConnectionException); var connectionFactory = new Mock <IConnectionFactory>(); connectionFactory.Setup(cf => cf.CreateConnection(serverId, _endPoint)).Returns(mockConnection.Object); var mockExceptionHandler = new Mock <IConnectionExceptionHandler>(); var connectionPoolSettings = new ConnectionPoolSettings(); var connectionPool = new ExclusiveConnectionPool(serverId, _endPoint, connectionPoolSettings, connectionFactory.Object, new EventAggregator(), mockExceptionHandler.Object); var mockConnectionPoolFactory = new Mock <IConnectionPoolFactory>(); mockConnectionPoolFactory .Setup(f => f.CreateConnectionPool(It.IsAny <ServerId>(), _endPoint, It.IsAny <IConnectionExceptionHandler>())) .Returns(connectionPool); var mockMonitorServerDescription = new ServerDescription(serverId, _endPoint); var mockServerMonitor = new Mock <IServerMonitor>(); mockServerMonitor.SetupGet(m => m.Description).Returns(mockMonitorServerDescription); mockServerMonitor.SetupGet(m => m.Lock).Returns(new object()); var mockServerMonitorFactory = new Mock <IServerMonitorFactory>(); mockServerMonitorFactory.Setup(f => f.Create(It.IsAny <ServerId>(), _endPoint)).Returns(mockServerMonitor.Object); var subject = new DefaultServer(_clusterId, _clusterClock, _clusterConnectionMode, _connectionModeSwitch, _directConnection, _settings, _endPoint, mockConnectionPoolFactory.Object, mockServerMonitorFactory.Object, _capturedEvents, _serverApi); connectionPool._connectionExceptionHandler(subject); subject.Initialize(); connectionPool.SetReady(); IChannelHandle channel = null; Exception exception; if (async) { exception = Record.Exception(() => channel = subject.GetChannelAsync(CancellationToken.None).GetAwaiter().GetResult()); } else { exception = Record.Exception(() => channel = subject.GetChannel(CancellationToken.None)); } channel.Should().BeNull(); exception.Should().Be(openConnectionException); subject.Description.Type.Should().Be(ServerType.Unknown); subject.Description.ReasonChanged.Should().Contain("ChannelException during handshake"); }
public void IsRetryableReadException_should_return_expected_result_using_exception_type(Type exceptionType, bool expectedResult) { var exception = CoreExceptionHelper.CreateException(exceptionType); var result = RetryabilityHelper.IsRetryableReadException(exception); result.Should().Be(expectedResult); }
public void IsResumableChangeStreamException_should_return_expected_result_for_servers_with_new_behavior_and_errors(Type exceptionType, bool isResumable) { var exception = (MongoException)CoreExceptionHelper.CreateException(exceptionType); var result = RetryabilityHelper.IsResumableChangeStreamException(exception, Feature.ServerReturnsResumableChangeStreamErrorLabel.FirstSupportedVersion); result.Should().Be(isResumable); }
public void AddRetryableWriteErrorLabelIfRequired_should_add_RetryableWriteError_for_network_errors() { var exception = (MongoException)CoreExceptionHelper.CreateException(typeof(MongoConnectionException)); RetryabilityHelper.AddRetryableWriteErrorLabelIfRequired(exception); var hasRetryableWriteErrorLabel = exception.HasErrorLabel("RetryableWriteError"); hasRetryableWriteErrorLabel.Should().BeTrue(); }
public void MoveNext_should_call_Resume_after_resumable_exception( [Values( typeof(MongoConnectionException), // network error typeof(MongoNotPrimaryException), typeof(MongoNodeIsRecoveringException))] Type resumableExceptionType, [Values(false, true)] bool expectedResult, [Values(false, true)] bool async) { var mockCursor = CreateMockCursor(); var mockBinding = new Mock <IReadBinding>(); var mockOperation = new Mock <IChangeStreamOperation <BsonDocument> >(); var subject = CreateSubject(cursor: mockCursor.Object, binding: mockBinding.Object, changeStreamOperation: mockOperation.Object); var cancellationToken = new CancellationTokenSource().Token; var resumableException = CoreExceptionHelper.CreateException(resumableExceptionType); var mockResumedCursor = CreateMockCursor(); // process the first batch so that we have a resume token var resumeToken = BsonDocument.Parse("{ resumeToken : 1 }"); var firstDocument = BsonDocument.Parse("{ _id : { resumeToken : 1 }, operationType : \"insert\", ns : { db : \"db\", coll : \"coll\" }, documentKey : { _id : 1 }, fullDocument : { _id : 1 } }"); var firstBatch = new[] { ToRawDocument(firstDocument) }; mockCursor.Setup(c => c.MoveNext(cancellationToken)).Returns(true); mockCursor.SetupGet(c => c.Current).Returns(firstBatch); subject.MoveNext(cancellationToken); bool result; if (async) { mockCursor.Setup(c => c.MoveNextAsync(cancellationToken)).Returns(CreateFaultedTask <bool>(resumableException)); mockOperation.Setup(o => o.ResumeAsync(mockBinding.Object, cancellationToken)).Returns(Task.FromResult(mockResumedCursor.Object)); mockResumedCursor.Setup(c => c.MoveNextAsync(cancellationToken)).Returns(Task.FromResult(expectedResult)); result = subject.MoveNextAsync(cancellationToken).GetAwaiter().GetResult(); mockCursor.Verify(c => c.MoveNextAsync(cancellationToken), Times.Once); mockOperation.Verify(o => o.ResumeAsync(mockBinding.Object, cancellationToken), Times.Once); mockResumedCursor.Verify(c => c.MoveNextAsync(cancellationToken), Times.Once); } else { mockCursor.Setup(c => c.MoveNext(cancellationToken)).Throws(resumableException); mockOperation.Setup(o => o.Resume(mockBinding.Object, cancellationToken)).Returns(mockResumedCursor.Object); mockResumedCursor.Setup(c => c.MoveNext(cancellationToken)).Returns(expectedResult); result = subject.MoveNext(cancellationToken); mockCursor.Verify(c => c.MoveNext(cancellationToken), Times.Exactly(2)); mockOperation.Verify(o => o.Resume(mockBinding.Object, cancellationToken), Times.Once); mockResumedCursor.Verify(c => c.MoveNext(cancellationToken), Times.Once); } result.Should().Be(expectedResult); }
public void AddRetryableWriteErrorLabelIfRequired_should_add_RetryableWriteError_for_network_errors([Values(false, true)] bool serverReturnsRetryableWriteErrorLabel) { var exception = (MongoException)CoreExceptionHelper.CreateException(typeof(MongoConnectionException)); var feature = Feature.ServerReturnsRetryableWriteErrorLabel; var serverVersion = serverReturnsRetryableWriteErrorLabel ? feature.FirstSupportedVersion : feature.LastNotSupportedVersion; RetryabilityHelper.AddRetryableWriteErrorLabelIfRequired(exception, serverVersion); var hasRetryableWriteErrorLabel = exception.HasErrorLabel("RetryableWriteError"); hasRetryableWriteErrorLabel.Should().BeTrue(); }
public void GetChannel_should_not_update_topology_and_clear_connection_pool_on_MongoConnectionException( [Values("TimedOutSocketException", "NetworkUnreachableSocketException")] string errorType, [Values(false, true)] bool async) { var serverId = new ServerId(_clusterId, _endPoint); var connectionId = new ConnectionId(serverId); var innerMostException = CoreExceptionHelper.CreateException(errorType); var mockConnectionExceptionHandler = new Mock <IConnectionExceptionHandler>(); var openConnectionException = new MongoConnectionException(connectionId, "Oops", new IOException("Cry", innerMostException)); var mockConnection = new Mock <IConnectionHandle>(); mockConnection.Setup(c => c.Open(It.IsAny <CancellationToken>())).Throws(openConnectionException); mockConnection.Setup(c => c.OpenAsync(It.IsAny <CancellationToken>())).ThrowsAsync(openConnectionException); var connectionFactory = new Mock <IConnectionFactory>(); connectionFactory.Setup(cf => cf.CreateConnection(serverId, _endPoint)).Returns(mockConnection.Object); var connectionPoolSettings = new ConnectionPoolSettings(); var connectionPool = new ExclusiveConnectionPool(serverId, _endPoint, connectionPoolSettings, connectionFactory.Object, new EventAggregator(), mockConnectionExceptionHandler.Object); var mockConnectionPoolFactory = new Mock <IConnectionPoolFactory>(); mockConnectionPoolFactory .Setup(f => f.CreateConnectionPool(It.IsAny <ServerId>(), _endPoint, It.IsAny <IConnectionExceptionHandler>())) .Returns(connectionPool); var subject = new LoadBalancedServer(_clusterId, _clusterClock, _settings, _endPoint, mockConnectionPoolFactory.Object, _capturedEvents, _serverApi); subject.Initialize(); IChannelHandle channel = null; Exception exception; if (async) { exception = Record.Exception(() => channel = subject.GetChannelAsync(CancellationToken.None).GetAwaiter().GetResult()); } else { exception = Record.Exception(() => channel = subject.GetChannel(CancellationToken.None)); } channel.Should().BeNull(); exception.Should().Be(openConnectionException); subject.Description.Type.Should().Be(ServerType.LoadBalanced); subject.Description.ReasonChanged.Should().Be("Initialized"); subject.Description.State.Should().Be(ServerState.Connected); _mockConnectionPool.Verify(c => c.Clear(It.IsAny <bool>()), Times.Never); }
public void IsResumableChangeStreamException_should_return_expected_result_for_servers_with_old_behavior(object exceptionDescription, bool isResumable) { MongoException exception; if (exceptionDescription is Type exceptionType) { exception = (MongoException)CoreExceptionHelper.CreateException(exceptionType); } else { exception = CoreExceptionHelper.CreateMongoCommandException((int)exceptionDescription); } var result = RetryabilityHelper.IsResumableChangeStreamException(exception, Feature.ServerReturnsResumableChangeStreamErrorLabel.LastNotSupportedVersion); result.Should().Be(isResumable); }
public void AddRetryableWriteErrorLabelIfRequired_should_add_RetryableWriteError_when_required(object exceptionDescription, bool shouldAddErrorLabel) { MongoException exception; if (exceptionDescription is Type exceptionType) { exception = (MongoException)CoreExceptionHelper.CreateException(exceptionType); } else { exception = CoreExceptionHelper.CreateMongoCommandException((int)exceptionDescription); } RetryabilityHelper.AddRetryableWriteErrorLabelIfRequired(exception, Feature.ServerReturnsRetryableWriteErrorLabel.LastNotSupportedVersion); var hasRetryableWriteErrorLabel = exception.HasErrorLabel("RetryableWriteError"); hasRetryableWriteErrorLabel.Should().Be(shouldAddErrorLabel); }
public void AddRetryableWriteErrorLabelIfRequired_should_not_add_error_label_for_non_retryWrites_server( [Values(false, true)] bool isNetworkError) { MongoException exception = null; if (isNetworkError) { exception = (MongoException)CoreExceptionHelper.CreateException(typeof(MongoConnectionException)); } else { exception = CoreExceptionHelper.CreateMongoCommandException((int)ServerErrorCode.HostNotFound); } RetryabilityHelper.AddRetryableWriteErrorLabelIfRequired(exception, Feature.RetryableWrites.LastNotSupportedVersion); var hasRetryableWriteErrorLabel = exception.HasErrorLabel("RetryableWriteError"); hasRetryableWriteErrorLabel.Should().BeFalse(); }
public void Heartbeat_should_make_immediate_next_attempt_for_streaming_protocol(string exceptionType, bool?moreToCome) { var capturedEvents = new EventCapturer() .Capture <ServerHeartbeatSucceededEvent>() .Capture <ServerHeartbeatFailedEvent>() .Capture <ServerDescriptionChangedEvent>(); var subject = CreateSubject(out var mockConnection, out _, out var mockRoundTimeTripMonitor, capturedEvents); subject.DescriptionChanged += (o, e) => { capturedEvents.TryGetEventHandler <ServerDescriptionChangedEvent>(out var eventHandler); eventHandler(new ServerDescriptionChangedEvent(e.OldServerDescription, e.NewServerDescription)); }; SetupHeartbeatConnection(mockConnection, isStreamable: true, autoFillStreamingResponses: false); Exception exception = null; switch (exceptionType) { case null: mockConnection.EnqueueCommandResponseMessage(CreateStreamableCommandResponseMessage(moreToCome.Value), null); break; case "MongoConnectionException": // previousDescription type is "Known" for this case mockConnection.EnqueueCommandResponseMessage( exception = CoreExceptionHelper.CreateException(exceptionType)); break; } // 10 seconds delay. Not expected to be processed mockConnection.EnqueueCommandResponseMessage(CreateStreamableCommandResponseMessage(), TimeSpan.FromSeconds(10)); subject.Initialize(); var expectedServerDescriptionChangedEventCount = exception != null ? 3 // +1 event because a connection initialized event doesn't have waiting : 2; capturedEvents.WaitForOrThrowIfTimeout( events => events.Count(e => e is ServerDescriptionChangedEvent) >= expectedServerDescriptionChangedEventCount, // the connection has been initialized and the first heatbeat event has been fired TimeSpan.FromSeconds(10)); capturedEvents.Next().Should().BeOfType <ServerDescriptionChangedEvent>(); // connection initialized AssertHeartbeatAttempt(); capturedEvents.Any().Should().BeFalse(); // the next attempt will be in 10 seconds because the second stremable respone has 10 seconds delay void AssertHeartbeatAttempt() { if (exception != null) { mockRoundTimeTripMonitor.Verify(c => c.Reset(), Times.Once); var serverHeartbeatFailedEvent = capturedEvents.Next().Should().BeOfType <ServerHeartbeatFailedEvent>().Subject; // updating the server based on the heartbeat serverHeartbeatFailedEvent.Exception.Should().Be(exception); var serverDescriptionChangedEvent = capturedEvents.Next().Should().BeOfType <ServerDescriptionChangedEvent>().Subject; serverDescriptionChangedEvent.NewDescription.HeartbeatException.Should().Be(exception); serverDescriptionChangedEvent = capturedEvents.Next().Should().BeOfType <ServerDescriptionChangedEvent>().Subject; // when we catch exceptions, we close the current connection, so opening connection will trigger one more ServerDescriptionChangedEvent serverDescriptionChangedEvent.OldDescription.HeartbeatException.Should().Be(exception); serverDescriptionChangedEvent.NewDescription.HeartbeatException.Should().BeNull(); } else { mockRoundTimeTripMonitor.Verify(c => c.Reset(), Times.Never); capturedEvents.Next().Should().BeOfType <ServerHeartbeatSucceededEvent>(); var serverDescriptionChangedEvent = capturedEvents.Next().Should().BeOfType <ServerDescriptionChangedEvent>().Subject; // updating the server based on the heartbeat serverDescriptionChangedEvent.NewDescription.HeartbeatException.Should().BeNull(); } } }
private void ApplyApplicationError(BsonDocument applicationError) { var expectedKeys = new[] { "address", "generation", // optional "maxWireVersion", "when", "type", "response" // optional }; JsonDrivenHelper.EnsureAllFieldsAreValid(applicationError, expectedKeys); var address = applicationError["address"].AsString; var endPoint = EndPointHelper.Parse(address); var server = (Server)_serverFactory.GetServer(endPoint); var connectionId = new ConnectionId(server.ServerId); var type = applicationError["type"].AsString; var maxWireVersion = applicationError["maxWireVersion"].AsInt32; Exception simulatedException = null; switch (type) { case "command": var response = applicationError["response"].AsBsonDocument; var command = new BsonDocument("Link", "start!"); simulatedException = ExceptionMapper.MapNotPrimaryOrNodeIsRecovering(connectionId, command, response, "errmsg"); Ensure.IsNotNull(simulatedException, nameof(simulatedException)); break; case "network": { var innerException = CoreExceptionHelper.CreateException("IOExceptionWithNetworkUnreachableSocketException"); simulatedException = new MongoConnectionException(connectionId, "Ignorance, yet knowledge.", innerException); break; } case "timeout": { var innerException = CoreExceptionHelper.CreateException("IOExceptionWithTimedOutSocketException"); simulatedException = new MongoConnectionException(connectionId, "Chaos, yet harmony.", innerException); break; } default: throw new ArgumentException($"Unsupported value of {type} for type"); } var mockConnection = new Mock <IConnectionHandle>(); var isMasterResult = new IsMasterResult(new BsonDocument { { "compressors", new BsonArray() } }); var serverVersion = WireVersionHelper.MapWireVersionToServerVersion(maxWireVersion); var buildInfoResult = new BuildInfoResult(new BsonDocument { { "version", serverVersion } }); mockConnection.SetupGet(c => c.Description) .Returns(new ConnectionDescription(connectionId, isMasterResult, buildInfoResult)); var generation = applicationError.Contains("generation") ? applicationError["generation"].AsInt32 : 0; mockConnection.SetupGet(c => c.Generation).Returns(generation); var when = applicationError["when"].AsString; switch (when) { case "beforeHandshakeCompletes": server.HandleBeforeHandshakeCompletesException(mockConnection.Object, simulatedException); break; case "afterHandshakeCompletes": server.HandleChannelException(mockConnection.Object, simulatedException); break; default: throw new ArgumentException($"Unsupported value of {when} for when."); } }