Skip to content

Commit ab5ca70

Browse files
committed
Added test cases
1 parent ce95df7 commit ab5ca70

File tree

3 files changed

+252
-0
lines changed

3 files changed

+252
-0
lines changed
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package org.hypertrace.circuitbreaker.grpcutils;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertFalse;
5+
import static org.junit.jupiter.api.Assertions.assertNotNull;
6+
import static org.junit.jupiter.api.Assertions.assertTrue;
7+
8+
import com.typesafe.config.Config;
9+
import com.typesafe.config.ConfigFactory;
10+
import java.time.Duration;
11+
import java.util.Map;
12+
import org.hypertrace.circuitbreaker.grpcutils.CircuitBreakerThresholds.SlidingWindowType;
13+
import org.junit.jupiter.api.Test;
14+
15+
class CircuitBreakerConfigParserTest {
16+
17+
@Test
18+
void testParseConfig() {
19+
String configStr =
20+
"enabled = true\n" +
21+
"defaultThresholds {\n" +
22+
" failureRateThreshold = 50.0\n" +
23+
" slowCallRateThreshold = 30.0\n" +
24+
" slowCallDurationThreshold = 1s\n" +
25+
" slidingWindowSize = 100\n" +
26+
" slidingWindowType = COUNT_BASED\n" +
27+
"}\n" +
28+
"service1 {\n" +
29+
" failureRateThreshold = 60.0\n" +
30+
" slowCallRateThreshold = 40.0\n" +
31+
" slowCallDurationThreshold = 2s\n" +
32+
" slidingWindowSize = 200\n" +
33+
" slidingWindowType = TIME_BASED\n" +
34+
"}";
35+
36+
Config config = ConfigFactory.parseString(configStr);
37+
CircuitBreakerConfiguration.CircuitBreakerConfigurationBuilder<Object> builder =
38+
CircuitBreakerConfigParser.parseConfig(config);
39+
CircuitBreakerConfiguration<Object> configuration = builder.build();
40+
41+
// Test enabled flag
42+
assertTrue(configuration.isEnabled());
43+
44+
// Test default thresholds
45+
CircuitBreakerThresholds defaultThresholds = configuration.getDefaultThresholds();
46+
assertNotNull(defaultThresholds);
47+
assertEquals(50.0f, defaultThresholds.getFailureRateThreshold());
48+
assertEquals(30.0f, defaultThresholds.getSlowCallRateThreshold());
49+
assertEquals(Duration.ofSeconds(1), defaultThresholds.getSlowCallDurationThreshold());
50+
assertEquals(100, defaultThresholds.getSlidingWindowSize());
51+
assertEquals(SlidingWindowType.COUNT_BASED, defaultThresholds.getSlidingWindowType());
52+
53+
// Test service specific thresholds
54+
Map<String, CircuitBreakerThresholds> thresholdsMap = configuration.getCircuitBreakerThresholdsMap();
55+
assertNotNull(thresholdsMap);
56+
assertTrue(thresholdsMap.containsKey("service1"));
57+
58+
CircuitBreakerThresholds service1Thresholds = thresholdsMap.get("service1");
59+
assertEquals(60.0f, service1Thresholds.getFailureRateThreshold());
60+
assertEquals(40.0f, service1Thresholds.getSlowCallRateThreshold());
61+
assertEquals(Duration.ofSeconds(2), service1Thresholds.getSlowCallDurationThreshold());
62+
assertEquals(200, service1Thresholds.getSlidingWindowSize());
63+
assertEquals(SlidingWindowType.TIME_BASED, service1Thresholds.getSlidingWindowType());
64+
}
65+
66+
@Test
67+
void testParseConfigWithMinimalConfig() {
68+
String configStr = "{}";
69+
Config config = ConfigFactory.parseString(configStr);
70+
CircuitBreakerConfiguration.CircuitBreakerConfigurationBuilder<Object> builder =
71+
CircuitBreakerConfigParser.parseConfig(config);
72+
CircuitBreakerConfiguration<Object> configuration = builder.build();
73+
74+
// Test that defaults are used when no config is provided
75+
assertFalse(configuration.isEnabled());
76+
assertNotNull(configuration.getDefaultThresholds());
77+
assertTrue(configuration.getCircuitBreakerThresholdsMap().isEmpty());
78+
}
79+
}

