public void DerrivedExceptionsHaveTheCorrectTransientValues(Exception exception,
                                                                    Type expectedMappedType)
        {
            var eventHubsException             = (TrackOne.EventHubsException)exception;
            EventHubsException mappedException = eventHubsException.MapToTrackTwoException();

            Assert.That(mappedException, Is.Not.Null, "The mapping should produce an exception.");
            Assert.That(mappedException.GetType(), Is.EqualTo(expectedMappedType), "The mapped exception type was incorrect.");
            Assert.That(mappedException.IsTransient, Is.EqualTo(eventHubsException.IsTransient), "The mapped exception should agree on being transient.");
            Assert.That(mappedException.ResourceName, Is.EqualTo(eventHubsException.EventHubsNamespace), "The mapped exception should use the namespace as its resource name.");
            Assert.That(mappedException.Message, Does.Contain(eventHubsException.EventHubsNamespace ?? string.Empty), "The mapped exception should include the namespace in its message.");
            Assert.That(mappedException.Message, Does.Contain(eventHubsException.RawMessage), "The mapped exception should include the message text in its message.");
            Assert.That(mappedException.InnerException, Is.EqualTo(eventHubsException), "The mapped exception should wrap the original instance.");
        }
        public async Task OpenConsumerLinkSurfacesAStolenPartition()
        {
            var eventHub          = "fake-hub";
            var terminalException = new EventHubsException(eventHub, "Expected", EventHubsException.FailureReason.ConsumerDisconnected);
            var capturedException = default(Exception);
            var mockConsumer      = new MockAmqpConsumer(eventHub, true, terminalException);

            SetActivePartitionStolenException(mockConsumer, terminalException);

            try
            {
                await mockConsumer.InvokeCreateConsumerLinkAsync("cg", "0", EventPosition.Earliest, 300, null, 34, true, TimeSpan.FromSeconds(30), CancellationToken.None);
            }
            catch (Exception ex)
            {
                capturedException = ex;
            }

            Assert.That(capturedException, Is.Not.Null, "An exception should have been surfaced.");
            Assert.That(capturedException.GetType(), Is.EqualTo(terminalException.GetType()), "The captured exception was not of the expected type.");
            Assert.That(capturedException, Is.SameAs(terminalException), "The mocked terminal exception should have been surfaced.");
        }
        public void SendBatchRespectsTheRetryPolicy(RetryOptions retryOptions)
        {
            var partitionKey = "testMe";
            var options      = new CreateBatchOptions {
                PartitionKey = partitionKey
            };
            var retriableException = new EventHubsException(true, "Test");
            var retryPolicy        = new BasicRetryPolicy(retryOptions);
            var batch = new EventDataBatch(Mock.Of <TransportEventBatch>(), options);

            var producer = new Mock <AmqpProducer>("aHub", null, Mock.Of <AmqpConnectionScope>(), new AmqpMessageConverter(), retryPolicy)
            {
                CallBase = true
            };

            producer
            .Protected()
            .Setup <Task <SendingAmqpLink> >("CreateLinkAndEnsureProducerStateAsync",
                                             ItExpr.IsAny <string>(),
                                             ItExpr.IsAny <TimeSpan>(),
                                             ItExpr.IsAny <CancellationToken>())
            .Throws(retriableException);

            using CancellationTokenSource cancellationSource = new CancellationTokenSource();
            Assert.That(async() => await producer.Object.SendAsync(batch, cancellationSource.Token), Throws.InstanceOf(retriableException.GetType()));

            producer
            .Protected()
            .Verify("CreateLinkAndEnsureProducerStateAsync", Times.Exactly(1 + retryOptions.MaximumRetries),
                    ItExpr.Is <string>(value => value == null),
                    ItExpr.IsAny <TimeSpan>(),
                    ItExpr.IsAny <CancellationToken>());
        }
        public void SendEnumerableRespectsTheRetryPolicy(RetryOptions retryOptions)
        {
            var partitionId        = "testMe";
            var retriableException = new EventHubsException(true, "Test");
            var retryPolicy        = new BasicRetryPolicy(retryOptions);

            var producer = new Mock <AmqpProducer>("aHub", partitionId, Mock.Of <AmqpConnectionScope>(), new AmqpMessageConverter(), retryPolicy)
            {
                CallBase = true
            };

            producer
            .Protected()
            .Setup <Task <SendingAmqpLink> >("CreateLinkAndEnsureProducerStateAsync",
                                             ItExpr.IsAny <string>(),
                                             ItExpr.IsAny <TimeSpan>(),
                                             ItExpr.IsAny <CancellationToken>())
            .Throws(retriableException);

            using CancellationTokenSource cancellationSource = new CancellationTokenSource();
            Assert.That(async() => await producer.Object.SendAsync(new[] { new EventData(new byte[] { 0x65 }) }, new SendOptions(), cancellationSource.Token), Throws.InstanceOf(retriableException.GetType()));

            producer
            .Protected()
            .Verify("CreateLinkAndEnsureProducerStateAsync", Times.Exactly(1 + retryOptions.MaximumRetries),
                    ItExpr.Is <string>(value => value == partitionId),
                    ItExpr.IsAny <TimeSpan>(),
                    ItExpr.IsAny <CancellationToken>());
        }
        public void RetryIsInvokedForRetriableTrackOneEventHubsException()
        {
            var retryCount    = 99;
            var lastException = new TrackOne.EventHubsTimeoutException("RETRY!");
            EventHubsException mappedException = lastException.MapToTrackTwoException();
            var expectedInterval = TimeSpan.FromMinutes(65);
            var mockRetryPolicy  = new Mock <EventHubRetryPolicy>();
            var retryPolicy      = new TrackOneRetryPolicy(mockRetryPolicy.Object);

            mockRetryPolicy
            .Setup(policy => policy.CalculateRetryDelay(It.Is <EventHubsException>(value => value.GetType() == mappedException.GetType()), It.Is <int>(value => value == retryCount)))
            .Returns(expectedInterval);

            Assert.That(TrackOne.RetryPolicy.IsRetryableException(lastException), Is.True, "The timeout exception should be considered as retriable by the TrackOne.RetryPolicy.");
            Assert.That(retryPolicy.GetNextRetryInterval(lastException, TimeSpan.FromHours(4), retryCount), Is.EqualTo(expectedInterval));
        }
