2 * ============LICENSE_START=======================================================
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
22 package org.onap.policy.controlloop.eventmanager;
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;
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;
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";
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);
103 private List<LockImpl> locks;
104 private ToscaPolicy tosca;
105 private ControlLoopParams params;
106 private ClEventManagerWithSteps<MyStep> mgr;
112 public void setUp() throws ControlLoopException, CoderException {
113 when(services.getActorService()).thenReturn(actors);
115 when(workMem.getFactHandle(any())).thenReturn(factHandle);
117 params = new ControlLoopParams();
118 params.setClosedLoopControlName(CL_NAME);
119 params.setPolicyName(POLICY_NAME);
120 params.setPolicyScope(POLICY_SCOPE);
121 params.setPolicyVersion(POLICY_VERSION);
123 loadPolicy(EVENT_MGR_SIMPLE_YAML);
125 locks = new ArrayList<>();
127 mgr = new MyManager(services, params, REQ_ID, workMem);
131 void testConstructor() {
132 assertEquals(POLICY_NAME, mgr.getPolicyName());
135 assertThatThrownBy(() -> new MyManager(services, params, null, workMem))
136 .isInstanceOf(ControlLoopException.class);
140 void testDestroy_testGetSteps() {
141 // add some steps to the queue
142 mgr.getSteps().add(stepa);
143 mgr.getSteps().add(stepb);
147 verify(stepa).cancel();
148 verify(stepb).cancel();
150 // if superclass destroy() was invoked, then freeLock() should have been submitted
152 verify(executor).execute(any());
156 void testOnStart() throws ControlLoopException {
157 var outcome = makeOutcome();
160 mgr.onStart(outcome);
162 assertSame(outcome, mgr.getOutcomes().poll());
163 assertThat(mgr.getOutcomes()).isEmpty();
165 verify(workMem).update(factHandle, mgr);
169 void testOnComplete() throws ControlLoopException {
170 var outcome = makeCompletedOutcome();
173 mgr.onComplete(outcome);
175 assertSame(outcome, mgr.getOutcomes().poll());
176 assertThat(mgr.getOutcomes()).isEmpty();
178 verify(workMem).update(factHandle, mgr);
182 void testToString() {
183 assertNotNull(mgr.toString());
187 void testStart() throws ControlLoopException {
192 assertThatCode(() -> mgr.start()).isInstanceOf(IllegalStateException.class)
193 .hasMessage("manager already started");
197 * Tests start() when the manager is not in working memory.
200 void testStartNotInWorkingMemory() throws ControlLoopException {
201 when(workMem.getFactHandle(any())).thenReturn(null);
203 assertThatCode(() -> mgr.start()).isInstanceOf(IllegalStateException.class)
204 .hasMessage("manager is not in working memory");
208 * Tests start() when the manager is not active.
211 void testStartInactive() throws Exception {
212 // make an inactive manager by deserializing it
213 mgr = Serializer.roundTrip(new RealManager(services, params, REQ_ID, workMem));
216 assertThatCode(() -> mgr.start()).isInstanceOf(IllegalStateException.class)
217 .hasMessage("manager is no longer active");
222 mgr.abort(ClEventManagerWithSteps.State.DONE, OperationFinalResult.FINAL_FAILURE_GUARD, "some message");
224 assertEquals(ClEventManagerWithSteps.State.DONE, mgr.getState());
225 assertEquals(OperationFinalResult.FINAL_FAILURE_GUARD, mgr.getFinalResult());
226 assertEquals("some message", mgr.getFinalMessage());
229 assertThatThrownBy(() -> mgr.abort(null, OperationFinalResult.FINAL_FAILURE_GUARD, ""))
230 .isInstanceOf(NullPointerException.class).hasMessageContaining("finalState");
234 public void testLoadNextPolicy() throws Exception {
235 loadPolicy(EVENT_MGR_MULTI_YAML);
236 mgr = new MyManager(services, params, REQ_ID, workMem);
238 // start and load step for first policy
240 assertEquals("OperationA", mgr.getSteps().poll().getOperationName());
241 assertNull(mgr.getFinalResult());
243 // indicate success and load next policy
244 mgr.loadNextPolicy(OperationResult.SUCCESS);
245 assertEquals("OperationB", mgr.getSteps().poll().getOperationName());
246 assertNull(mgr.getFinalResult());
248 // indicate failure - should go to final failure
249 mgr.loadNextPolicy(OperationResult.FAILURE);
250 assertEquals(OperationFinalResult.FINAL_FAILURE, mgr.getFinalResult());
254 void testLoadPolicy() throws ControlLoopException {
255 // start() will invoke loadPolicy()
258 assertNull(mgr.getFinalResult());
260 var step = mgr.getSteps().peek();
262 assertEquals("First", step.getActorName());
263 assertEquals("OperationA", step.getOperationName());
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());
279 void testLoadPreprocessorSteps() {
280 stepa = new MyStep(mgr, ControlLoopOperationParams.builder().build()) {
282 protected Operation buildOperation() {
283 return policyOperation;
287 var steps = mgr.getSteps();
291 mgr.loadPreprocessorSteps();
293 // no additional steps should have been loaded
294 assertThat(steps).hasSize(2);
296 assertSame(stepa, steps.poll());
297 assertSame(stepb, steps.poll());
298 assertThat(steps).isEmpty();
300 assertNotNull(stepa.getOperation());
301 assertNull(stepb.getOperation());
305 * Tests loadPreprocessorSteps() when there are too many steps in the queue.
308 void testLoadPreprocessorStepsTooManySteps() {
309 stepa = new MyStep(mgr, ControlLoopOperationParams.builder().build()) {
311 protected Operation buildOperation() {
312 return policyOperation;
316 var steps = mgr.getSteps();
318 // load up a bunch of steps
319 for (int nsteps = 0; nsteps < ClEventManagerWithSteps.MAX_STEPS; ++nsteps) {
324 assertThatIllegalStateException().isThrownBy(() -> mgr.loadPreprocessorSteps()).withMessage("too many steps");
326 // add another step, should still fail
328 assertThatIllegalStateException().isThrownBy(() -> mgr.loadPreprocessorSteps()).withMessage("too many steps");
330 // remove two steps - should now succeed
334 int nsteps = steps.size();
336 mgr.loadPreprocessorSteps();
337 assertEquals(nsteps, steps.size());
341 void testExecuteStep() {
342 // no steps to execute
343 assertFalse(mgr.executeStep());
345 // add a step to the queue
346 mgr.getSteps().add(stepa);
348 // step returns false
349 when(stepa.start(anyLong())).thenReturn(false);
350 assertFalse(mgr.executeStep());
353 when(stepa.start(anyLong())).thenReturn(true);
354 assertTrue(mgr.executeStep());
358 void testNextStep() {
359 mgr.getSteps().add(stepa);
363 assertThat(mgr.getSteps()).isEmpty();
368 mgr.deliver(MY_SINK, null, "null notification", "null rule");
369 verify(engineMgr, never()).deliver(any(), any());
371 mgr.deliver(MY_SINK, "publishA", "A notification", "A rule");
372 verify(engineMgr).deliver(MY_SINK, "publishA");
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();
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();
383 params.setToscaPolicy(tosca);
386 private OperationOutcome makeCompletedOutcome() {
387 var outcome = makeOutcome();
388 outcome.setEnd(outcome.getStart());
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);
406 private class MyManager extends ClEventManagerWithSteps<MyStep> {
407 private static final long serialVersionUID = 1L;
409 public MyManager(EventManagerServices services, ControlLoopParams params, UUID requestId, WorkingMemory workMem)
410 throws ControlLoopException {
412 super(services, params, requestId, workMem);
416 protected ExecutorService getBlockingExecutor() {
421 protected void makeLock(String targetEntity, String requestId, int holdSec, LockCallback callback) {
422 var lock = new LockImpl(LockState.ACTIVE, targetEntity, requestId, holdSec, callback);
424 callback.lockAvailable(lock);
428 protected PolicyEngine getPolicyEngineManager() {
433 protected void loadPolicyStep(ControlLoopOperationParams params) {
434 getSteps().add(new MyStep(this, params));
439 private static class RealManager extends ClEventManagerWithSteps<MyStep> {
440 private static final long serialVersionUID = 1L;
442 public RealManager(EventManagerServices services, ControlLoopParams params, UUID requestId,
443 WorkingMemory workMem) throws ControlLoopException {
445 super(services, params, requestId, workMem);
449 protected void loadPolicyStep(ControlLoopOperationParams params) {
450 getSteps().add(new MyStep(this, params));
454 private static class MyStep extends Step {
455 public MyStep(StepContext stepContext, ControlLoopOperationParams params) {
456 super(params, new AtomicReference<>());