1 package net.secodo.jcircuitbreaker.breaker.impl;
2
3 import static org.hamcrest.core.IsEqual.equalTo;
4
5 import static org.junit.Assert.assertThat;
6
7 import static org.mockito.Matchers.any;
8 import static org.mockito.Matchers.anyInt;
9 import static org.mockito.Matchers.anyString;
10
11 import static org.mockito.Mockito.mock;
12 import static org.mockito.Mockito.never;
13 import static org.mockito.Mockito.times;
14 import static org.mockito.Mockito.verify;
15 import static org.mockito.Mockito.verifyZeroInteractions;
16 import static org.mockito.Mockito.when;
17
18 import java.util.Iterator;
19 import java.util.LinkedHashMap;
20 import java.util.List;
21 import java.util.Map;
22
23 import net.secodo.jcircuitbreaker.breaker.ContextAwareCircuitBreaker;
24 import net.secodo.jcircuitbreaker.breaker.execution.ExecutionContext;
25 import net.secodo.jcircuitbreaker.breakhandler.BreakHandler;
26 import net.secodo.jcircuitbreaker.breakhandler.BreakHandlerFactory;
27 import net.secodo.jcircuitbreaker.breakhandler.OnePerExecutionHandlerFactory;
28 import net.secodo.jcircuitbreaker.breakhandler.impl.StatefulRetryHandler;
29 import net.secodo.jcircuitbreaker.breakstrategy.BreakStrategy;
30 import net.secodo.jcircuitbreaker.exception.TaskExecutionException;
31 import net.secodo.jcircuitbreaker.task.Task;
32
33 import org.junit.Test;
34
35
36 @SuppressWarnings({ "unchecked" })
37 public class ReusableCircuitBreakerTest {
38 @Test
39 public void shouldConstructWorkingCircuitBreakerUsingBuilder() throws TaskExecutionException {
40
41 final SomeTestClassWithSomeMethod someTestObject = mock(SomeTestClassWithSomeMethod.class);
42 when(someTestObject.someMethod(anyString(), anyInt())).thenReturn(7777L);
43
44 final Task<Long> methodCall = () -> someTestObject.someMethod("asdsas", 13213);
45
46 abstract class Cpu {
47 abstract int getCpuLoad();
48 }
49
50 final Cpu cpu = mock(Cpu.class);
51
52 ReusableCircuitBreaker<Long> breaker = ReusableCircuitBreaker.builder()
53 .withBreakStrategy((task, executionContext) -> cpu.getCpuLoad() > 80)
54 .withBreakHandler((circuitBreaker, task, breakStrategy, executionContext) -> -1L).build();
55
56 when(cpu.getCpuLoad()).thenReturn(60).thenReturn(81);
57
58
59 Long returnValue = breaker.execute(methodCall);
60
61
62 assertThat(returnValue, equalTo(7777L));
63
64
65 returnValue = breaker.execute(methodCall);
66
67
68 assertThat(returnValue, equalTo(-1L));
69
70
71 }
72
73 @Test
74 public void shouldConstructWorkingCircuitBreakerUsingCheckedBuilder() throws TaskExecutionException {
75
76 final SomeTestClassWithSomeMethod someTestObject = mock(SomeTestClassWithSomeMethod.class);
77 when(someTestObject.someMethod(anyString(), anyInt())).thenReturn(7777L);
78
79 final Task<Long> methodCall = () -> someTestObject.someMethod("asdsas", 13213);
80
81 abstract class Cpu {
82 abstract int getCpuLoad();
83 }
84
85 final Cpu cpu = mock(Cpu.class);
86
87 ReusableCircuitBreaker<Long> breaker = ReusableCircuitBreaker.<Long>builderP()
88 .withBreakStrategy((task,executionContext) -> cpu.getCpuLoad() > 80)
89 .withBreakHandler((circuitBreaker, task, breakStrategy, executionContext) -> -1L)
90 .build();
91
92 when(cpu.getCpuLoad()).thenReturn(60).thenReturn(81);
93
94
95 Long returnValue = breaker.execute(methodCall);
96
97
98 assertThat(returnValue, equalTo(7777L));
99
100
101 returnValue = breaker.execute(methodCall);
102
103
104 assertThat(returnValue, equalTo(-1L));
105
106
107 }
108
109 @Test
110 public void shouldStopMethodExecutionIfBreakerDecidesToBreak() throws TaskExecutionException {
111
112 final long returnValue = 99999L;
113 final long fallbackReturnValue = 33333L;
114 final String paramValue1 = "VALUE IS HERE";
115 final Integer paramValue2 = 10;
116
117 final BreakStrategy breakStrategy = mock(BreakStrategy.class);
118 final List<?> userData = mock(List.class);
119 final BreakHandler<Long> breakHandler = mock(BreakHandler.class);
120 final SomeTestClassWithSomeMethod someTestObject = mock(SomeTestClassWithSomeMethod.class);
121
122 Task<Long> methodCall = () -> someTestObject.someMethod(paramValue1, paramValue2);
123
124 when(breakStrategy.shouldBreak(any(Task.class), any(ExecutionContext.class))).thenReturn(true);
125 when(someTestObject.someMethod(paramValue1, paramValue2)).thenReturn(returnValue);
126 when(
127 breakHandler.onBreak(any(ContextAwareCircuitBreaker.class),
128 any(Task.class),
129 any(BreakStrategy.class),
130 any(ExecutionContext.class))).thenReturn(
131 fallbackReturnValue);
132
133
134 final ReusableCircuitBreaker<Long> genericCBTest = new ReusableCircuitBreaker<>(breakStrategy,
135 breakHandler);
136 final Long methodReturnValue = genericCBTest.execute(methodCall, userData);
137
138
139
140 verify(someTestObject, never()).someMethod(anyString(), anyInt());
141 verify(breakHandler, times(1)).onBreak(any(ContextAwareCircuitBreaker.class),
142 any(Task.class),
143 any(BreakStrategy.class),
144 any(ExecutionContext.class));
145
146 assertThat(methodReturnValue, equalTo(fallbackReturnValue));
147
148 }
149
150 @Test
151 public void shouldContinueAndExecuteMethodIfBreakerDecidesNotToBreak() throws TaskExecutionException {
152
153 final Long returnValue = 9999L;
154 final long fallbackReturnValue = 3333L;
155 final String paramValue1 = "VALUE IS HERE";
156 final Integer paramValue2 = 10;
157
158 final BreakStrategy breakStrategy = mock(BreakStrategy.class);
159 final List<?> userData = mock(List.class);
160 final BreakHandler<Long> breakHandler = mock(BreakHandler.class);
161 final SomeTestClassWithSomeMethod someTestObject = mock(SomeTestClassWithSomeMethod.class);
162
163 Task<Long> methodCall = () -> someTestObject.someMethod(paramValue1, paramValue2);
164
165 when(breakStrategy.shouldBreak(any(Task.class), any(ExecutionContext.class))).thenReturn(false);
166 when(someTestObject.someMethod(paramValue1, paramValue2)).thenReturn(returnValue);
167 when(
168 breakHandler.onBreak(any(ContextAwareCircuitBreaker.class),
169 any(Task.class),
170 any(BreakStrategy.class),
171 any(ExecutionContext.class))).thenReturn(
172 fallbackReturnValue);
173
174
175 final ReusableCircuitBreaker<Long> genericCB = new ReusableCircuitBreaker<>(breakStrategy,
176 breakHandler);
177 final Long methodReturnValue = genericCB.execute(methodCall, userData);
178
179
180
181 verify(someTestObject, times(1)).someMethod(anyString(), anyInt());
182 verify(breakHandler, never()).onBreak(any(ContextAwareCircuitBreaker.class),
183 any(Task.class),
184 any(BreakStrategy.class),
185 any(ExecutionContext.class));
186
187 assertThat(methodReturnValue, equalTo(returnValue));
188 }
189
190 @Test
191 public void shouldNotShareStateBetweenExecutionsWhenOnePerRequestHandlerFactoryIsUsedWithNotReusableBreakHandler()
192 throws TaskExecutionException {
193
194 final Long returnValue = 9999L;
195 final long fallbackReturnValue = 3333L;
196 final String paramValue1 = "VALUE IS HERE";
197 final Integer paramValue2 = 10;
198
199 final BreakStrategy breakStrategy = mock(BreakStrategy.class);
200
201
202 LinkedHashMap<Integer, Integer> invocationsCounted = new LinkedHashMap<>();
203
204
205 class NotReusableBreakHandler implements BreakHandler<Long> {
206 @Override
207 public Long onBreak(ContextAwareCircuitBreaker<Long> circuitBreaker, Task<Long> task,
208 BreakStrategy<Long> breakStrategy, ExecutionContext<Long> executionContext)
209 throws TaskExecutionException {
210 int thisObject = this.hashCode();
211
212 if (!invocationsCounted.containsKey(thisObject)) {
213 invocationsCounted.put(thisObject, 1);
214 } else {
215 int count = invocationsCounted.get(thisObject);
216 invocationsCounted.put(thisObject, count + 1);
217 }
218
219
220 return fallbackReturnValue;
221 }
222 }
223
224
225 final SomeTestClassWithSomeMethod someTestObject = mock(SomeTestClassWithSomeMethod.class);
226 Task<Long> methodCall = () -> someTestObject.someMethod(paramValue1, paramValue2);
227
228 when(breakStrategy.shouldBreak(any(Task.class), any(ExecutionContext.class))).thenReturn(true).thenReturn(true).thenReturn(true);
229
230
231 final ReusableCircuitBreaker<Long> breaker = new ReusableCircuitBreaker<>(breakStrategy,
232 (OnePerExecutionHandlerFactory<Long>) (task, executionContext) -> new NotReusableBreakHandler());
233
234
235 Long methodReturnValue = breaker.execute(methodCall);
236
237
238
239 assertThat(invocationsCounted.size(), equalTo(1));
240 assertThat(invocationsCounted.entrySet().iterator().next().getValue(),
241 equalTo(1));
242
243 verifyZeroInteractions(someTestObject);
244
245 assertThat(methodReturnValue, equalTo(fallbackReturnValue));
246
247
248
249
250 methodReturnValue = breaker.execute(methodCall);
251
252
253 assertThat(invocationsCounted.size(), equalTo(2));
254
255 final Iterator<Map.Entry<Integer, Integer>> invocationMapIt = invocationsCounted.entrySet().iterator();
256 assertThat(invocationMapIt.next().getValue(),
257 equalTo(1));
258 assertThat(invocationMapIt.next().getValue(), equalTo(1));
259
260 verifyZeroInteractions(someTestObject);
261
262 assertThat(methodReturnValue, equalTo(fallbackReturnValue));
263
264 }
265
266 @Test
267 public void shouldNotShareStateBetweenExecutionsWhenUsingOnePerExecutionHandlerFactoryWithStatefulRetryHandler()
268 throws TaskExecutionException {
269
270 final Long returnValue = 9999L;
271 final String paramValue1 = "VALUE IS HERE";
272 final Integer paramValue2 = 10;
273
274
275 LinkedHashMap<Integer, Integer> invocationsCounted = new LinkedHashMap<>();
276
277 final SomeTestClassWithSomeMethod someTestObject = mock(SomeTestClassWithSomeMethod.class);
278 when(someTestObject.someMethod(paramValue1, paramValue2)).thenReturn(returnValue);
279
280 Task<Long> methodCall = () -> someTestObject.someMethod(paramValue1, paramValue2);
281
282 class MyStatefulRetryHandler extends StatefulRetryHandler<Long> {
283 public MyStatefulRetryHandler(int maxNumberOfRetries) {
284 super(maxNumberOfRetries);
285 }
286
287 @Override
288 protected void onRetry(int currentRetryAttempt, Task<Long> task, ExecutionContext<Long> executionContext) {
289 int thisObject = this.hashCode();
290
291 if (!invocationsCounted.containsKey(thisObject)) {
292 invocationsCounted.put(thisObject, 1);
293 } else {
294 int count = invocationsCounted.get(thisObject);
295 invocationsCounted.put(thisObject, count + 1);
296 }
297 }
298 }
299
300 BreakHandlerFactory<Long> breakHandlerFactory = (OnePerExecutionHandlerFactory<Long>) (task, executionContext) ->
301 new MyStatefulRetryHandler(10);
302
303 final BreakStrategy breakStrategy = mock(BreakStrategy.class);
304 when(breakStrategy.shouldBreak(any(Task.class), any(ExecutionContext.class))).thenReturn(true)
305 .thenReturn(true)
306 .thenReturn(true)
307 .thenReturn(false)
308 .thenReturn(true)
309 .thenReturn(true)
310 .thenReturn(false);
311
312 final ReusableCircuitBreaker<Long> breaker = new ReusableCircuitBreaker<>(breakStrategy,
313 breakHandlerFactory);
314
315
316 Long methodReturnValue = breaker.execute(methodCall);
317
318
319
320 assertThat(invocationsCounted.size(), equalTo(1));
321 assertThat(invocationsCounted.entrySet().iterator().next().getValue(), equalTo(3));
322
323 verify(someTestObject, times(1)).someMethod(any(String.class), any(Integer.class));
324
325 assertThat(methodReturnValue, equalTo(returnValue));
326
327
328
329
330 methodReturnValue = breaker.execute(methodCall);
331
332
333 assertThat(invocationsCounted.size(), equalTo(2));
334
335 final Iterator<Map.Entry<Integer, Integer>> invocationMapIt = invocationsCounted.entrySet().iterator();
336 assertThat(invocationMapIt.next().getValue(), equalTo(3));
337 assertThat(invocationMapIt.next().getValue(), equalTo(2));
338
339 verify(someTestObject, times(2)).someMethod(any(String.class), any(Integer.class));
340
341 assertThat(methodReturnValue, equalTo(returnValue));
342
343 }
344
345
346 class SomeTestClassWithSomeMethod {
347 SomeTestClassWithSomeMethod() {
348 }
349
350 public Long someMethod(String paramValue1, Integer paramValue2) {
351 return (long) 1;
352 }
353
354
355 }
356
357 }