06dc838e21d52867d0e8348ef4690c7e09e92c8c
[policy/drools-applications.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP
4  * ================================================================================
5  * Copyright (C) 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.assertj.core.api.Assertions.assertThatIllegalStateException;
26 import static org.assertj.core.api.Assertions.assertThatThrownBy;
27 import static org.junit.Assert.assertEquals;
28 import static org.junit.Assert.assertFalse;
29 import static org.junit.Assert.assertNotNull;
30 import static org.junit.Assert.assertNull;
31 import static org.junit.Assert.assertSame;
32 import static org.junit.Assert.assertTrue;
33 import static org.mockito.ArgumentMatchers.any;
34 import static org.mockito.ArgumentMatchers.anyLong;
35 import static org.mockito.Mockito.never;
36 import static org.mockito.Mockito.verify;
37 import static org.mockito.Mockito.when;
38
39 import java.time.Instant;
40 import java.util.ArrayList;
41 import java.util.Deque;
42 import java.util.List;
43 import java.util.UUID;
44 import java.util.concurrent.ExecutorService;
45 import java.util.concurrent.ForkJoinPool;
46 import java.util.concurrent.atomic.AtomicReference;
47 import org.drools.core.WorkingMemory;
48 import org.junit.Before;
49 import org.junit.Test;
50 import org.junit.runner.RunWith;
51 import org.kie.api.runtime.rule.FactHandle;
52 import org.mockito.Mock;
53 import org.mockito.junit.MockitoJUnitRunner;
54 import org.onap.policy.common.utils.coder.Coder;
55 import org.onap.policy.common.utils.coder.CoderException;
56 import org.onap.policy.common.utils.coder.StandardYamlCoder;
57 import org.onap.policy.common.utils.io.Serializer;
58 import org.onap.policy.common.utils.resources.ResourceUtils;
59 import org.onap.policy.controlloop.ControlLoopException;
60 import org.onap.policy.controlloop.actorserviceprovider.ActorService;
61 import org.onap.policy.controlloop.actorserviceprovider.Operation;
62 import org.onap.policy.controlloop.actorserviceprovider.OperationFinalResult;
63 import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
64 import org.onap.policy.controlloop.actorserviceprovider.OperationResult;
65 import org.onap.policy.controlloop.actorserviceprovider.Operator;
66 import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
67 import org.onap.policy.controlloop.actorserviceprovider.spi.Actor;
68 import org.onap.policy.controlloop.drl.legacy.ControlLoopParams;
69 import org.onap.policy.controlloop.ophistory.OperationHistoryDataManager;
70 import org.onap.policy.drools.core.lock.LockCallback;
71 import org.onap.policy.drools.core.lock.LockImpl;
72 import org.onap.policy.drools.core.lock.LockState;
73 import org.onap.policy.drools.system.PolicyEngine;
74 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
75 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
76
77 @RunWith(MockitoJUnitRunner.class)
78 public class ClEventManagerWithStepsTest {
79     private static final UUID REQ_ID = UUID.randomUUID();
80     private static final String CL_NAME = "my-closed-loop-name";
81     private static final String POLICY_NAME = "my-policy-name";
82     private static final String POLICY_SCOPE = "my-scope";
83     private static final String POLICY_VERSION = "1.2.3";
84     private static final String SIMPLE_ACTOR = "First";
85     private static final String SIMPLE_OPERATION = "OperationA";
86     private static final String MY_TARGET = "my-target";
87     private static final String EVENT_MGR_MULTI_YAML =
88                     "../eventmanager/src/test/resources/eventManager/event-mgr-multi.yaml";
89     private static final String EVENT_MGR_SIMPLE_YAML =
90                     "../eventmanager/src/test/resources/eventManager/event-mgr-simple.yaml";
91     private static final Coder yamlCoder = new StandardYamlCoder();
92     private static final String OUTCOME_MSG = "my outcome message";
93     private static final String MY_SINK = "my-topic-sink";
94
95     @Mock
96     private PolicyEngine engineMgr;
97     @Mock
98     private WorkingMemory workMem;
99     @Mock
100     private FactHandle factHandle;
101     @Mock
102     private Operator policyOperator;
103     @Mock
104     private Operation policyOperation;
105     @Mock
106     private Actor policyActor;
107     @Mock
108     private ActorService actors;
109     @Mock
110     private OperationHistoryDataManager dataMgr;
111     @Mock
112     private ExecutorService executor;
113     @Mock
114     private MyStep stepa;
115     @Mock
116     private MyStep stepb;
117
118     private List<LockImpl> locks;
119     private ToscaPolicy tosca;
120     private ControlLoopParams params;
121     private ClEventManagerWithSteps<MyStep> mgr;
122
123     /**
124      * Sets up.
125      */
126     @Before
127     public void setUp() throws ControlLoopException, CoderException {
128         when(workMem.getFactHandle(any())).thenReturn(factHandle);
129
130         params = new ControlLoopParams();
131         params.setClosedLoopControlName(CL_NAME);
132         params.setPolicyName(POLICY_NAME);
133         params.setPolicyScope(POLICY_SCOPE);
134         params.setPolicyVersion(POLICY_VERSION);
135
136         loadPolicy(EVENT_MGR_SIMPLE_YAML);
137
138         locks = new ArrayList<>();
139
140         mgr = new MyManager(params, REQ_ID, workMem);
141     }
142
143     @Test
144     public void testConstructor() {
145         assertEquals(POLICY_NAME, mgr.getPolicyName());
146
147         // invalid
148         assertThatThrownBy(() -> new MyManager(params, null, workMem)).isInstanceOf(ControlLoopException.class);
149     }
150
151     @Test
152     public void testDestroy_testGetSteps() {
153         // add some steps to the queue
154         mgr.getSteps().add(stepa);
155         mgr.getSteps().add(stepb);
156
157         mgr.destroy();
158
159         verify(stepa).cancel();
160         verify(stepb).cancel();
161
162         // if superclass destroy() was invoked, then freeLock() should have been submitted
163         // to the executor
164         verify(executor).execute(any());
165     }
166
167     @Test
168     public void testOnStart() throws ControlLoopException {
169         OperationOutcome outcome = makeOutcome();
170
171         mgr.start();
172         mgr.onStart(outcome);
173
174         assertSame(outcome, mgr.getOutcomes().poll());
175         assertThat(mgr.getOutcomes()).isEmpty();
176
177         verify(workMem).update(factHandle, mgr);
178     }
179
180     @Test
181     public void testOnComplete() throws ControlLoopException {
182         OperationOutcome outcome = makeCompletedOutcome();
183
184         mgr.start();
185         mgr.onComplete(outcome);
186
187         assertSame(outcome, mgr.getOutcomes().poll());
188         assertThat(mgr.getOutcomes()).isEmpty();
189
190         verify(workMem).update(factHandle, mgr);
191     }
192
193     @Test
194     public void testToString() {
195         assertNotNull(mgr.toString());
196     }
197
198     @Test
199     public void testStart() throws ControlLoopException {
200         // start it
201         mgr.start();
202
203         // cannot re-start
204         assertThatCode(() -> mgr.start()).isInstanceOf(IllegalStateException.class)
205                         .hasMessage("manager already started");
206     }
207
208     /**
209      * Tests start() when the manager is not in working memory.
210      */
211     @Test
212     public void testStartNotInWorkingMemory() throws ControlLoopException {
213         when(workMem.getFactHandle(any())).thenReturn(null);
214
215         assertThatCode(() -> mgr.start()).isInstanceOf(IllegalStateException.class)
216                         .hasMessage("manager is not in working memory");
217     }
218
219     /**
220      * Tests start() when the manager is not active.
221      */
222     @Test
223     public void testStartInactive() throws Exception {
224         // make an inactive manager by deserializing it
225         RealManager mgr2 = Serializer.roundTrip(new RealManager(params, REQ_ID, workMem));
226         mgr = mgr2;
227
228         // cannot re-start
229         assertThatCode(() -> mgr.start()).isInstanceOf(IllegalStateException.class)
230                         .hasMessage("manager is no longer active");
231     }
232
233     @Test
234     public void testAbort() {
235         mgr.abort(ClEventManagerWithSteps.State.DONE, OperationFinalResult.FINAL_FAILURE_GUARD, "some message");
236
237         assertEquals(ClEventManagerWithSteps.State.DONE, mgr.getState());
238         assertEquals(OperationFinalResult.FINAL_FAILURE_GUARD, mgr.getFinalResult());
239         assertEquals("some message", mgr.getFinalMessage());
240
241         // try null state
242         assertThatThrownBy(() -> mgr.abort(null, OperationFinalResult.FINAL_FAILURE_GUARD, ""))
243                         .isInstanceOf(NullPointerException.class).hasMessageContaining("finalState");
244     }
245
246     @Test
247     public void testLoadNextPolicy() throws Exception {
248         loadPolicy(EVENT_MGR_MULTI_YAML);
249         mgr = new MyManager(params, REQ_ID, workMem);
250
251         // start and load step for first policy
252         mgr.start();
253         assertEquals("OperationA", mgr.getSteps().poll().getOperationName());
254         assertNull(mgr.getFinalResult());
255
256         // indicate success and load next policy
257         mgr.loadNextPolicy(OperationResult.SUCCESS);
258         assertEquals("OperationB", mgr.getSteps().poll().getOperationName());
259         assertNull(mgr.getFinalResult());
260
261         // indicate failure - should go to final failure
262         mgr.loadNextPolicy(OperationResult.FAILURE);
263         assertEquals(OperationFinalResult.FINAL_FAILURE, mgr.getFinalResult());
264     }
265
266     @Test
267     public void testLoadPolicy() throws ControlLoopException {
268         // start() will invoke loadPolicy()
269         mgr.start();
270
271         assertNull(mgr.getFinalResult());
272
273         MyStep step = mgr.getSteps().peek();
274         assertNotNull(step);
275         assertEquals("First", step.getActorName());
276         assertEquals("OperationA", step.getOperationName());
277
278         ControlLoopOperationParams params2 = step.getParams();
279         assertSame(actors, params2.getActorService());
280         assertSame(REQ_ID, params2.getRequestId());
281         assertSame(ForkJoinPool.commonPool(), params2.getExecutor());
282         assertNotNull(params2.getTargetType());
283         assertNotNull(params2.getTargetEntityIds());
284         assertEquals(Integer.valueOf(300), params2.getTimeoutSec());
285         assertEquals(Integer.valueOf(0), params2.getRetry());
286         assertThat(params2.getPayload()).isEmpty();
287         assertNotNull(params2.getStartCallback());
288         assertNotNull(params2.getCompleteCallback());
289     }
290
291     @Test
292     public void testLoadPreprocessorSteps() {
293         stepa = new MyStep(mgr, ControlLoopOperationParams.builder().build()) {
294             @Override
295             protected Operation buildOperation() {
296                 return policyOperation;
297             }
298         };
299
300         Deque<MyStep> steps = mgr.getSteps();
301         steps.add(stepa);
302         steps.add(stepb);
303
304         mgr.loadPreprocessorSteps();
305
306         // no additional steps should have been loaded
307         assertThat(steps).hasSize(2);
308
309         assertSame(stepa, steps.poll());
310         assertSame(stepb, steps.poll());
311         assertThat(steps).isEmpty();
312
313         assertNotNull(stepa.getOperation());
314         assertNull(stepb.getOperation());
315     }
316
317     /**
318      * Tests loadPreprocessorSteps() when there are too many steps in the queue.
319      */
320     @Test
321     public void testLoadPreprocessorStepsTooManySteps() {
322         stepa = new MyStep(mgr, ControlLoopOperationParams.builder().build()) {
323             @Override
324             protected Operation buildOperation() {
325                 return policyOperation;
326             }
327         };
328
329         Deque<MyStep> steps = mgr.getSteps();
330
331         // load up a bunch of steps
332         for (int nsteps = 0; nsteps < ClEventManagerWithSteps.MAX_STEPS; ++nsteps) {
333             steps.add(stepa);
334         }
335
336         // should fail
337         assertThatIllegalStateException().isThrownBy(() -> mgr.loadPreprocessorSteps()).withMessage("too many steps");
338
339         // add another step, should still fail
340         steps.add(stepa);
341         assertThatIllegalStateException().isThrownBy(() -> mgr.loadPreprocessorSteps()).withMessage("too many steps");
342
343         // remove two steps - should now succeed
344         steps.remove();
345         steps.remove();
346
347         int nsteps = steps.size();
348
349         mgr.loadPreprocessorSteps();
350         assertEquals(nsteps, steps.size());
351     }
352
353     @Test
354     public void testExecuteStep() {
355         // no steps to execute
356         assertFalse(mgr.executeStep());
357
358         // add a step to the queue
359         mgr.getSteps().add(stepa);
360
361         // step returns false
362         when(stepa.start(anyLong())).thenReturn(false);
363         assertFalse(mgr.executeStep());
364
365         // step returns true
366         when(stepa.start(anyLong())).thenReturn(true);
367         assertTrue(mgr.executeStep());
368     }
369
370     @Test
371     public void testNextStep() {
372         mgr.getSteps().add(stepa);
373
374         mgr.nextStep();
375
376         assertThat(mgr.getSteps()).isEmpty();
377     }
378
379     @Test
380     public void testDeliver() {
381         mgr.deliver(MY_SINK, null, "null notification", "null rule");
382         verify(engineMgr, never()).deliver(any(), any());
383
384         mgr.deliver(MY_SINK, "publishA", "A notification", "A rule");
385         verify(engineMgr).deliver(MY_SINK, "publishA");
386
387         // cause deliver() to throw an exception
388         when(engineMgr.deliver(any(), any())).thenThrow(new IllegalStateException("expected exception"));
389         assertThatCode(() -> mgr.deliver(MY_SINK, "publishB", "B notification", "B rule")).doesNotThrowAnyException();
390     }
391
392     private void loadPolicy(String fileName) throws CoderException {
393         ToscaServiceTemplate template =
394                         yamlCoder.decode(ResourceUtils.getResourceAsString(fileName), ToscaServiceTemplate.class);
395         tosca = template.getToscaTopologyTemplate().getPolicies().get(0).values().iterator().next();
396
397         params.setToscaPolicy(tosca);
398     }
399
400     private OperationOutcome makeCompletedOutcome() {
401         OperationOutcome outcome = makeOutcome();
402         outcome.setEnd(outcome.getStart());
403
404         return outcome;
405     }
406
407     private OperationOutcome makeOutcome() {
408         OperationOutcome outcome = new OperationOutcome();
409         outcome.setActor(SIMPLE_ACTOR);
410         outcome.setOperation(SIMPLE_OPERATION);
411         outcome.setMessage(OUTCOME_MSG);
412         outcome.setResult(OperationResult.SUCCESS);
413         outcome.setStart(Instant.now());
414         outcome.setTarget(MY_TARGET);
415
416         return outcome;
417     }
418
419
420     private class MyManager extends ClEventManagerWithSteps<MyStep> {
421         private static final long serialVersionUID = 1L;
422
423         public MyManager(ControlLoopParams params, UUID requestId, WorkingMemory workMem) throws ControlLoopException {
424
425             super(params, requestId, workMem);
426         }
427
428         @Override
429         protected ExecutorService getBlockingExecutor() {
430             return executor;
431         }
432
433         @Override
434         protected void makeLock(String targetEntity, String requestId, int holdSec, LockCallback callback) {
435             LockImpl lock = new LockImpl(LockState.ACTIVE, targetEntity, requestId, holdSec, callback);
436             locks.add(lock);
437             callback.lockAvailable(lock);
438         }
439
440         @Override
441         public ActorService getActorService() {
442             return actors;
443         }
444
445         @Override
446         public OperationHistoryDataManager getDataManager() {
447             return dataMgr;
448         }
449
450         @Override
451         protected PolicyEngine getPolicyEngineManager() {
452             return engineMgr;
453         }
454
455         @Override
456         protected void loadPolicyStep(ControlLoopOperationParams params) {
457             getSteps().add(new MyStep(this, params));
458         }
459     }
460
461
462     private static class RealManager extends ClEventManagerWithSteps<MyStep> {
463         private static final long serialVersionUID = 1L;
464
465         public RealManager(ControlLoopParams params, UUID requestId, WorkingMemory workMem)
466                         throws ControlLoopException {
467
468             super(params, requestId, workMem);
469         }
470
471         @Override
472         protected void loadPolicyStep(ControlLoopOperationParams params) {
473             getSteps().add(new MyStep(this, params));
474         }
475     }
476
477     private static class MyStep extends Step {
478         public MyStep(StepContext stepContext, ControlLoopOperationParams params) {
479             super(params, new AtomicReference<>());
480         }
481     }
482 }