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