grpc-circuitbreaker-utils/src/test/java/org/hypertrace/circuitbreaker/grpcutils/resilience/ResilienceCircuitBreakerConfigConverterTest.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
import static org.junit.jupiter.api.Assertions.assertEquals;
44
import static org.junit.jupiter.api.Assertions.assertThrows;
5+
import static org.junit.jupiter.api.Assertions.assertTrue;
56

67
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
78
import java.time.Duration;
89
import java.util.HashMap;
10+
import java.util.List;
911
import java.util.Map;
1012
import org.hypertrace.circuitbreaker.grpcutils.CircuitBreakerThresholds;
1113
import org.junit.jupiter.api.Assertions;
@@ -51,4 +53,44 @@ void shouldThrowExceptionWhenConfigurationIsNull() {
5153
NullPointerException.class,
5254
() -> ResilienceCircuitBreakerConfigConverter.convertConfig(null));
5355
}
56+
57+
@Test
58+
void shouldGetDisabledKeys() {
59+
// Create a mix of enabled and disabled configurations
60+
CircuitBreakerThresholds enabledThresholds =
61+
CircuitBreakerThresholds.builder()
62+
.enabled(true)
63+
.failureRateThreshold(50.0f)
64+
.build();
65+
66+
CircuitBreakerThresholds disabledThresholds1 =
67+
CircuitBreakerThresholds.builder()
68+
.enabled(false)
69+
.failureRateThreshold(50.0f)
70+
.build();
71+
72+
CircuitBreakerThresholds disabledThresholds2 =
73+
CircuitBreakerThresholds.builder()
74+
.enabled(false)
75+
.failureRateThreshold(60.0f)
76+
.build();
77+
78+
Map<String, CircuitBreakerThresholds> configMap = new HashMap<>();
79+
configMap.put("enabledService", enabledThresholds);
80+
configMap.put("disabledService1", disabledThresholds1);
81+
configMap.put("disabledService2", disabledThresholds2);
82+
83+
List<String> disabledKeys = ResilienceCircuitBreakerConfigConverter.getDisabledKeys(configMap);
84+
85+
assertEquals(2, disabledKeys.size());
86+
assertTrue(disabledKeys.contains("disabledService1"));
87+
assertTrue(disabledKeys.contains("disabledService2"));
88+
}
89+
90+
@Test
91+
void shouldGetEmptyDisabledKeysForEmptyConfig() {
92+
Map<String, CircuitBreakerThresholds> configMap = new HashMap<>();
93+
List<String> disabledKeys = ResilienceCircuitBreakerConfigConverter.getDisabledKeys(configMap);
94+
assertTrue(disabledKeys.isEmpty());
95+
}
5496
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package org.hypertrace.circuitbreaker.grpcutils.resilience;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertFalse;
5+
import static org.junit.jupiter.api.Assertions.assertTrue;
6+
import static org.mockito.ArgumentMatchers.any;
7+
import static org.mockito.ArgumentMatchers.eq;
8+
import static org.mockito.Mockito.lenient;
9+
import static org.mockito.Mockito.times;
10+
import static org.mockito.Mockito.verify;
11+
12+
import io.github.resilience4j.circuitbreaker.CircuitBreaker;
13+
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
14+
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;
15+
import java.util.Arrays;
16+
import java.util.HashMap;
17+
import java.util.List;
18+
import java.util.Map;
19+
import java.util.Optional;
20+
import org.junit.jupiter.api.BeforeEach;
21+
import org.junit.jupiter.api.Test;
22+
import org.junit.jupiter.api.extension.ExtendWith;
23+
import org.mockito.Mock;
24+
import org.mockito.junit.jupiter.MockitoExtension;
25+
26+
@ExtendWith(MockitoExtension.class)
27+
class ResilienceCircuitBreakerProviderTest {
28+
29+
@Mock private CircuitBreakerRegistry circuitBreakerRegistry;
30+
@Mock private CircuitBreaker mockCircuitBreaker;
31+
@Mock private CircuitBreaker.EventPublisher eventPublisher;
32+
33+
private ResilienceCircuitBreakerProvider provider;
34+
private Map<String, CircuitBreakerConfig> configMap;
35+
private List<String> disabledKeys;
36+
private static final String CONFIG_KEY = "defaultConfig";
37+
private static final String ENABLED_SERVICE = "enabledService";
38+
private static final String DISABLED_SERVICE = "disabledService";
39+
private static final String NON_CONFIGURED_SERVICE = "nonConfiguredService";
40+
private CircuitBreakerConfig defaultConfig;
41+
private CircuitBreakerConfig serviceConfig;
42+
43+
@BeforeEach
44+
void setup() {
45+
defaultConfig = CircuitBreakerConfig.custom().build();
46+
serviceConfig = CircuitBreakerConfig.custom().build();
47+
48+
configMap = new HashMap<>();
49+
configMap.put(ENABLED_SERVICE, serviceConfig);
50+
configMap.put(CONFIG_KEY, defaultConfig);
51+
52+
disabledKeys = Arrays.asList(DISABLED_SERVICE);
53+
54+
// Use lenient() for all mocks since they might not be used in every test
55+
lenient().when(mockCircuitBreaker.getEventPublisher()).thenReturn(eventPublisher);
56+
lenient().when(eventPublisher.onStateTransition(any())).thenReturn(eventPublisher);
57+
lenient().when(eventPublisher.onCallNotPermitted(any())).thenReturn(eventPublisher);
58+
lenient().doNothing().when(eventPublisher).onEvent(any());
59+
60+
lenient().when(circuitBreakerRegistry.circuitBreaker(eq(ENABLED_SERVICE), eq(serviceConfig)))
61+
.thenReturn(mockCircuitBreaker);
62+
lenient().when(circuitBreakerRegistry.circuitBreaker(eq(NON_CONFIGURED_SERVICE), eq(defaultConfig)))
63+
.thenReturn(mockCircuitBreaker);
64+
65+
provider = new ResilienceCircuitBreakerProvider(
66+
circuitBreakerRegistry,
67+
configMap,
68+
disabledKeys,
69+
true,
70+
CONFIG_KEY);
71+
}
72+
73+
@Test
74+
void shouldReturnCircuitBreakerForEnabledKey() {
75+
Optional<CircuitBreaker> result = provider.getCircuitBreaker(ENABLED_SERVICE);
76+
assertTrue(result.isPresent());
77+
assertEquals(mockCircuitBreaker, result.get());
78+
79+
// Verify cache behavior by getting the same key again
80+
Optional<CircuitBreaker> secondResult = provider.getCircuitBreaker(ENABLED_SERVICE);
81+
assertTrue(secondResult.isPresent());
82+
assertEquals(mockCircuitBreaker, secondResult.get());
83+
84+
// Verify circuit breaker was created only once
85+
verify(circuitBreakerRegistry, times(1))
86+
.circuitBreaker(eq(ENABLED_SERVICE), eq(serviceConfig));
87+
}
88+
89+
@Test
90+
void shouldReturnEmptyForDisabledKey() {
91+
Optional<CircuitBreaker> result = provider.getCircuitBreaker(DISABLED_SERVICE);
92+
assertFalse(result.isPresent());
93+
}
94+
95+
@Test
96+
void shouldReturnEmptyForNonConfiguredServiceWhenConfigKeyDisabled() {
97+
// Create provider with disabled config key
98+
ResilienceCircuitBreakerProvider providerWithDisabledConfig = new ResilienceCircuitBreakerProvider(
99+
circuitBreakerRegistry,
100+
configMap,
101+
Arrays.asList(DISABLED_SERVICE, CONFIG_KEY), // CONFIG_KEY is disabled
102+
true,
103+
CONFIG_KEY);
104+
105+
Optional<CircuitBreaker> result = providerWithDisabledConfig.getCircuitBreaker(NON_CONFIGURED_SERVICE);
106+
assertFalse(result.isPresent());
107+
}
108+
109+
@Test
110+
void shouldReturnCircuitBreakerForNonConfiguredServiceWhenConfigKeyEnabled() {
111+
Optional<CircuitBreaker> result = provider.getCircuitBreaker(NON_CONFIGURED_SERVICE);
112+
assertTrue(result.isPresent());
113+
assertEquals(mockCircuitBreaker, result.get());
114+
115+
// Verify it used the default config
116+
verify(circuitBreakerRegistry, times(1))
117+
.circuitBreaker(eq(NON_CONFIGURED_SERVICE), eq(defaultConfig));
118+
}
119+
120+
@Test
121+
void shouldCacheCircuitBreakerInstances() {
122+
// Get the same circuit breaker multiple times
123+
provider.getCircuitBreaker(ENABLED_SERVICE);
124+
provider.getCircuitBreaker(ENABLED_SERVICE);
125+
provider.getCircuitBreaker(ENABLED_SERVICE);
126+
127+
// Verify circuit breaker was created only once despite multiple calls
128+
verify(circuitBreakerRegistry, times(1))
129+
.circuitBreaker(eq(ENABLED_SERVICE), eq(serviceConfig));
130+
}
131+
}

0 commit comments

Comments
 (0)