public virtual void testSingleTestOnOpenCircuitAfterTimeWindow()
        {
            int sleepWindow = 200;
            var properties  = GetCommandConfig();

            properties.CircuitBreakerSleepWindowInMilliseconds = sleepWindow;
            ICommandMetrics metrics = GetMetrics(properties);
            var             cb      = new TestCircuitBreaker(properties, metrics);

            // fail
            metrics.MarkFailure();
            metrics.MarkFailure();
            metrics.MarkFailure();
            metrics.MarkFailure();

            // everything has failed in the test window so we should return false now
            Assert.IsFalse(cb.AllowRequest());
            Assert.IsTrue(cb.IsOpen());

            // wait for sleepWindow to pass
            Thread.Sleep(sleepWindow + 50);

            // we should now allow 1 request
            Assert.IsTrue(cb.AllowRequest());
            // but the circuit should still be open
            Assert.IsTrue(cb.IsOpen());
            // and further requests are still blocked
            Assert.IsFalse(cb.AllowRequest());
        }
        public virtual void TestTripCircuit()
        {
            var             properties = GetCommandConfig();
            ICommandMetrics metrics    = GetMetrics(properties);
            var             cb         = new TestCircuitBreaker(properties, metrics);

            metrics.MarkSuccess();
            metrics.MarkSuccess();
            metrics.MarkSuccess();


            // this should still allow requests as everything has been successful
            Thread.Sleep(properties.MetricsHealthSnapshotIntervalInMilliseconds);
            Assert.IsTrue(cb.AllowRequest());
            Assert.IsFalse(cb.IsOpen());

            // fail
            metrics.MarkFailure();
            metrics.MarkFailure();
            metrics.MarkFailure();;
            Thread.Sleep(properties.MetricsHealthSnapshotIntervalInMilliseconds);

            // everything has failed in the test window so we should return false now
            Assert.IsFalse(cb.AllowRequest());
            Assert.IsTrue(cb.IsOpen());;
        }
        public virtual void testCircuitClosedAfterSuccessAndClearsStatisticalWindow()
        {
            int statisticalWindow = 200;
            int sleepWindow       = 10; // this is set very low so that returning from a retry still ends up having data in the buckets for the statisticalWindow
            var properties        = GetCommandConfig();

            properties.CircuitBreakerSleepWindowInMilliseconds       = sleepWindow;
            properties.MetricsRollingStatisticalWindowInMilliseconds = statisticalWindow;
            ICommandMetrics metrics = GetMetrics(properties);
            var             cb      = new TestCircuitBreaker(properties, metrics);

            // fail
            metrics.MarkFailure();
            metrics.MarkFailure();
            metrics.MarkFailure();
            metrics.MarkFailure();

            // everything has failed in the test window so we should return false now
            Assert.IsFalse(cb.AllowRequest());
            Assert.IsTrue(cb.IsOpen());

            // wait for sleepWindow to pass
            Thread.Sleep(sleepWindow + 50);

            // we should now allow 1 request
            Assert.IsTrue(cb.AllowRequest());
            // but the circuit should still be open
            Assert.IsTrue(cb.IsOpen());
            // and further requests are still blocked
            Assert.IsFalse(cb.AllowRequest());

            // the 'singleTest' succeeds so should cause the circuit to be closed
            metrics.MarkSuccess();
            cb.MarkSuccess();

            // all requests should be open again
            Assert.IsTrue(cb.AllowRequest());
            Assert.IsTrue(cb.AllowRequest());
            Assert.IsTrue(cb.AllowRequest());
            // and the circuit should be closed again
            Assert.IsFalse(cb.IsOpen());
        }
        public void TestTripCircuitOnTimeouts()
        {
            var             properties = GetCommandConfig();
            ICommandMetrics metrics    = GetMetrics(properties);
            var             cb         = new TestCircuitBreaker(properties, metrics);

            // this should start as allowing requests
            Assert.IsTrue(cb.AllowRequest());
            Assert.IsFalse(cb.IsOpen());

            metrics.MarkTimeout();
            metrics.MarkTimeout();
            metrics.MarkTimeout();
            metrics.MarkTimeout();
            Thread.Sleep(properties.MetricsHealthSnapshotIntervalInMilliseconds);


            // this should remain open as the failure threshold is below the percentage limit
            Assert.IsFalse(cb.AllowRequest());
            Assert.IsTrue(cb.IsOpen());
        }
        public void TestCircuitDoesNotTripOnFailuresBelowThreshold()
        {
            var             properties = GetCommandConfig();
            ICommandMetrics metrics    = GetMetrics(properties);
            var             cb         = new TestCircuitBreaker(properties, metrics);

            // this should start as allowing requests
            Assert.IsTrue(cb.AllowRequest());
            Assert.IsFalse(cb.IsOpen());

            metrics.MarkSuccess();
            metrics.MarkSuccess();
            metrics.MarkSuccess();
            metrics.MarkSuccess();
            metrics.MarkFailure();
            metrics.MarkFailure();

            // this should remain open as the failure threshold is below the percentage limit
            Assert.IsTrue(cb.AllowRequest());
            Assert.IsFalse(cb.IsOpen());
        }
        public void TestTripCircuitOnFailuresAboveThreshold()
        {
            var             properties = GetCommandConfig();
            ICommandMetrics metrics    = GetMetrics(properties);
            var             cb         = new TestCircuitBreaker(properties, metrics);

            // this should start as allowing requests
            Assert.IsTrue(cb.AllowRequest());
            Assert.IsFalse(cb.IsOpen());

            // success with high latency
            metrics.MarkSuccess();
            metrics.MarkFailure();
            metrics.MarkSuccess();
            metrics.MarkFailure();
            metrics.MarkFailure();
            metrics.MarkFailure();
            Thread.Sleep(properties.MetricsHealthSnapshotIntervalInMilliseconds);

            // this should trip the circuit as the error percentage is above the threshold
            Assert.IsFalse(cb.AllowRequest());
            Assert.IsTrue(cb.IsOpen());
        }
        public void testLowVolumeDoesNotTripCircuit()
        {
            int sleepWindow = 200;
            int lowVolume   = 5;

            var properties = GetCommandConfig();

            properties.CircuitBreakerSleepWindowInMilliseconds = sleepWindow;
            properties.CircuitBreakerRequestCountThreshold     = lowVolume;
            ICommandMetrics metrics = GetMetrics(properties);
            var             cb      = new TestCircuitBreaker(properties, metrics);

            // fail
            metrics.MarkFailure();
            metrics.MarkFailure();
            metrics.MarkFailure();
            metrics.MarkFailure();

            // even though it has all failed we won't trip the circuit because the volume is low
            Assert.IsTrue(cb.AllowRequest());
            Assert.IsFalse(cb.IsOpen());
        }
        public void TestMultipleTimeWindowRetriesBeforeClosingCircuit()
        {
            int sleepWindow = 200;
            var properties  = GetCommandConfig();

            properties.CircuitBreakerSleepWindowInMilliseconds = sleepWindow;
            ICommandMetrics metrics = GetMetrics(properties);
            var             cb      = new TestCircuitBreaker(properties, metrics);

            // fail
            metrics.MarkFailure();
            metrics.MarkFailure();
            metrics.MarkFailure();
            metrics.MarkFailure();

            // everything has failed in the test window so we should return false now
            Assert.IsFalse(cb.AllowRequest());
            Assert.IsTrue(cb.IsOpen());

            // wait for sleepWindow to pass
            Thread.Sleep(sleepWindow + 50);

            // we should now allow 1 request
            Assert.IsTrue(cb.AllowRequest());
            // but the circuit should still be open
            Assert.IsTrue(cb.IsOpen());
            // and further requests are still blocked
            Assert.IsFalse(cb.AllowRequest());

            // the 'singleTest' fails so it should go back to sleep and not allow any requests again until another 'singleTest' after the sleep
            metrics.MarkFailure();

            Assert.IsFalse(cb.AllowRequest());
            Assert.IsFalse(cb.AllowRequest());
            Assert.IsFalse(cb.AllowRequest());

            // wait for sleepWindow to pass
            Thread.Sleep(sleepWindow + 50);

            // we should now allow 1 request
            Assert.IsTrue(cb.AllowRequest());
            // but the circuit should still be open
            Assert.IsTrue(cb.IsOpen());
            // and further requests are still blocked
            Assert.IsFalse(cb.AllowRequest());

            // the 'singleTest' fails again so it should go back to sleep and not allow any requests again until another 'singleTest' after the sleep
            metrics.MarkFailure();


            Assert.IsFalse(cb.AllowRequest());
            Assert.IsFalse(cb.AllowRequest());
            Assert.IsFalse(cb.AllowRequest());

            // wait for sleepWindow to pass
            Thread.Sleep(sleepWindow + 50);


            // we should now allow 1 request
            Assert.IsTrue(cb.AllowRequest());
            // but the circuit should still be open
            Assert.IsTrue(cb.IsOpen());
            // and further requests are still blocked
            Assert.IsFalse(cb.AllowRequest());

            // now it finally succeeds
            metrics.MarkSuccess();
            cb.MarkSuccess();

            // all requests should be open again
            Assert.IsTrue(cb.AllowRequest());
            Assert.IsTrue(cb.AllowRequest());
            Assert.IsTrue(cb.AllowRequest());
            // and the circuit should be closed again
            Assert.IsFalse(cb.IsOpen());
        }