From 5085a97c21bf7bdfd4050625ab339959599f82f7 Mon Sep 17 00:00:00 2001 From: Renan Pimentel Date: Tue, 6 Oct 2026 10:18:28 -0300 Subject: [PATCH 1/2] test(circuit-breaker): verify open circuit does not call the service - Add a test with a call-counting service: once the circuit is open, attemptRequest() must return the cached failure without calling the service again. Previously a fallback and a real failing call returned the same string, so ignoring the OPEN state went undetected. - testEvaluateState computed "a failure long ago" with int arithmetic (1000 * 1000 * 1000 * 1000 overflows to -727379968), which placed the failure in the future. Use long literals and assert OPEN within the retry period and HALF_OPEN after it. --- .../DefaultCircuitBreakerTest.java | 35 +++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/DefaultCircuitBreakerTest.java b/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/DefaultCircuitBreakerTest.java index c184a8376220..982adb2b80ef 100644 --- a/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/DefaultCircuitBreakerTest.java +++ b/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/DefaultCircuitBreakerTest.java @@ -25,7 +25,9 @@ package com.iluwatar.circuitbreaker; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import java.util.concurrent.atomic.AtomicInteger; import org.junit.jupiter.api.Test; /** Circuit Breaker test */ @@ -34,20 +36,20 @@ class DefaultCircuitBreakerTest { // long timeout, int failureThreshold, long retryTimePeriod @Test void testEvaluateState() { - var circuitBreaker = new DefaultCircuitBreaker(null, 1, 1, 100); + var circuitBreaker = new DefaultCircuitBreaker(null, 1, 1, 60_000_000_000L); // Right now, failureCountfailureThreshold, and lastFailureTime is nearly equal to current time, + // the retry period has not elapsed yet, so state should be open + assertEquals(circuitBreaker.getState(), "OPEN"); + // Since failureCount>failureThreshold, and lastFailureTime is older than the retry period, // state should be half-open - assertEquals(circuitBreaker.getState(), "HALF_OPEN"); - // Since failureCount>failureThreshold, and lastFailureTime is much lesser current time, - // state should be open - circuitBreaker.lastFailureTime = System.nanoTime() - 1000 * 1000 * 1000 * 1000; + circuitBreaker.lastFailureTime = System.nanoTime() - 120_000_000_000L; circuitBreaker.evaluateState(); - assertEquals(circuitBreaker.getState(), "OPEN"); + assertEquals(circuitBreaker.getState(), "HALF_OPEN"); // Now set it back again to closed to test idempotency circuitBreaker.failureCount = 0; circuitBreaker.evaluateState(); @@ -79,4 +81,25 @@ public String call() throws RemoteServiceException { var response = circuitBreaker.attemptRequest(); assertEquals(response, "Remote Success"); } + + @Test + void testOpenCircuitReturnsCachedResponseWithoutCallingService() throws RemoteServiceException { + var calls = new AtomicInteger(); + RemoteService failingService = + new RemoteService() { + @Override + public String call() throws RemoteServiceException { + throw new RemoteServiceException("Failure #" + calls.incrementAndGet()); + } + }; + var circuitBreaker = new DefaultCircuitBreaker(failingService, 1, 1, 60_000_000_000L); + + // The first failure reaches the threshold and opens the circuit + assertThrows(RemoteServiceException.class, circuitBreaker::attemptRequest); + + // While open, the cached failure response is returned without calling the service again + assertEquals("Failure #1", circuitBreaker.attemptRequest()); + assertEquals(1, calls.get()); + assertEquals("OPEN", circuitBreaker.getState()); + } } From 6f47a623ba45e8991767077597bbedbf7e3e8e36 Mon Sep 17 00:00:00 2001 From: Renan Pimentel Date: Tue, 6 Oct 2026 10:53:36 -0300 Subject: [PATCH 2/2] test(circuit-breaker): use (expected, actual) order in testEvaluateState --- .../circuitbreaker/DefaultCircuitBreakerTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/DefaultCircuitBreakerTest.java b/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/DefaultCircuitBreakerTest.java index 982adb2b80ef..ad8896beb557 100644 --- a/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/DefaultCircuitBreakerTest.java +++ b/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/DefaultCircuitBreakerTest.java @@ -38,22 +38,22 @@ class DefaultCircuitBreakerTest { void testEvaluateState() { var circuitBreaker = new DefaultCircuitBreaker(null, 1, 1, 60_000_000_000L); // Right now, failureCountfailureThreshold, and lastFailureTime is nearly equal to current time, // the retry period has not elapsed yet, so state should be open - assertEquals(circuitBreaker.getState(), "OPEN"); + assertEquals("OPEN", circuitBreaker.getState()); // Since failureCount>failureThreshold, and lastFailureTime is older than the retry period, // state should be half-open circuitBreaker.lastFailureTime = System.nanoTime() - 120_000_000_000L; circuitBreaker.evaluateState(); - assertEquals(circuitBreaker.getState(), "HALF_OPEN"); + assertEquals("HALF_OPEN", circuitBreaker.getState()); // Now set it back again to closed to test idempotency circuitBreaker.failureCount = 0; circuitBreaker.evaluateState(); - assertEquals(circuitBreaker.getState(), "CLOSED"); + assertEquals("CLOSED", circuitBreaker.getState()); } @Test