1 package net.secodo.jcircuitbreaker.breakhandler.impl;
2
3 import net.secodo.jcircuitbreaker.exception.TaskExecutionException;
4 import net.secodo.jcircuitbreaker.task.Task;
5 import org.junit.Test;
6 import net.secodo.jcircuitbreaker.exception.CircuitBreakerException;
7 import net.secodo.jcircuitbreaker.breaker.CircuitBreaker;
8 import net.secodo.jcircuitbreaker.breaker.execution.ExecutionContext;
9 import net.secodo.jcircuitbreaker.breaker.impl.DefaultCircuitBreaker;
10 import net.secodo.jcircuitbreaker.breakstrategy.BreakStrategy;
11 import java.lang.reflect.Field;
12 import java.util.concurrent.Callable;
13 import static junit.framework.TestCase.fail;
14 import static org.hamcrest.CoreMatchers.equalTo;
15 import static org.junit.Assert.assertThat;
16 import static org.junit.Assert.assertTrue;
17 import static org.mockito.Matchers.any;
18 import static org.mockito.Mockito.mock;
19 import static org.mockito.Mockito.times;
20 import static org.mockito.Mockito.verify;
21 import static org.mockito.Mockito.when;
22
23
24 @SuppressWarnings("unchecked")
25 public class StatefulRetryHandlerTest {
26 @Test
27 public void shouldResultInRetryExceptionWhenBreakStrategyBlocksTaskExecutionAndNumberOfRetryAttemptsExceeds()
28 throws TaskExecutionException, NoSuchFieldException, IllegalAccessException {
29
30 final int maxNumberOfRetries = 3;
31 final int wantedNumberOfTaskInvocations = maxNumberOfRetries + 1;
32
33
34 final StatefulRetryHandler<Long> retryHandler = new StatefulRetryHandler<>(maxNumberOfRetries);
35 final CircuitBreaker<Long> circuitBreaker = new DefaultCircuitBreaker<>();
36
37 final BreakStrategy<Long> breakStrategy = mock(BreakStrategy.class);
38 when(breakStrategy.shouldBreak(any(Task.class), any(ExecutionContext.class))).thenReturn(true);
39
40 final SomeTestClassWithSomeMethod someTestObject = mock(SomeTestClassWithSomeMethod.class);
41
42 Task<Long> methodCall = () -> someTestObject.someMethod("44444444", 222222);
43
44
45 RetryHandlerException resultedException = null;
46
47 try {
48 circuitBreaker.execute(methodCall, breakStrategy, retryHandler, new Object());
49
50 fail("Expected " + RetryHandlerException.class.getSimpleName() + " to occur, but no exception was thrown");
51 } catch (RetryHandlerException e) {
52 resultedException = e;
53 }
54
55
56
57 assertTrue(RetryHandlerException.class.isInstance(resultedException));
58 verify(breakStrategy, times(wantedNumberOfTaskInvocations)).shouldBreak(any(Task.class), any(ExecutionContext.class));
59
60 assertThat(getCurrentRetryAttempt(retryHandler), equalTo(maxNumberOfRetries));
61
62 }
63
64 @Test
65 public void shouldRetryOnceInCaseOfStrategyDoesNotAllowTaskExecutionAndOnlyOneExtraInvocatinIsAllowedByRetryHandler()
66 throws TaskExecutionException, NoSuchFieldException, IllegalAccessException {
67
68 final int maxNumberOfRetries = 1;
69 final int wantedNumberOfTaskInvocations = 2;
70
71
72 final StatefulRetryHandler<Long> retryHandler = new StatefulRetryHandler<>();
73 final CircuitBreaker circuitBreaker = new DefaultCircuitBreaker();
74 final BreakStrategy breakStrategy = mock(BreakStrategy.class);
75 final SomeTestClassWithSomeMethod someTestObject = mock(SomeTestClassWithSomeMethod.class);
76
77 when(breakStrategy.shouldBreak(any(Task.class), any(ExecutionContext.class))).thenReturn(true);
78
79 Task<Long> methodCall = () -> someTestObject.someMethod("44444444", 222222);
80
81
82 RetryHandlerException resultedException = null;
83
84 try {
85 circuitBreaker.execute(methodCall, breakStrategy, retryHandler, new Object());
86
87 fail("Expected " + CircuitBreakerException.class.getSimpleName() + " to occur, but no exception was thrown");
88 } catch (RetryHandlerException e) {
89 resultedException = e;
90 }
91
92
93 assertTrue(RetryHandlerException.class.isInstance(resultedException));
94 verify(breakStrategy, times(wantedNumberOfTaskInvocations)).shouldBreak(any(Task.class), any(ExecutionContext.class));
95
96 assertThat(getCurrentRetryAttempt(retryHandler), equalTo(maxNumberOfRetries));
97
98 }
99
100 @Test
101 public void shouldRetryInCaseOfStrategyDoesNotAllowTaskExecutionThreeTimesButAllowFourthTime()
102 throws TaskExecutionException,
103 RetryHandlerException,
104 NoSuchFieldException,
105 IllegalAccessException {
106
107 final int maxNumberOfRetries = 3;
108 final int wantedNumberOfInvocations = maxNumberOfRetries + 1;
109 final String paramValue1 = "44444444";
110 final int paramValue2 = 222222;
111 final Long returnValue = 88888L;
112
113
114 final StatefulRetryHandler<Long> retryHandler = new StatefulRetryHandler<>(maxNumberOfRetries);
115 final CircuitBreaker<Long> circuitBreaker = new DefaultCircuitBreaker<>();
116 final BreakStrategy breakStrategy = mock(BreakStrategy.class);
117 final SomeTestClassWithSomeMethod someTestObject = mock(SomeTestClassWithSomeMethod.class);
118
119 when(breakStrategy.shouldBreak(any(Task.class), any(ExecutionContext.class))).thenReturn(true)
120 .thenReturn(true)
121 .thenReturn(true)
122 .thenReturn(false);
123 when(someTestObject.someMethod(paramValue1, paramValue2)).thenReturn(returnValue);
124
125 Task<Long> methodCall = () -> someTestObject.someMethod(paramValue1, paramValue2);
126
127
128
129 final Long resultingReturnValue = circuitBreaker.execute(methodCall, breakStrategy, retryHandler, new Object());
130
131
132 verify(breakStrategy, times(wantedNumberOfInvocations)).shouldBreak(any(Task.class), any(ExecutionContext.class));
133 assertThat(getCurrentRetryAttempt(retryHandler), equalTo(3));
134
135 verify(someTestObject, times(1)).someMethod(paramValue1, paramValue2);
136 assertThat(resultingReturnValue, equalTo(returnValue));
137
138 }
139
140 @Test
141 public void shouldCallOnRetryCallbackForEachRetryIfCallbackWasDefined() throws TaskExecutionException {
142
143 final int maxNumberOfRetries = 3;
144 final String paramValue1 = "44444444";
145 final int paramValue2 = 222222;
146
147 final RetryHandlerOnRetryCallback callback = mock(RetryHandlerOnRetryCallback.class);
148 final StatefulRetryHandler<Long> retryHandler = new StatefulRetryHandler<>(maxNumberOfRetries, callback);
149
150 final CircuitBreaker<Long> circuitBreaker = new DefaultCircuitBreaker();
151 final BreakStrategy breakStrategy = mock(BreakStrategy.class);
152 final SomeTestClassWithSomeMethod someTestObject = mock(SomeTestClassWithSomeMethod.class);
153
154 when(breakStrategy.shouldBreak(any(Task.class), any(ExecutionContext.class))).thenReturn(true);
155
156 Task<Long> methodCall = () -> someTestObject.someMethod(paramValue1, paramValue2);
157
158
159 final Long resultingReturnValue;
160 try {
161 resultingReturnValue = circuitBreaker.execute(methodCall, breakStrategy, retryHandler, new Object());
162 fail("RetryHandlerException should have been thrown");
163 } catch (RetryHandlerException e) {
164
165
166 }
167
168
169 verify(callback, times(maxNumberOfRetries)).onRetry(any(Integer.class), any(ExecutionContext.class));
170 verify(breakStrategy, times(maxNumberOfRetries + 1)).shouldBreak(any(Task.class), any(ExecutionContext.class));
171
172 }
173
174 class SomeTestClassWithSomeMethod {
175 SomeTestClassWithSomeMethod() {
176 }
177
178 public Long someMethod(String paramValue1, Integer paramValue2) {
179 return (long) 1;
180 }
181
182
183 }
184
185 private int getCurrentRetryAttempt(StatefulRetryHandler retryHandler) throws NoSuchFieldException, IllegalAccessException {
186 Field field = StatefulRetryHandler.class.getDeclaredField("currentRetryAttempt");
187 field.setAccessible(true);
188 return (int) field.get(retryHandler);
189
190 }
191 }