ece13611b45cc905725efc6904af87db3f6762a1
[policy/drools-applications.git] / controlloop / common / eventmanager / src / test / java / org / onap / policy / controlloop / eventmanager / ControlLoopEventManagerTest.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP
4  * ================================================================================
5  * Copyright (C) 2020-2021 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.policy.controlloop.eventmanager;
22
23 import static org.assertj.core.api.Assertions.assertThat;
24 import static org.assertj.core.api.Assertions.assertThatCode;
25 import static org.junit.Assert.assertEquals;
26 import static org.junit.Assert.assertFalse;
27 import static org.junit.Assert.assertNotNull;
28 import static org.junit.Assert.assertSame;
29 import static org.junit.Assert.assertTrue;
30 import static org.mockito.Mockito.verify;
31 import static org.mockito.Mockito.when;
32
33 import java.io.IOException;
34 import java.util.ArrayList;
35 import java.util.List;
36 import java.util.UUID;
37 import java.util.concurrent.CompletableFuture;
38 import java.util.concurrent.ExecutorService;
39 import org.junit.Before;
40 import org.junit.Test;
41 import org.junit.runner.RunWith;
42 import org.mockito.ArgumentCaptor;
43 import org.mockito.Mock;
44 import org.mockito.junit.MockitoJUnitRunner;
45 import org.onap.policy.common.utils.coder.Coder;
46 import org.onap.policy.common.utils.coder.CoderException;
47 import org.onap.policy.common.utils.coder.StandardYamlCoder;
48 import org.onap.policy.common.utils.io.Serializer;
49 import org.onap.policy.common.utils.resources.ResourceUtils;
50 import org.onap.policy.controlloop.ControlLoopException;
51 import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
52 import org.onap.policy.controlloop.actorserviceprovider.OperationResult;
53 import org.onap.policy.controlloop.drl.legacy.ControlLoopParams;
54 import org.onap.policy.controlloop.ophistory.OperationHistoryDataManager;
55 import org.onap.policy.controlloop.ophistory.OperationHistoryDataManagerStub;
56 import org.onap.policy.drools.core.lock.LockCallback;
57 import org.onap.policy.drools.core.lock.LockImpl;
58 import org.onap.policy.drools.core.lock.LockState;
59 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
60 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
61
62 @RunWith(MockitoJUnitRunner.class)
63 public class ControlLoopEventManagerTest {
64     private static final UUID REQ_ID = UUID.randomUUID();
65     private static final String EXPECTED_EXCEPTION = "expected exception";
66     private static final String CL_NAME = "my-closed-loop-name";
67     private static final String POLICY_NAME = "my-policy-name";
68     private static final String POLICY_SCOPE = "my-scope";
69     private static final String POLICY_VERSION = "1.2.3";
70     private static final String LOCK1 = "my-lock-A";
71     private static final String LOCK2 = "my-lock-B";
72     private static final Coder yamlCoder = new StandardYamlCoder();
73     private static final String MY_KEY = "def";
74
75     @Mock
76     private ExecutorService executor;
77     @Mock
78     private EventManagerServices services;
79     @Mock
80     private OperationHistoryDataManager dataMgr;
81
82     private long preCreateTimeMs;
83     private List<LockImpl> locks;
84     private ToscaPolicy tosca;
85     private ControlLoopParams params;
86     private ControlLoopEventManager mgr;
87
88     /**
89      * Sets up.
90      */
91     @Before
92     public void setUp() throws ControlLoopException, CoderException {
93         when(services.getDataManager()).thenReturn(dataMgr);
94
95         params = new ControlLoopParams();
96         params.setClosedLoopControlName(CL_NAME);
97         params.setPolicyName(POLICY_NAME);
98         params.setPolicyScope(POLICY_SCOPE);
99         params.setPolicyVersion(POLICY_VERSION);
100
101         loadPolicy("eventManager/event-mgr-simple.yaml");
102
103         locks = new ArrayList<>();
104
105         preCreateTimeMs = System.currentTimeMillis();
106
107         MyManager.executor = executor;
108         MyManager.locks = locks;
109
110         mgr = new MyManager(services, params, REQ_ID);
111     }
112
113     @Test
114     public void testConstructor() {
115         assertEquals(POLICY_NAME, mgr.getPolicyName());
116
117         assertTrue(mgr.isActive());
118         assertEquals(CL_NAME, mgr.getClosedLoopControlName());
119         assertSame(REQ_ID, mgr.getRequestId());
120         assertEquals(POLICY_NAME, mgr.getPolicyName());
121         assertEquals(POLICY_VERSION, mgr.getPolicyVersion());
122         assertNotNull(mgr.getProcessor());
123         assertThat(mgr.getEndTimeMs()).isGreaterThanOrEqualTo(preCreateTimeMs);
124     }
125
126     @Test
127     public void testGetCreateCount() throws ControlLoopException {
128         long original = ControlLoopEventManager.getCreateCount();
129
130         new MyManager(services, params, REQ_ID);
131         assertEquals(original + 1, ControlLoopEventManager.getCreateCount());
132
133         new MyManager(services, params, REQ_ID);
134         assertEquals(original + 2, ControlLoopEventManager.getCreateCount());
135     }
136
137     @Test
138     public void testIsActive() throws Exception {
139         mgr = new ControlLoopEventManager(services, params, REQ_ID);
140         assertTrue(mgr.isActive());
141
142         ControlLoopEventManager mgr2 = Serializer.roundTrip(mgr);
143         assertFalse(mgr2.isActive());
144     }
145
146     @Test
147     public void testDestroy() throws IOException {
148         mgr.requestLock(LOCK1);
149         mgr.requestLock(LOCK2);
150         mgr.requestLock(LOCK1);
151
152         // ensure destroy() doesn't throw an exception if the object is deserialized
153         ControlLoopEventManager mgr2 = Serializer.roundTrip(mgr);
154         assertThatCode(() -> mgr2.destroy()).doesNotThrowAnyException();
155
156         // locks should not have been freed
157         for (LockImpl lock : locks) {
158             assertFalse(lock.isUnavailable());
159         }
160
161         mgr.destroy();
162
163         runExecutor();
164
165         for (LockImpl lock : locks) {
166             assertTrue(lock.isUnavailable());
167         }
168     }
169
170     @Test
171     public void testDetmControlLoopTimeoutMs() throws Exception {
172         long timeMs = 1200 * 1000L;
173         long end = mgr.getEndTimeMs();
174         assertThat(end).isGreaterThanOrEqualTo(preCreateTimeMs + timeMs).isLessThan(preCreateTimeMs + timeMs + 5000);
175     }
176
177     @Test
178     public void testRequestLock() {
179         final CompletableFuture<OperationOutcome> future1 = mgr.requestLock(LOCK1);
180         assertTrue(mgr.getOutcomes().isEmpty());
181
182         final CompletableFuture<OperationOutcome> future2 = mgr.requestLock(LOCK2);
183         assertTrue(mgr.getOutcomes().isEmpty());
184
185         assertSame(future1, mgr.requestLock(LOCK1));
186         assertTrue(mgr.getOutcomes().isEmpty());
187
188         assertEquals(2, locks.size());
189
190         assertTrue(future1.isDone());
191         assertTrue(future2.isDone());
192
193         // indicate that the first lock failed
194         locks.get(0).notifyUnavailable();
195
196         verifyLock(OperationResult.FAILURE, ActorConstants.LOCK_OPERATION);
197         assertTrue(mgr.getOutcomes().isEmpty());
198     }
199
200     @Test
201     public void testReleaseLock() {
202         mgr.requestLock(LOCK1);
203         mgr.requestLock(LOCK2);
204
205         // release one lock
206         final CompletableFuture<OperationOutcome> future = mgr.releaseLock(LOCK1);
207
208         // asynchronous, thus should not have executed yet
209         assertThat(future.isDone()).isFalse();
210
211         // asynchronous, thus everything should still be locked
212         for (LockImpl lock : locks) {
213             assertThat(lock.isUnavailable()).isFalse();
214         }
215
216         runExecutor();
217
218         verifyLock(OperationResult.SUCCESS, ActorConstants.UNLOCK_OPERATION);
219         assertThat(mgr.getOutcomes()).isEmpty();
220
221         // first lock should have been released, thus no longer available to the manager
222         assertThat(locks.get(0).isUnavailable()).isTrue();
223
224         // second should still be locked
225         assertThat(locks.get(1).isUnavailable()).isFalse();
226     }
227
228     /**
229      * Tests releaseLock() when there is no lock.
230      */
231     @Test
232     public void testReleaseLockNotLocked() {
233         final CompletableFuture<OperationOutcome> future = mgr.releaseLock(LOCK1);
234
235         // lock didn't exist, so the request should already be complete
236         assertThat(future.isDone()).isTrue();
237
238         verifyLock(OperationResult.SUCCESS, ActorConstants.UNLOCK_OPERATION);
239         assertThat(mgr.getOutcomes()).isEmpty();
240     }
241
242     /**
243      * Tests releaseLock() when lock.free() throws an exception.
244      */
245     @Test
246     public void testReleaseLockException() throws ControlLoopException {
247         mgr = new MyManager(services, params, REQ_ID) {
248             private static final long serialVersionUID = 1L;
249
250             @Override
251             protected void makeLock(String targetEntity, String requestId, int holdSec, LockCallback callback) {
252
253                 LockImpl lock = new LockImpl(LockState.ACTIVE, targetEntity, requestId, holdSec, callback) {
254                     private static final long serialVersionUID = 1L;
255
256                     @Override
257                     public boolean free() {
258                         throw new RuntimeException(EXPECTED_EXCEPTION);
259                     }
260                 };
261
262                 locks.add(lock);
263                 callback.lockAvailable(lock);
264             }
265         };
266
267         mgr.requestLock(LOCK1);
268
269         // release the lock
270         final CompletableFuture<OperationOutcome> future = mgr.releaseLock(LOCK1);
271
272         // asynchronous, thus should not have executed yet
273         assertThat(future.isDone()).isFalse();
274
275         runExecutor();
276
277         verifyLock(OperationResult.FAILURE_EXCEPTION, ActorConstants.UNLOCK_OPERATION);
278         assertThat(mgr.getOutcomes()).isEmpty();
279     }
280
281     private void verifyLock(OperationResult result, String lockOperation) {
282         OperationOutcome outcome = mgr.getOutcomes().poll();
283         assertNotNull(outcome);
284         assertEquals(ActorConstants.LOCK_ACTOR, outcome.getActor());
285         assertEquals(lockOperation, outcome.getOperation());
286         assertNotNull(outcome.getEnd());
287         assertTrue(outcome.isFinalOutcome());
288         assertEquals(result, outcome.getResult());
289     }
290
291     @Test
292     public void testOnStart() {
293         OperationOutcome outcome1 = new OperationOutcome();
294         OperationOutcome outcome2 = new OperationOutcome();
295
296         mgr.onStart(outcome1);
297         mgr.onStart(outcome2);
298
299         assertSame(outcome1, mgr.getOutcomes().poll());
300         assertSame(outcome2, mgr.getOutcomes().poll());
301         assertTrue(mgr.getOutcomes().isEmpty());
302     }
303
304     @Test
305     public void testOnComplete() {
306         OperationOutcome outcome1 = new OperationOutcome();
307         OperationOutcome outcome2 = new OperationOutcome();
308
309         mgr.onComplete(outcome1);
310         mgr.onComplete(outcome2);
311
312         assertSame(outcome1, mgr.getOutcomes().poll());
313         assertSame(outcome2, mgr.getOutcomes().poll());
314         assertTrue(mgr.getOutcomes().isEmpty());
315     }
316
317     @Test
318     public void testContains_testGetProperty_testSetProperty_testRemoveProperty() {
319         mgr.setProperty("abc", "a string");
320         mgr.setProperty(MY_KEY, 100);
321
322         assertTrue(mgr.contains(MY_KEY));
323         assertFalse(mgr.contains("ghi"));
324
325         String strValue = mgr.getProperty("abc");
326         assertEquals("a string", strValue);
327
328         int intValue = mgr.getProperty(MY_KEY);
329         assertEquals(100, intValue);
330
331         mgr.removeProperty(MY_KEY);
332         assertFalse(mgr.contains(MY_KEY));
333     }
334
335     /**
336      * Tests getDataManager() when not disabled.
337      */
338     @Test
339     public void testGetDataManagerNotDisabled() throws ControlLoopException {
340         assertThat(mgr.getDataManager()).isSameAs(dataMgr);
341     }
342
343     /**
344      * Tests getDataManager() when guard.disabled=true.
345      */
346     @Test
347     public void testGetDataManagerDisabled() throws ControlLoopException {
348         mgr = new MyManager(services, params, REQ_ID) {
349             private static final long serialVersionUID = 1L;
350
351             @Override
352             protected String getEnvironmentProperty(String propName) {
353                 return ("guard.disabled".equals(propName) ? "true" : null);
354             }
355         };
356
357         assertThat(mgr.getDataManager()).isInstanceOf(OperationHistoryDataManagerStub.class);
358     }
359
360     @Test
361     public void testToString() {
362         assertNotNull(mgr.toString());
363     }
364
365     private void loadPolicy(String fileName) throws CoderException {
366         ToscaServiceTemplate template =
367                         yamlCoder.decode(ResourceUtils.getResourceAsString(fileName), ToscaServiceTemplate.class);
368         tosca = template.getToscaTopologyTemplate().getPolicies().get(0).values().iterator().next();
369
370         params.setToscaPolicy(tosca);
371     }
372
373     private void runExecutor() {
374         ArgumentCaptor<Runnable> runCaptor = ArgumentCaptor.forClass(Runnable.class);
375         verify(executor).execute(runCaptor.capture());
376
377         runCaptor.getValue().run();
378     }
379
380
381     private static class MyManager extends ControlLoopEventManager {
382         private static final long serialVersionUID = 1L;
383
384         private static ExecutorService executor;
385         private static List<LockImpl> locks;
386
387         public MyManager(EventManagerServices services, ControlLoopParams params, UUID requestId)
388                         throws ControlLoopException {
389             super(services, params, requestId);
390         }
391
392         @Override
393         protected ExecutorService getBlockingExecutor() {
394             return executor;
395         }
396
397         @Override
398         protected void makeLock(String targetEntity, String requestId, int holdSec, LockCallback callback) {
399             LockImpl lock = new LockImpl(LockState.ACTIVE, targetEntity, requestId, holdSec, callback);
400             locks.add(lock);
401             callback.lockAvailable(lock);
402         }
403     }
404 }