Exemple #6
0
        public void ReceiveAsyncRespectsTheRetryPolicy(RetryOptions retryOptions)
        {
            var eventHub      = "eventHubName";
            var consumerGroup = "$DEFAULT";
            var partition     = "3";
            var eventPosition = EventPosition.FromOffset(123);
            var options       = new EventHubConsumerOptions {
                Identifier = "OMG!"
            };
            var tokenValue         = "123ABC";
            var retryPolicy        = new BasicRetryPolicy(retryOptions);
            var retriableException = new EventHubsException(true, "Test");
            var mockConverter      = new Mock <AmqpMessageConverter>();
            var mockCredential     = new Mock <TokenCredential>();
            var mockScope          = new Mock <AmqpConnectionScope>();

            using var cancellationSource = new CancellationTokenSource();

            mockCredential
            .Setup(credential => credential.GetTokenAsync(It.IsAny <TokenRequestContext>(), It.Is <CancellationToken>(value => value == cancellationSource.Token)))
            .Returns(Task.FromResult(new AccessToken(tokenValue, DateTimeOffset.MaxValue)));

            mockScope
            .Setup(scope => scope.OpenConsumerLinkAsync(
                       It.IsAny <string>(),
                       It.IsAny <string>(),
                       It.IsAny <EventPosition>(),
                       It.IsAny <EventHubConsumerOptions>(),
                       It.IsAny <TimeSpan>(),
                       It.IsAny <CancellationToken>()))
            .Throws(retriableException);

            var consumer = new AmqpEventHubConsumer(eventHub, consumerGroup, partition, eventPosition, options, mockScope.Object, Mock.Of <AmqpMessageConverter>(), retryPolicy, null);

            Assert.That(async() => await consumer.ReceiveAsync(100, null, cancellationSource.Token), Throws.InstanceOf(retriableException.GetType()));

            mockScope
            .Verify(scope => scope.OpenConsumerLinkAsync(
                        It.Is <string>(value => value == consumerGroup),
                        It.Is <string>(value => value == partition),
                        It.Is <EventPosition>(value => value == eventPosition),
                        It.Is <EventHubConsumerOptions>(value => value == options),
                        It.IsAny <TimeSpan>(),
                        It.IsAny <CancellationToken>()),
                    Times.Exactly(1 + retryOptions.MaximumRetries));
        }
 public void DerrivedExceptionsHaveTheCorrectTransientValues(EventHubsException exception,
                                                             bool expectedTransient)
 {
     Assert.That(exception.IsTransient, Is.EqualTo(expectedTransient), $"The { exception.GetType().Name } has an incorrect IsTransient value.");
 }
        public void GetPartitionPropertiesAsyncAppliesTheRetryPolicy(EventHubsRetryOptions retryOptions)
        {
            var eventHubName       = "myName";
            var partitionId        = "Barney";
            var tokenValue         = "123ABC";
            var retryPolicy        = new BasicRetryPolicy(retryOptions);
            var retriableException = new EventHubsException(true, "Test");
            var mockConverter      = new Mock <AmqpMessageConverter>();
            var mockCredential     = new Mock <EventHubTokenCredential>(Mock.Of <TokenCredential>());
            var mockScope          = new Mock <AmqpConnectionScope>();

            using var cancellationSource = new CancellationTokenSource();

            mockCredential
            .Setup(credential => credential.GetTokenAsync(It.IsAny <TokenRequestContext>(), It.Is <CancellationToken>(value => value == cancellationSource.Token)))
            .Returns(new ValueTask <AccessToken>(new AccessToken(tokenValue, DateTimeOffset.MaxValue)));

            mockConverter
            .Setup(converter => converter.CreatePartitionPropertiesRequest(It.Is <string>(value => value == eventHubName), It.Is <string>(value => value == partitionId), It.Is <string>(value => value == tokenValue)))
            .Returns(default(AmqpMessage));

            mockScope
            .Setup(scope => scope.OpenManagementLinkAsync(It.IsAny <TimeSpan>(), It.IsAny <CancellationToken>()))
            .Throws(retriableException);

            var client = new InjectableMockClient("my.eventhub.com", eventHubName, mockCredential.Object, new EventHubConnectionOptions(), mockScope.Object, mockConverter.Object);

            Assert.That(async() => await client.GetPartitionPropertiesAsync(partitionId, retryPolicy, cancellationSource.Token), Throws.InstanceOf(retriableException.GetType()));

            mockScope.Verify(scope => scope.OpenManagementLinkAsync(It.IsAny <TimeSpan>(), It.IsAny <CancellationToken>()), Times.Exactly(1 + retryOptions.MaximumRetries));
        }
        public void OpenConsumerLinkPreservesTheStolenExceptionWhenInvalidateIsSet()
        {
            var eventHub          = "fake-hub";
            var terminalException = new EventHubsException(eventHub, "Expected", EventHubsException.FailureReason.ConsumerDisconnected);
            var mockConsumer      = new MockAmqpConsumer(eventHub, true, terminalException);

            SetActivePartitionStolenException(mockConsumer, terminalException);
            Assert.That(async() => await mockConsumer.InvokeCreateConsumerLinkAsync("cg", "0", EventPosition.Earliest, 300, null, 34, true, TimeSpan.FromSeconds(30), CancellationToken.None), Throws.InstanceOf(terminalException.GetType()), "The exception should have been surfaced on the first call.");
            Assert.That(async() => await mockConsumer.InvokeCreateConsumerLinkAsync("cg", "0", EventPosition.Earliest, 300, null, 34, true, TimeSpan.FromSeconds(30), CancellationToken.None), Throws.InstanceOf(terminalException.GetType()), "The exception should have been surfaced on the second call.");
            Assert.That(async() => await mockConsumer.InvokeCreateConsumerLinkAsync("cg", "0", EventPosition.Earliest, 300, null, 34, true, TimeSpan.FromSeconds(30), CancellationToken.None), Throws.InstanceOf(terminalException.GetType()), "The exception should have been surfaced on the third call.");

            var capturedException = GetActivePartitionStolenException(mockConsumer);

            Assert.That(capturedException, Is.SameAs(terminalException), "The active exception should have been preserved after the calls were completed.");
        }
        public void ReceiveAsyncAppliesTheRetryPolicy(EventHubsRetryOptions retryOptions)
        {
            var eventHub           = "eventHubName";
            var consumerGroup      = "$DEFAULT";
            var partition          = "3";
            var eventPosition      = EventPosition.FromOffset(123);
            var trackLastEnqueued  = false;
            var invalidateOnSteal  = true;
            var ownerLevel         = 123L;
            var tokenValue         = "123ABC";
            var retryPolicy        = new BasicRetryPolicy(retryOptions);
            var retriableException = new EventHubsException(true, "Test");
            var mockConverter      = new Mock <AmqpMessageConverter>();
            var mockCredential     = new Mock <TokenCredential>();
            var mockScope          = new Mock <AmqpConnectionScope>();

            using var cancellationSource = new CancellationTokenSource();

            mockCredential
            .Setup(credential => credential.GetTokenAsync(It.IsAny <TokenRequestContext>(), It.Is <CancellationToken>(value => value == cancellationSource.Token)))
            .Returns(new ValueTask <AccessToken>(new AccessToken(tokenValue, DateTimeOffset.MaxValue)));

            mockScope
            .Setup(scope => scope.OpenConsumerLinkAsync(
                       It.IsAny <string>(),
                       It.IsAny <string>(),
                       It.IsAny <EventPosition>(),
                       It.IsAny <TimeSpan>(),
                       It.IsAny <uint>(),
                       It.IsAny <long?>(),
                       It.IsAny <long?>(),
                       It.IsAny <bool>(),
                       It.IsAny <CancellationToken>()))
            .Throws(retriableException);

            var consumer = new AmqpConsumer(eventHub, consumerGroup, partition, eventPosition, trackLastEnqueued, invalidateOnSteal, ownerLevel, null, null, mockScope.Object, Mock.Of <AmqpMessageConverter>(), retryPolicy);

            Assert.That(async() => await consumer.ReceiveAsync(100, null, cancellationSource.Token), Throws.InstanceOf(retriableException.GetType()));

            mockScope
            .Verify(scope => scope.OpenConsumerLinkAsync(
                        It.Is <string>(value => value == consumerGroup),
                        It.Is <string>(value => value == partition),
                        It.Is <EventPosition>(value => value == eventPosition),
                        It.IsAny <TimeSpan>(),
                        It.IsAny <uint>(),
                        It.IsAny <long?>(),
                        It.Is <long?>(value => value == ownerLevel),
                        It.Is <bool>(value => value == trackLastEnqueued),
                        It.IsAny <CancellationToken>()),
                    Times.Exactly(1 + retryOptions.MaximumRetries));
        }