public async Task SetsExecutionTimeOnCommandWhenInvokedWithoutBreakerAndCommandFails()
            {
                // Arrange

                var key      = AnyString;
                var groupKey = GroupKey.Named(key);

                var mockMetricEvents   = new Mock <IMetricEvents>();
                var mockBreakerInvoker = new Mock <IBreakerInvoker>();

                var mockBulkhead = new Mock <ISemaphoreBulkhead>();

                mockBulkhead.Setup(m => m.TryEnter()).Returns(true);
                mockBulkhead.SetupGet(m => m.Name).Returns(key);

                var mockBulkheadFactory = new Mock <IBulkheadFactory>(MockBehavior.Strict);

                mockBulkheadFactory.Setup(m => m.GetBulkhead(groupKey)).Returns(mockBulkhead.Object);

                var mockConfig = new MjolnirConfiguration {
                    UseCircuitBreakers = false
                };

                // Pass false for useCircuitBreakers to bypass the breaker; we're testing that here.
                var invoker = new BulkheadInvoker(mockBreakerInvoker.Object, mockBulkheadFactory.Object, mockMetricEvents.Object, mockConfig);
                var command = new ConfigurableKeyThrowingAsyncCommand(key);

                // Act + Assert

                await Assert.ThrowsAsync <ExpectedTestException>(() => invoker.ExecuteWithBulkheadAsync(command, CancellationToken.None));

                Assert.True(command.ExecutionTimeMillis > 0);
            }
            public async Task DoesntSetExecutionTimeOnCommandWhenInvokedWithBreakerAndCommandSucceeds()
            {
                // If we execute on the breaker, the breaker should set the execution time instead
                // of the bulkhead invoker.

                // Arrange

                var key      = AnyString;
                var groupKey = GroupKey.Named(key);

                var mockMetricEvents   = new Mock <IMetricEvents>();
                var mockBreakerInvoker = new Mock <IBreakerInvoker>();

                var mockBulkhead = new Mock <ISemaphoreBulkhead>();

                mockBulkhead.Setup(m => m.TryEnter()).Returns(true);
                mockBulkhead.SetupGet(m => m.Name).Returns(key);

                var mockBulkheadFactory = new Mock <IBulkheadFactory>(MockBehavior.Strict);

                mockBulkheadFactory.Setup(m => m.GetBulkhead(groupKey)).Returns(mockBulkhead.Object);

                var command = new ConfigurableKeyThrowingAsyncCommand(key);

                mockBreakerInvoker.Setup(m => m.ExecuteWithBreakerAsync(command, It.IsAny <CancellationToken>()))
                .Returns(Task.FromResult(true));

                var mockConfig = new MjolnirConfiguration {
                    UseCircuitBreakers = true
                };

                // Pass true for useCircuitBreakers, we need to test that behavior here.
                var invoker = new BulkheadInvoker(mockBreakerInvoker.Object, mockBulkheadFactory.Object, mockMetricEvents.Object, mockConfig);

                // Act

                await invoker.ExecuteWithBulkheadAsync(command, CancellationToken.None);

                // Assert

                Assert.Equal(0, command.ExecutionTimeMillis);
            }
            public async Task FiresMetricEventWhenEnteringAndLeavingBulkheadAndCommandFails()
            {
                // Arrange

                var key      = AnyString;
                var groupKey = GroupKey.Named(key);

                var mockMetricEvents   = new Mock <IMetricEvents>();
                var mockBreakerInvoker = new Mock <IBreakerInvoker>();

                var mockBulkhead = new Mock <ISemaphoreBulkhead>();

                mockBulkhead.Setup(m => m.TryEnter()).Returns(true);
                mockBulkhead.SetupGet(m => m.Name).Returns(key);

                var mockBulkheadFactory = new Mock <IBulkheadFactory>(MockBehavior.Strict);

                mockBulkheadFactory.Setup(m => m.GetBulkhead(groupKey)).Returns(mockBulkhead.Object);

                var mockConfig = new MjolnirConfiguration {
                    UseCircuitBreakers = false
                };

                // The breaker invoker behavior doesn't matter here, we shouldn't get to the point
                // where we try to use it. Pass a "false" value for useCircuitBreakers to help
                // ensure that.
                var invoker = new BulkheadInvoker(mockBreakerInvoker.Object, mockBulkheadFactory.Object, mockMetricEvents.Object, mockConfig);
                var command = new ConfigurableKeyThrowingAsyncCommand(key);

                // Act + Assert

                await Assert.ThrowsAsync <ExpectedTestException>(() => invoker.ExecuteWithBulkheadAsync(command, CancellationToken.None));

                mockMetricEvents.Verify(m => m.EnterBulkhead(key, command.Name));
                mockMetricEvents.Verify(m => m.LeaveBulkhead(key, command.Name));
            }