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