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)); }
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)); }