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>();
        }
Exemplo n.º 2
0
        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");
        }
Exemplo n.º 3
0
        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);
        }
Exemplo n.º 4
0
        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 IsResumableChangeStreamException_should_return_expected_result_using_error_label(string label, bool expectedResult)
        {
            var exception = CoreExceptionHelper.CreateMongoCommandException(label: label);

            var result = RetryabilityHelper.IsResumableChangeStreamException(exception);

            result.Should().Be(expectedResult);
        }
Exemplo n.º 6
0
        public void IsRetryableReadException_should_return_expected_result_using_code(int code, bool expectedResult)
        {
            var exception = CoreExceptionHelper.CreateMongoCommandException(code);

            var result = RetryabilityHelper.IsRetryableReadException(exception);

            result.Should().Be(expectedResult);
        }
Exemplo n.º 7
0
        public void AddRetryableWriteErrorLabelIfRequired_should_add_RetryableWriteError_for_MongoWriteConcernException_when_required(int errorCode, bool shouldAddErrorLabel)
        {
            var exception = CoreExceptionHelper.CreateMongoWriteConcernException(BsonDocument.Parse($"{{ writeConcernError : {{ code : {errorCode} }} }}"));

            RetryabilityHelper.AddRetryableWriteErrorLabelIfRequired(exception, Feature.ServerReturnsRetryableWriteErrorLabel.LastNotSupportedVersion);

            var hasRetryableWriteErrorLabel = exception.HasErrorLabel("RetryableWriteError");

            hasRetryableWriteErrorLabel.Should().Be(shouldAddErrorLabel);
        }
        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();
        }
Exemplo n.º 9
0
        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);
        }
Exemplo n.º 10
0
        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);
        }
Exemplo n.º 12
0
        public void IsResumableChangeStreamException_should_return_expected_result_for_servers_with_new_behavior([Values(false, true)] bool hasResumableChangeStreamErrorLabel)
        {
            var exception = CoreExceptionHelper.CreateMongoCommandException(-1);

            if (hasResumableChangeStreamErrorLabel)
            {
                exception.AddErrorLabel("ResumableChangeStreamError");
            }

            var result = RetryabilityHelper.IsResumableChangeStreamException(exception, Feature.ServerReturnsResumableChangeStreamErrorLabel.FirstSupportedVersion);

            result.Should().Be(hasResumableChangeStreamErrorLabel);
        }
Exemplo n.º 13
0
        public void IsRetryableWriteException_should_return_expected_result([Values(false, true)] bool hasRetryableWriteLabel)
        {
            var exception = CoreExceptionHelper.CreateMongoCommandException(-1);

            if (hasRetryableWriteLabel)
            {
                exception.AddErrorLabel("RetryableWriteError");
            }

            var result = RetryabilityHelper.IsRetryableWriteException(exception);

            result.Should().Be(hasRetryableWriteLabel);
        }
Exemplo n.º 14
0
        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);
        }
Exemplo n.º 15
0
        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);
        }
Exemplo n.º 16
0
        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();
        }
Exemplo n.º 17
0
        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();
                }
            }
        }
Exemplo n.º 18
0
        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.");
            }
        }
Exemplo n.º 19
0
        public void GetChannel_should_update_topology_and_clear_connection_pool_on_network_error_or_timeout(
            [Values("timedout", "networkunreachable")] string errorType,
            [Values(false, true)] bool async)
        {
            var serverId           = new ServerId(_clusterId, _endPoint);
            var connectionId       = new ConnectionId(serverId);
            var innerMostException = CoreExceptionHelper.CreateSocketException(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 mockConnectionPool = new Mock <IConnectionPool>();

            mockConnectionPool.Setup(p => p.AcquireConnection(It.IsAny <CancellationToken>())).Returns(mockConnection.Object);
            mockConnectionPool.Setup(p => p.AcquireConnectionAsync(It.IsAny <CancellationToken>())).ReturnsAsync(mockConnection.Object);
            var mockConnectionPoolFactory = new Mock <IConnectionPoolFactory>();

            mockConnectionPoolFactory
            .Setup(f => f.CreateConnectionPool(It.IsAny <ServerId>(), _endPoint))
            .Returns(mockConnectionPool.Object);
            var mockMonitorServerDescription = new ServerDescription(serverId, _endPoint);
            var mockServerMonitor            = new Mock <IServerMonitor>();

            mockServerMonitor.SetupGet(m => m.Description).Returns(mockMonitorServerDescription);
            mockServerMonitor
            .Setup(m => m.Invalidate(It.IsAny <string>()))
            .Callback((string reason) => MockMonitorInvalidate(reason));
            var mockServerMonitorFactory = new Mock <IServerMonitorFactory>();

            mockServerMonitorFactory.Setup(f => f.Create(It.IsAny <ServerId>(), _endPoint)).Returns(mockServerMonitor.Object);

            var subject = new Server(_clusterId, _clusterClock, _clusterConnectionMode, _settings, _endPoint, mockConnectionPoolFactory.Object, mockServerMonitorFactory.Object, _capturedEvents);

            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.Unknown);
            subject.Description.ReasonChanged.Should().Contain("ChannelException during handshake");
            mockServerMonitor.Verify(m => m.Invalidate(It.IsAny <string>()), Times.Once);
            mockConnectionPool.Verify(p => p.Clear(), Times.Once);

            void MockMonitorInvalidate(string reason)
            {
                var currentDescription = mockServerMonitor.Object.Description;

                mockServerMonitor.SetupGet(m => m.Description).Returns(currentDescription.With(reason));
            }
        }