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.assertThatThrownBy;
27 import static org.junit.jupiter.api.Assertions.assertEquals;
28 import static org.junit.jupiter.api.Assertions.assertNotNull;
29 import static org.junit.jupiter.api.Assertions.assertNull;
30 import static org.junit.jupiter.api.Assertions.assertSame;
31 import static org.mockito.ArgumentMatchers.any;
32 import static org.mockito.Mockito.mock;
33 import static org.mockito.Mockito.verify;
34 import static org.mockito.Mockito.when;
36 import java.time.Instant;
37 import java.util.ArrayList;
38 import java.util.List;
40 import java.util.TreeMap;
41 import java.util.UUID;
42 import java.util.concurrent.ExecutorService;
43 import java.util.concurrent.atomic.AtomicReference;
44 import org.drools.core.WorkingMemory;
45 import org.drools.core.common.InternalFactHandle;
46 import org.junit.jupiter.api.BeforeEach;
47 import org.junit.jupiter.api.Test;
48 import org.onap.policy.common.utils.coder.Coder;
49 import org.onap.policy.common.utils.coder.CoderException;
50 import org.onap.policy.common.utils.coder.StandardYamlCoder;
51 import org.onap.policy.common.utils.resources.ResourceUtils;
52 import org.onap.policy.controlloop.ControlLoopEventStatus;
53 import org.onap.policy.controlloop.ControlLoopException;
54 import org.onap.policy.controlloop.ControlLoopTargetType;
55 import org.onap.policy.controlloop.VirtualControlLoopEvent;
56 import org.onap.policy.controlloop.VirtualControlLoopNotification;
57 import org.onap.policy.controlloop.actorserviceprovider.ActorService;
58 import org.onap.policy.controlloop.actorserviceprovider.Operation;
59 import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
60 import org.onap.policy.controlloop.actorserviceprovider.OperationResult;
61 import org.onap.policy.controlloop.actorserviceprovider.Operator;
62 import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
63 import org.onap.policy.controlloop.actorserviceprovider.spi.Actor;
64 import org.onap.policy.controlloop.drl.legacy.ControlLoopParams;
65 import org.onap.policy.controlloop.eventmanager.ClEventManagerWithEvent.NewEventStatus;
66 import org.onap.policy.controlloop.ophistory.OperationHistoryDataManager;
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 ClEventManagerWithEventTest {
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 TARGET_PROP = "my-target-property";
83 private static final String MY_TARGET = "my-target";
84 private static final String EVENT_MGR_MULTI_YAML =
85 "../eventmanager/src/test/resources/eventManager/event-mgr-multi.yaml";
86 private static final String EVENT_MGR_SIMPLE_YAML =
87 "../eventmanager/src/test/resources/eventManager/event-mgr-simple.yaml";
88 private static final Coder yamlCoder = new StandardYamlCoder();
89 private static final String OUTCOME_MSG = "my outcome message";
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 EventManagerServices services = mock(EventManagerServices.class);
98 private final ActorService actors = mock(ActorService.class);
99 private final OperationHistoryDataManager dataMgr = mock(OperationHistoryDataManager.class);
100 private final ExecutorService executor = mock(ExecutorService.class);
101 private final MyStep stepa = mock(MyStep.class);
102 private final MyStep stepb = mock(MyStep.class);
104 private List<LockImpl> locks;
105 private ToscaPolicy tosca;
106 private ControlLoopParams params;
107 private VirtualControlLoopEvent event;
108 private ClEventManagerWithEvent<MyStep> mgr;
114 public void setUp() throws ControlLoopException, CoderException {
115 when(services.getActorService()).thenReturn(actors);
116 when(services.getDataManager()).thenReturn(dataMgr);
118 when(workMem.getFactHandle(any())).thenReturn(factHandle);
120 event = new VirtualControlLoopEvent();
121 event.setRequestId(REQ_ID);
122 event.setTarget(TARGET_PROP);
123 event.setAai(new TreeMap<>(Map.of(TARGET_PROP, MY_TARGET)));
124 event.setClosedLoopEventStatus(ControlLoopEventStatus.ONSET);
125 event.setClosedLoopControlName(CL_NAME);
126 event.setTargetType(ControlLoopTargetType.VNF);
128 params = new ControlLoopParams();
129 params.setClosedLoopControlName(CL_NAME);
130 params.setPolicyName(POLICY_NAME);
131 params.setPolicyScope(POLICY_SCOPE);
132 params.setPolicyVersion(POLICY_VERSION);
134 loadPolicy(EVENT_MGR_SIMPLE_YAML);
136 locks = new ArrayList<>();
138 mgr = new MyManager(services, params, event, workMem);
142 void testConstructor() {
143 assertEquals(POLICY_NAME, mgr.getPolicyName());
144 assertSame(event, mgr.getEvent());
147 assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
151 assertThatThrownBy(() -> new MyManager(services, params, event, workMem))
152 .isInstanceOf(ControlLoopException.class);
156 void testPopulateNotification() throws Exception {
157 loadPolicy(EVENT_MGR_MULTI_YAML);
158 mgr = new MyManager(services, params, event, workMem);
161 assertNotNull(mgr.makeNotification());
165 mgr.addToHistory(makeCompletedOutcome());
166 mgr.addToHistory(makeCompletedOutcome());
167 mgr.addToHistory(makeCompletedOutcome());
169 // check notification while running
170 VirtualControlLoopNotification notif = mgr.makeNotification();
171 assertThat(notif.getMessage()).contains(SIMPLE_ACTOR);
172 assertThat(notif.getHistory()).hasSize(3);
173 assertThat(notif.getAai()).isEqualTo(event.getAai());
174 assertThat(notif.getClosedLoopAlarmEnd()).isEqualTo(event.getClosedLoopAlarmEnd());
175 assertThat(notif.getClosedLoopAlarmStart()).isEqualTo(event.getClosedLoopAlarmStart());
176 assertThat(notif.getClosedLoopControlName()).isEqualTo(event.getClosedLoopControlName());
177 assertThat(notif.getClosedLoopEventClient()).isEqualTo(event.getClosedLoopEventClient());
178 assertThat(notif.getFrom()).isEqualTo("policy");
179 assertThat(notif.getTarget()).isEqualTo(event.getTarget());
180 assertThat(notif.getTargetType()).isEqualTo(event.getTargetType());
182 // indicate success and load the next policy - should clear the partial history
183 mgr.loadNextPolicy(OperationResult.SUCCESS);
185 // check notification
186 notif = mgr.makeNotification();
187 assertNull(notif.getMessage());
188 assertThat(notif.getHistory()).isEmpty();
190 // add outcomes and check again
191 mgr.addToHistory(makeCompletedOutcome());
192 mgr.addToHistory(makeCompletedOutcome());
194 notif = mgr.makeNotification();
195 assertNotNull(notif.getMessage());
197 // should only have history for last two outcomes
198 assertThat(notif.getHistory()).hasSize(2);
200 // indicate failure - should go to final state
201 mgr.loadNextPolicy(OperationResult.FAILURE);
203 // check notification
204 notif = mgr.makeNotification();
205 assertNull(notif.getMessage());
207 // should be no history
208 assertThat(notif.getHistory()).isEmpty();
211 assertThatThrownBy(() -> mgr.loadNextPolicy(null)).isInstanceOf(NullPointerException.class)
212 .hasMessageContaining("lastResult");
216 void testStoreInDataBase() throws ControlLoopException {
218 OperationOutcome outcome = makeOutcome();
219 mgr.addToHistory(outcome);
221 mgr.storeInDataBase(mgr.getPartialHistory().peekLast(), MY_TARGET);
223 verify(dataMgr).store(REQ_ID.toString(), event.getClosedLoopControlName(), event, MY_TARGET,
224 mgr.getPartialHistory().peekLast().getClOperation());
228 void testMakeControlLoopResponse() {
229 final var outcome = new OperationOutcome();
231 var resp = mgr.makeControlLoopResponse(outcome);
233 assertEquals("DCAE", resp.getTarget());
234 assertEquals(event.getClosedLoopControlName(), resp.getClosedLoopControlName());
235 assertEquals(event.getPolicyName(), resp.getPolicyName());
236 assertEquals(event.getPolicyVersion(), resp.getPolicyVersion());
237 assertEquals(REQ_ID, resp.getRequestId());
238 assertEquals(event.getVersion(), resp.getVersion());
242 void testOnNewEvent() {
243 var event2 = new VirtualControlLoopEvent(event);
244 assertEquals(NewEventStatus.FIRST_ONSET, mgr.onNewEvent(event2));
246 event2.setPayload("other payload");
247 assertEquals(NewEventStatus.SUBSEQUENT_ONSET, mgr.onNewEvent(event2));
248 assertEquals(NewEventStatus.SUBSEQUENT_ONSET, mgr.onNewEvent(event2));
249 assertEquals(NewEventStatus.FIRST_ONSET, mgr.onNewEvent(event));
251 event2.setClosedLoopEventStatus(ControlLoopEventStatus.ABATED);
252 assertEquals(NewEventStatus.FIRST_ABATEMENT, mgr.onNewEvent(event2));
254 assertEquals(NewEventStatus.SUBSEQUENT_ABATEMENT, mgr.onNewEvent(event2));
255 assertEquals(NewEventStatus.SUBSEQUENT_ABATEMENT, mgr.onNewEvent(event2));
257 event2.setClosedLoopEventStatus(null);
258 assertEquals(NewEventStatus.SYNTAX_ERROR, mgr.onNewEvent(event2));
262 void testCheckEventSyntax() {
263 // initially, it's valid
264 assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
266 event.setTarget(null);
267 assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
268 .hasMessage("No target field");
270 // abated supersedes previous errors - so it shouldn't throw an exception
271 event.setClosedLoopEventStatus(ControlLoopEventStatus.ABATED);
272 assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
274 event.setRequestId(null);
275 assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
276 .hasMessage("No request ID");
278 event.setClosedLoopControlName(null);
279 assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
280 .hasMessage("No control loop name");
284 void testValidateStatus() {
285 event.setClosedLoopEventStatus(ControlLoopEventStatus.ONSET);
286 assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
288 event.setClosedLoopEventStatus(ControlLoopEventStatus.ABATED);
289 assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
291 event.setClosedLoopEventStatus(null);
292 assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
293 .hasMessage("Invalid value in closedLoopEventStatus");
296 private void loadPolicy(String fileName) throws CoderException {
298 yamlCoder.decode(ResourceUtils.getResourceAsString(fileName), ToscaServiceTemplate.class);
299 tosca = template.getToscaTopologyTemplate().getPolicies().get(0).values().iterator().next();
301 params.setToscaPolicy(tosca);
304 private OperationOutcome makeCompletedOutcome() {
305 var outcome = makeOutcome();
306 outcome.setEnd(outcome.getStart());
311 private OperationOutcome makeOutcome() {
312 var outcome = new OperationOutcome();
313 outcome.setActor(SIMPLE_ACTOR);
314 outcome.setOperation(SIMPLE_OPERATION);
315 outcome.setMessage(OUTCOME_MSG);
316 outcome.setResult(OperationResult.SUCCESS);
317 outcome.setStart(Instant.now());
318 outcome.setTarget(MY_TARGET);
324 private class MyManager extends ClEventManagerWithEvent<MyStep> {
325 private static final long serialVersionUID = 1L;
327 public MyManager(EventManagerServices services, ControlLoopParams params, VirtualControlLoopEvent event,
328 WorkingMemory workMem) throws ControlLoopException {
330 super(services, params, event, workMem);
334 protected ExecutorService getBlockingExecutor() {
339 protected void makeLock(String targetEntity, String requestId, int holdSec, LockCallback callback) {
340 LockImpl lock = new LockImpl(LockState.ACTIVE, targetEntity, requestId, holdSec, callback);
342 callback.lockAvailable(lock);
346 protected PolicyEngine getPolicyEngineManager() {
351 protected void loadPolicyStep(ControlLoopOperationParams params) {
352 getSteps().add(new MyStep(this, params, getEvent()));
356 private static class MyStep extends Step {
357 public MyStep(StepContext stepContext, ControlLoopOperationParams params, VirtualControlLoopEvent event) {
358 super(params, new AtomicReference<>());