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