View Javadoc
1   package net.secodo.jcircuitbreaker.breakhandler;
2   
3   import static org.hamcrest.CoreMatchers.equalTo;
4   
5   import static org.junit.Assert.assertThat;
6   
7   import static org.mockito.Mockito.mock;
8   
9   import java.lang.reflect.Field;
10  import java.util.Map;
11  import java.util.concurrent.ConcurrentHashMap;
12  import java.util.concurrent.atomic.AtomicInteger;
13  
14  import net.secodo.jcircuitbreaker.breaker.ContextAwareCircuitBreaker;
15  import net.secodo.jcircuitbreaker.breaker.execution.ExecutionContext;
16  import net.secodo.jcircuitbreaker.breaker.execution.impl.DefaultExecutionContextImpl;
17  import net.secodo.jcircuitbreaker.breakhandler.impl.ReturnStaticValueHandler;
18  import net.secodo.jcircuitbreaker.breakstrategy.BreakStrategy;
19  
20  import net.secodo.jcircuitbreaker.exception.TaskExecutionException;
21  import net.secodo.jcircuitbreaker.task.Task;
22  import org.junit.Test;
23  
24  
25  public class OnePerExecutionHandlerFactoryTest {
26    @Test
27    public void shouldCreateAnUseOnlyOnInstanceOfBreakHandlerPerExecutionContext() throws Exception {
28      // given
29      ExecutionContext<Long> executionContext = new DefaultExecutionContextImpl<>(new ConcurrentHashMap<>(), null);
30  
31      AtomicInteger counter = new AtomicInteger();
32  
33      OnePerExecutionHandlerFactory<Long> breakHandler = ((task, context) -> {
34          counter.incrementAndGet();
35          return new ReturnStaticValueHandler<>(counter.longValue());
36        });
37  
38      // when
39      final Long value1 = breakHandler.onBreak(mock(ContextAwareCircuitBreaker.class),
40        mock(Task.class),
41        mock(BreakStrategy.class),
42        executionContext);
43  
44      final Long value2 = breakHandler.onBreak(mock(ContextAwareCircuitBreaker.class),
45        mock(Task.class),
46        mock(BreakStrategy.class),
47        executionContext);
48  
49      final Long value3 = breakHandler.onBreak(mock(ContextAwareCircuitBreaker.class),
50        mock(Task.class),
51        mock(BreakStrategy.class),
52        executionContext);
53  
54      // then
55  
56      // the same static value handler should be returned all the time (because it
57      // already in current context), so the value of first such handler is one as defined by "counter" variable
58      assertThat(value1, equalTo(1L));
59      assertThat(value2, equalTo(1L));
60      assertThat(value3, equalTo(1L));
61  
62  
63      // NOW TEST WITH NEW CONTEXT
64  
65      // given
66  
67      executionContext = new DefaultExecutionContextImpl<>(new ConcurrentHashMap<>(), null);
68  
69      // when
70      final Long value4 = breakHandler.onBreak(mock(ContextAwareCircuitBreaker.class),
71        mock(Task.class),
72        mock(BreakStrategy.class),
73        executionContext);
74  
75      final Long value5 = breakHandler.onBreak(mock(ContextAwareCircuitBreaker.class),
76        mock(Task.class),
77        mock(BreakStrategy.class),
78        executionContext);
79  
80      final Long value6 = breakHandler.onBreak(mock(ContextAwareCircuitBreaker.class),
81        mock(Task.class),
82        mock(BreakStrategy.class),
83        executionContext);
84  
85      // then
86  
87      // the same static value handler should be returned all the time (because it
88      // already in current context), new handler should have been created for the first call because new execution
89      // context was supplied - and because of this the value od  "counter" variable should have been increased to 2
90      assertThat(value4, equalTo(2L));
91      assertThat(value5, equalTo(2L));
92      assertThat(value6, equalTo(2L));
93    }
94  
95    @Test
96    public void shouldUseDifferentContextNamesForEachImplementationOfOnePerExecutionHandlerFactory()
97      throws TaskExecutionException, NoSuchFieldException, IllegalAccessException {
98      // given
99      class Factory1 implements OnePerExecutionHandlerFactory<String> {
100       @Override
101       public BreakHandler<String> createNewHandler(Task<String> task, ExecutionContext<String> executionContext) {
102         return (circuitBreaker, task1, breakStrategy, executionContext1) -> "ABC";
103       }
104     }
105 
106     class Factory2 implements OnePerExecutionHandlerFactory<String> {
107       @Override
108       public BreakHandler<String> createNewHandler(Task<String> task, ExecutionContext<String> executionContext) {
109         return (circuitBreaker, task1, breakStrategy, executionContext1) -> "XYZ";
110       }
111     }
112 
113     Factory1 breakHandlerFactory1 = new Factory1();
114     Factory2 breakHandlerFactory2 = new Factory2();
115 
116     DefaultExecutionContextImpl<String> executionContext = new DefaultExecutionContextImpl<>(new ConcurrentHashMap<>(), null);
117 
118     // when - then
119     breakHandlerFactory1.onBreak(mock(ContextAwareCircuitBreaker.class), mock(Task.class), mock(BreakStrategy.class),
120       executionContext);
121 
122     assertThat(getContextAttributes(executionContext).size(), equalTo(1));
123 
124     // when - then
125     breakHandlerFactory1.onBreak(mock(ContextAwareCircuitBreaker.class), mock(Task.class), mock(BreakStrategy.class),
126       executionContext); // another call to breakHandlerFactory1 in the same executionContext should use the same name
127 
128     assertThat(getContextAttributes(executionContext).size(), equalTo(1));
129 
130     // ============= now for second break handler factory ========== //
131 
132     // when - then
133     breakHandlerFactory2.onBreak(mock(ContextAwareCircuitBreaker.class), mock(Task.class), mock(BreakStrategy.class),
134       executionContext); // call to another factory should use different execution context variable name...
135 
136     assertThat(getContextAttributes(executionContext).size(), equalTo(2)); // ... so there should be two attributes in context
137 
138     // when - then
139     breakHandlerFactory2.onBreak(mock(ContextAwareCircuitBreaker.class), mock(Task.class), mock(BreakStrategy.class),
140       executionContext);
141 
142     assertThat(getContextAttributes(executionContext).size(), equalTo(2)); // no change.. still two factories
143 
144 
145   }
146 
147   private Map<String, Object> getContextAttributes(DefaultExecutionContextImpl defaultExecutionContext)
148     throws NoSuchFieldException, IllegalAccessException {
149 
150     Field contextAttributesField = DefaultExecutionContextImpl.class.getDeclaredField("contextAttributes");
151     contextAttributesField.setAccessible(true);
152     return (Map<String, Object>) contextAttributesField.get(defaultExecutionContext);
153   }
154 
155 
156 
157 }
158