1
+ package io .github .resilience4j .circuitbreaker ;
2
+
3
+ import io .github .resilience4j .circuitbreaker .CircuitBreaker .CircuitBreakerFuture ;
4
+ import org .junit .Test ;
5
+
6
+ import java .util .concurrent .*;
7
+
8
+ import static org .assertj .core .api .Assertions .assertThat ;
9
+ import static org .assertj .core .api .Assertions .catchThrowable ;
10
+ import static org .mockito .ArgumentMatchers .any ;
11
+ import static org .mockito .ArgumentMatchers .anyLong ;
12
+ import static org .mockito .BDDMockito .then ;
13
+ import static org .mockito .Mockito .mock ;
14
+ import static org .mockito .Mockito .times ;
15
+ import static org .mockito .Mockito .when ;
16
+
17
+ /**
18
+ * Class CircuitBreakerFutureTest.
19
+ */
20
+ public class CircuitBreakerFutureTest {
21
+
22
+ @ Test
23
+ public void shouldDecorateFutureAndReturnSuccess () throws Exception {
24
+ CircuitBreaker circuitBreaker = CircuitBreaker .ofDefaults ("testName" );
25
+
26
+ final Future <String > future = mock (Future .class );
27
+ when (future .get ()).thenReturn ("Hello World" );
28
+
29
+ CircuitBreakerFuture <String > decoratedFuture = new CircuitBreakerFuture <>(circuitBreaker , future );
30
+ String value = decoratedFuture .get ();
31
+
32
+ assertThat (value ).isEqualTo ("Hello World" );
33
+
34
+ assertThat (circuitBreaker .getMetrics ().getNumberOfBufferedCalls ()).isEqualTo (1 );
35
+ assertThat (circuitBreaker .getMetrics ().getNumberOfFailedCalls ()).isEqualTo (0 );
36
+ assertThat (circuitBreaker .getMetrics ().getNumberOfSuccessfulCalls ()).isEqualTo (1 );
37
+ assertThat (circuitBreaker .getMetrics ().getNumberOfNotPermittedCalls ()).isEqualTo (0 );
38
+ assertThat (circuitBreaker .getState ()).isEqualTo (CircuitBreaker .State .CLOSED );
39
+
40
+ then (future ).should ().get ();
41
+ }
42
+
43
+ @ Test
44
+ public void shouldDecorateFutureAndCircuitBreakingLogicApplyOnceOnMultipleFutureEval () throws Exception {
45
+ CircuitBreaker circuitBreaker = CircuitBreaker .ofDefaults ("testName" );
46
+
47
+ final Future <String > future = mock (Future .class );
48
+ when (future .get ()).thenReturn ("Hello World" );
49
+
50
+ CircuitBreakerFuture <String > decoratedFuture = new CircuitBreakerFuture <>(circuitBreaker , future );
51
+
52
+ //called twice but circuit breaking should be evaluated once.
53
+ decoratedFuture .get ();
54
+ decoratedFuture .get ();
55
+
56
+ assertThat (circuitBreaker .getMetrics ().getNumberOfBufferedCalls ()).isEqualTo (1 );
57
+ assertThat (circuitBreaker .getMetrics ().getNumberOfFailedCalls ()).isEqualTo (0 );
58
+ assertThat (circuitBreaker .getMetrics ().getNumberOfSuccessfulCalls ()).isEqualTo (1 );
59
+ assertThat (circuitBreaker .getMetrics ().getNumberOfNotPermittedCalls ()).isEqualTo (0 );
60
+ assertThat (circuitBreaker .getState ()).isEqualTo (CircuitBreaker .State .CLOSED );
61
+
62
+ then (future ).should (times (2 )).get ();
63
+ }
64
+
65
+ @ Test
66
+ public void shouldDecorateFutureAndThrowExecutionException () throws Exception {
67
+ CircuitBreaker circuitBreaker = CircuitBreaker .ofDefaults ("testName" );
68
+
69
+ final Future <String > future = mock (Future .class );
70
+ when (future .get ()).thenThrow (new ExecutionException (new RuntimeException ("BAM!" )));
71
+
72
+ CircuitBreakerFuture <String > decoratedFuture = new CircuitBreakerFuture <>(circuitBreaker , future );
73
+
74
+ Throwable thrown = catchThrowable (() -> decoratedFuture .get ());
75
+
76
+ assertThat (thrown ).isInstanceOf (ExecutionException .class )
77
+ .hasCauseInstanceOf (RuntimeException .class );
78
+
79
+ assertThat (circuitBreaker .getMetrics ().getNumberOfBufferedCalls ()).isEqualTo (1 );
80
+ assertThat (circuitBreaker .getMetrics ().getNumberOfFailedCalls ()).isEqualTo (1 );
81
+ assertThat (circuitBreaker .getMetrics ().getNumberOfSuccessfulCalls ()).isEqualTo (0 );
82
+ assertThat (circuitBreaker .getMetrics ().getNumberOfNotPermittedCalls ()).isEqualTo (0 );
83
+ assertThat (circuitBreaker .getState ()).isEqualTo (CircuitBreaker .State .CLOSED );
84
+
85
+ then (future ).should ().get ();
86
+ }
87
+
88
+ @ Test
89
+ public void shouldDecorateFutureAndThrowTimeoutException () throws Exception {
90
+ CircuitBreaker circuitBreaker = CircuitBreaker .ofDefaults ("testName" );
91
+
92
+ final Future <String > future = mock (Future .class );
93
+ when (future .get (anyLong (), any (TimeUnit .class ))).thenThrow (new TimeoutException ());
94
+
95
+ CircuitBreakerFuture <String > decoratedFuture = new CircuitBreakerFuture <>(circuitBreaker , future );
96
+
97
+ Throwable thrown = catchThrowable (() -> decoratedFuture .get (5 , TimeUnit .SECONDS ));
98
+
99
+ assertThat (thrown ).isInstanceOf (TimeoutException .class );
100
+
101
+ assertThat (circuitBreaker .getMetrics ().getNumberOfBufferedCalls ()).isEqualTo (1 );
102
+ assertThat (circuitBreaker .getMetrics ().getNumberOfFailedCalls ()).isEqualTo (1 );
103
+ assertThat (circuitBreaker .getMetrics ().getNumberOfSuccessfulCalls ()).isEqualTo (0 );
104
+ assertThat (circuitBreaker .getMetrics ().getNumberOfNotPermittedCalls ()).isEqualTo (0 );
105
+ assertThat (circuitBreaker .getState ()).isEqualTo (CircuitBreaker .State .CLOSED );
106
+
107
+ then (future ).should ().get (anyLong (), any (TimeUnit .class ));
108
+ }
109
+
110
+ @ Test
111
+ public void shouldDecorateFutureAndCallerRequestCancelled () throws Exception {
112
+ CircuitBreaker circuitBreaker = CircuitBreaker .ofDefaults ("testName" );
113
+
114
+ final Future <String > future = mock (Future .class );
115
+ when (future .get ()).thenThrow (new CancellationException ());
116
+
117
+ CircuitBreakerFuture <String > decoratedFuture = new CircuitBreakerFuture <>(circuitBreaker , future );
118
+ Throwable thrown = catchThrowable (() -> decoratedFuture .get ());
119
+
120
+ assertThat (thrown ).isInstanceOf (CancellationException .class );
121
+
122
+ assertThat (circuitBreaker .getMetrics ().getNumberOfBufferedCalls ()).isEqualTo (0 );
123
+ assertThat (circuitBreaker .getMetrics ().getNumberOfFailedCalls ()).isEqualTo (0 );
124
+ assertThat (circuitBreaker .getMetrics ().getNumberOfSuccessfulCalls ()).isEqualTo (0 );
125
+ assertThat (circuitBreaker .getMetrics ().getNumberOfNotPermittedCalls ()).isEqualTo (0 );
126
+ assertThat (circuitBreaker .getState ()).isEqualTo (CircuitBreaker .State .CLOSED );
127
+
128
+ then (future ).should ().get ();
129
+ }
130
+
131
+ @ Test
132
+ public void shouldDecorateFutureAndInterruptedExceptionThrownByTaskThread () throws Exception {
133
+ CircuitBreaker circuitBreaker = CircuitBreaker .ofDefaults ("testName" );
134
+
135
+ final Future <String > future = mock (Future .class );
136
+ when (future .get (anyLong (), any (TimeUnit .class ))).thenThrow (new ExecutionException (new InterruptedException ()));
137
+
138
+ CircuitBreakerFuture <String > decoratedFuture = new CircuitBreakerFuture <>(circuitBreaker , future );
139
+ Throwable thrown = catchThrowable (() -> decoratedFuture .get (5 , TimeUnit .SECONDS ));
140
+
141
+ //If interrupt is called on the Task thread than InterruptedException is thrown wrapped in
142
+ // ExecutionException where as if current thread gets interrupted it throws
143
+ // InterruptedException directly.
144
+ assertThat (thrown ).isInstanceOf (ExecutionException .class ).hasCauseInstanceOf (InterruptedException .class );
145
+
146
+ assertThat (circuitBreaker .getMetrics ().getNumberOfBufferedCalls ()).isEqualTo (1 );
147
+ assertThat (circuitBreaker .getMetrics ().getNumberOfFailedCalls ()).isEqualTo (1 );
148
+ assertThat (circuitBreaker .getMetrics ().getNumberOfSuccessfulCalls ()).isEqualTo (0 );
149
+ assertThat (circuitBreaker .getMetrics ().getNumberOfNotPermittedCalls ()).isEqualTo (0 );
150
+ assertThat (circuitBreaker .getState ()).isEqualTo (CircuitBreaker .State .CLOSED );
151
+
152
+ then (future ).should ().get (anyLong (), any (TimeUnit .class ));
153
+ }
154
+
155
+ @ Test
156
+ public void shouldDecorateFutureAndInterruptedExceptionThrownByCallingThread () throws Exception {
157
+ CircuitBreaker circuitBreaker = CircuitBreaker .ofDefaults ("testName" );
158
+
159
+ //long running task
160
+ final Future <String > future = mock (Future .class );
161
+ when (future .get ()).thenThrow (new InterruptedException ());
162
+
163
+ CircuitBreakerFuture <String > decoratedFuture = new CircuitBreakerFuture <>(circuitBreaker , future );
164
+
165
+ Throwable thrown = catchThrowable (() -> decoratedFuture .get ());
166
+ //If interrupt is called on the Task thread than InterruptedException is thrown wrapped in
167
+ // ExecutionException where as if current thread gets interrupted it throws
168
+ // InterruptedException directly.
169
+ assertThat (thrown ).isInstanceOf (InterruptedException .class );
170
+
171
+ assertThat (circuitBreaker .getMetrics ().getNumberOfBufferedCalls ()).isEqualTo (0 );
172
+ assertThat (circuitBreaker .getMetrics ().getNumberOfFailedCalls ()).isEqualTo (0 );
173
+ assertThat (circuitBreaker .getMetrics ().getNumberOfSuccessfulCalls ()).isEqualTo (0 );
174
+ assertThat (circuitBreaker .getMetrics ().getNumberOfNotPermittedCalls ()).isEqualTo (0 );
175
+ assertThat (circuitBreaker .getState ()).isEqualTo (CircuitBreaker .State .CLOSED );
176
+
177
+ then (future ).should ().get ();
178
+ }
179
+
180
+ }
0 commit comments