diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml
index 6b02067..8da69a5 100644
--- a/.github/workflows/run-tests.yml
+++ b/.github/workflows/run-tests.yml
@@ -30,6 +30,11 @@ jobs:
echo "::add-matcher::${{ runner.tool_cache }}/php.json"
echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
+ - name: Setup redis
+ uses: supercharge/redis-github-action@1.2.0
+ with:
+ redis-version: 6
+
- name: Install dependencies
run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 74d6f49..ed04472 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -13,4 +13,7 @@
./src
+
+
+
diff --git a/tests/CircuitBreakerTest.php b/tests/CircuitBreakerTest.php
index 4b45130..f8aecc2 100644
--- a/tests/CircuitBreakerTest.php
+++ b/tests/CircuitBreakerTest.php
@@ -9,6 +9,8 @@
use Stfn\CircuitBreaker\Exceptions\CircuitForceOpenException;
use Stfn\CircuitBreaker\Exceptions\CircuitHalfOpenFailException;
use Stfn\CircuitBreaker\Exceptions\CircuitOpenException;
+use Stfn\CircuitBreaker\Storage\InMemoryStorage;
+use Stfn\CircuitBreaker\Storage\RedisStorage;
class CircuitBreakerTest extends TestCase
{
@@ -29,6 +31,8 @@ public function test_if_it_can_handle_function_success()
});
$this->assertEquals($object, $result);
+
+ $this->assertTrue($breaker->isClosed());
}
public function test_if_it_will_throw_an_exception_if_circuit_breaker_is_open()
@@ -121,6 +125,18 @@ public function test_if_it_will_transit_back_to_open_state_after_first_fail()
$this->assertTrue($breaker->isOpen());
}
+ public function test_if_it_will_transit_to_half_open_state_after_recovery_time()
+ {
+ $breaker = CircuitBreaker::for('test')->withOptions(['recovery_time' => 1]);
+ $breaker->openCircuit();
+
+ sleep(2);
+
+ $breaker->call(fn () => true);
+
+ $this->assertEquals(CircuitState::HalfOpen, $breaker->getStorage()->getState());
+ }
+
public function test_if_listener_is_called()
{
$object = new class () extends CircuitBreakerListener {
@@ -250,4 +266,15 @@ public function onStateChange(CircuitBreaker $breaker, CircuitState $previousSta
$this->assertEquals("closed->open,open->half_open,half_open->closed,closed->force_open,", $object->state);
}
+
+ public function test_if_it_can_set_a_new_storage()
+ {
+ $breaker = CircuitBreaker::for('test');
+
+ $this->assertInstanceOf(InMemoryStorage::class, $breaker->getStorage());
+
+ $breaker->storage(new RedisStorage(new \Redis()));
+
+ $this->assertInstanceOf(RedisStorage::class, $breaker->getStorage());
+ }
}
diff --git a/tests/ConfigTest.php b/tests/ConfigTest.php
index 838c5ff..9d58910 100644
--- a/tests/ConfigTest.php
+++ b/tests/ConfigTest.php
@@ -27,5 +27,7 @@ public function test_if_it_can_set_valid_config()
$this->assertEquals($setup['failure_threshold'], $config->failureThreshold);
$this->assertEquals($setup['recovery_time'], $config->recoveryTime);
$this->assertEquals($setup['sample_duration'], $config->sampleDuration);
+
+ $this->assertEquals($setup, $config->toArray());
}
}
diff --git a/tests/Storage/RedisStorageTest.php b/tests/Storage/RedisStorageTest.php
new file mode 100644
index 0000000..d51625e
--- /dev/null
+++ b/tests/Storage/RedisStorageTest.php
@@ -0,0 +1,108 @@
+getRedisInstance());
+
+ $storage->init(CircuitBreaker::for('test'));
+
+ $this->assertEquals(CircuitState::Closed, $storage->getState());
+ }
+
+ public function test_if_set_state_will_change_value()
+ {
+ $storage = new RedisStorage($this->getRedisInstance());
+
+ $storage->init(CircuitBreaker::for('test'));
+
+ $this->assertEquals(CircuitState::Closed, $storage->getState());
+
+ $storage->setState(CircuitState::HalfOpen);
+
+ $this->assertEquals(CircuitState::HalfOpen, $storage->getState());
+ }
+
+ public function test_if_increment_failure_will_increase_number_of_failures()
+ {
+ $storage = new RedisStorage($this->getRedisInstance());
+
+ $storage->init(CircuitBreaker::for('test'));
+
+ $this->assertEquals(0, $storage->getNumberOfFailures());
+
+ $storage->incrementFailure();
+
+ $this->assertEquals(1, $storage->getNumberOfFailures());
+
+ $storage->incrementFailure();
+
+
+ $this->assertEquals(2, $storage->getNumberOfFailures());
+ }
+
+ public function test_if_reset_counter_will_remove_fail_count()
+ {
+ $storage = new RedisStorage($this->getRedisInstance());
+ $storage->init(CircuitBreaker::for('test'));
+
+ $storage->incrementFailure();
+ $storage->incrementFailure();
+ $storage->incrementFailure();
+
+ $this->assertEquals(3, $storage->getNumberOfFailures());
+
+ $storage->resetCounter();
+
+ $this->assertEquals(0, $storage->getNumberOfFailures());
+ }
+
+ public function test_transition_to_open_state()
+ {
+ $storage = new RedisStorage($this->getRedisInstance());
+ $storage->init(CircuitBreaker::for('test'));
+
+ $storage->open();
+
+ $this->assertEquals(CircuitState::Open, $storage->getState());
+ $this->assertEquals(0, $storage->getNumberOfFailures());
+ $this->assertNotEquals(0, $storage->openedAt());
+ }
+
+ public function test_transition_to_closed_state()
+ {
+ $storage = new RedisStorage($this->getRedisInstance());
+ $storage->init(CircuitBreaker::for('test'));
+
+ $storage->open();
+ $storage->close();
+
+ $this->assertEquals(CircuitState::Closed, $storage->getState());
+ $this->assertEquals(0, $storage->openedAt());
+ }
+
+ public function getRedisInstance()
+ {
+ if (! $this->redis) {
+ $this->redis = new \Redis();
+ $this->redis->connect(getenv("REDIS_HOST"));
+ }
+
+ return $this->redis;
+ }
+
+ public function tearDown(): void
+ {
+ $this->getRedisInstance()->flushDB();
+ }
+}