6b60ec84cd8e49ce3b46c76dbed763144ca7b850
[policy/drools-applications.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP
4  * ================================================================================
5  * Copyright (C) 2021, 2023 AT&T Intellectual Property. All rights reserved.
6  * Modifications Copyright (C) 2023-2024 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.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;
35
36 import java.time.Instant;
37 import java.util.ArrayList;
38 import java.util.List;
39 import java.util.Map;
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.OperationOutcome;
59 import org.onap.policy.controlloop.actorserviceprovider.OperationResult;
60 import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
61 import org.onap.policy.controlloop.drl.legacy.ControlLoopParams;
62 import org.onap.policy.controlloop.eventmanager.ClEventManagerWithEvent.NewEventStatus;
63 import org.onap.policy.controlloop.ophistory.OperationHistoryDataManager;
64 import org.onap.policy.drools.core.lock.LockCallback;
65 import org.onap.policy.drools.core.lock.LockImpl;
66 import org.onap.policy.drools.core.lock.LockState;
67 import org.onap.policy.drools.system.PolicyEngine;
68 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
69 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
70
71 class ClEventManagerWithEventTest {
72     private static final UUID REQ_ID = UUID.randomUUID();
73     private static final String CL_NAME = "my-closed-loop-name";
74     private static final String POLICY_NAME = "my-policy-name";
75     private static final String POLICY_SCOPE = "my-scope";
76     private static final String POLICY_VERSION = "1.2.3";
77     private static final String SIMPLE_ACTOR = "First";
78     private static final String SIMPLE_OPERATION = "OperationA";
79     private static final String TARGET_PROP = "my-target-property";
80     private static final String MY_TARGET = "my-target";
81     private static final String EVENT_MGR_MULTI_YAML =
82         "../eventmanager/src/test/resources/eventManager/event-mgr-multi.yaml";
83     private static final String EVENT_MGR_SIMPLE_YAML =
84         "../eventmanager/src/test/resources/eventManager/event-mgr-simple.yaml";
85     private static final Coder yamlCoder = new StandardYamlCoder();
86     private static final String OUTCOME_MSG = "my outcome message";
87
88     private final PolicyEngine engineMgr = mock(PolicyEngine.class);
89     private final WorkingMemory workMem = mock(WorkingMemory.class);
90     private final InternalFactHandle factHandle = mock(InternalFactHandle.class);
91     private final EventManagerServices services = mock(EventManagerServices.class);
92     private final ActorService actors = mock(ActorService.class);
93     private final OperationHistoryDataManager dataMgr = mock(OperationHistoryDataManager.class);
94     private final ExecutorService executor = mock(ExecutorService.class);
95
96     private List<LockImpl> locks;
97     private ControlLoopParams params;
98     private VirtualControlLoopEvent event;
99     private ClEventManagerWithEvent<MyStep> mgr;
100
101     /**
102      * Sets up.
103      */
104     @BeforeEach
105     public void setUp() throws ControlLoopException, CoderException {
106         when(services.getActorService()).thenReturn(actors);
107         when(services.getDataManager()).thenReturn(dataMgr);
108
109         when(workMem.getFactHandle(any())).thenReturn(factHandle);
110
111         event = new VirtualControlLoopEvent();
112         event.setRequestId(REQ_ID);
113         event.setTarget(TARGET_PROP);
114         event.setAai(new TreeMap<>(Map.of(TARGET_PROP, MY_TARGET)));
115         event.setClosedLoopEventStatus(ControlLoopEventStatus.ONSET);
116         event.setClosedLoopControlName(CL_NAME);
117         event.setTargetType(ControlLoopTargetType.VNF);
118
119         params = new ControlLoopParams();
120         params.setClosedLoopControlName(CL_NAME);
121         params.setPolicyName(POLICY_NAME);
122         params.setPolicyScope(POLICY_SCOPE);
123         params.setPolicyVersion(POLICY_VERSION);
124
125         loadPolicy(EVENT_MGR_SIMPLE_YAML);
126
127         locks = new ArrayList<>();
128
129         mgr = new MyManager(services, params, event, workMem);
130     }
131
132     @Test
133     void testConstructor() {
134         assertEquals(POLICY_NAME, mgr.getPolicyName());
135         assertSame(event, mgr.getEvent());
136
137         // valid
138         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
139
140         // invalid
141         event.setTarget("");
142         assertThatThrownBy(() -> new MyManager(services, params, event, workMem))
143             .isInstanceOf(ControlLoopException.class);
144     }
145
146     @Test
147     void testPopulateNotification() throws Exception {
148         loadPolicy(EVENT_MGR_MULTI_YAML);
149         mgr = new MyManager(services, params, event, workMem);
150
151         // before started
152         assertNotNull(mgr.makeNotification());
153
154         mgr.start();
155
156         mgr.addToHistory(makeCompletedOutcome());
157         mgr.addToHistory(makeCompletedOutcome());
158         mgr.addToHistory(makeCompletedOutcome());
159
160         // check notification while running
161         VirtualControlLoopNotification notif = mgr.makeNotification();
162         assertThat(notif.getMessage()).contains(SIMPLE_ACTOR);
163         assertThat(notif.getHistory()).hasSize(3);
164         assertThat(notif.getAai()).isEqualTo(event.getAai());
165         assertThat(notif.getClosedLoopAlarmEnd()).isEqualTo(event.getClosedLoopAlarmEnd());
166         assertThat(notif.getClosedLoopAlarmStart()).isEqualTo(event.getClosedLoopAlarmStart());
167         assertThat(notif.getClosedLoopControlName()).isEqualTo(event.getClosedLoopControlName());
168         assertThat(notif.getClosedLoopEventClient()).isEqualTo(event.getClosedLoopEventClient());
169         assertThat(notif.getFrom()).isEqualTo("policy");
170         assertThat(notif.getTarget()).isEqualTo(event.getTarget());
171         assertThat(notif.getTargetType()).isEqualTo(event.getTargetType());
172
173         // indicate success and load the next policy - should clear the partial history
174         mgr.loadNextPolicy(OperationResult.SUCCESS);
175
176         // check notification
177         notif = mgr.makeNotification();
178         assertNull(notif.getMessage());
179         assertThat(notif.getHistory()).isEmpty();
180
181         // add outcomes and check again
182         mgr.addToHistory(makeCompletedOutcome());
183         mgr.addToHistory(makeCompletedOutcome());
184
185         notif = mgr.makeNotification();
186         assertNotNull(notif.getMessage());
187
188         // should only have history for last two outcomes
189         assertThat(notif.getHistory()).hasSize(2);
190
191         // indicate failure - should go to final state
192         mgr.loadNextPolicy(OperationResult.FAILURE);
193
194         // check notification
195         notif = mgr.makeNotification();
196         assertNull(notif.getMessage());
197
198         // should be no history
199         assertThat(notif.getHistory()).isEmpty();
200
201         // null case
202         assertThatThrownBy(() -> mgr.loadNextPolicy(null)).isInstanceOf(NullPointerException.class)
203             .hasMessageContaining("lastResult");
204     }
205
206     @Test
207     void testStoreInDataBase() throws ControlLoopException {
208         mgr.start();
209         OperationOutcome outcome = makeOutcome();
210         mgr.addToHistory(outcome);
211
212         var peeked = mgr.getPartialHistory().peekLast();
213         assertNotNull(peeked);
214         mgr.storeInDataBase(peeked, MY_TARGET);
215
216         verify(dataMgr).store(REQ_ID.toString(), event.getClosedLoopControlName(), event, MY_TARGET,
217             peeked.getClOperation());
218     }
219
220     @Test
221     void testMakeControlLoopResponse() {
222         final var outcome = new OperationOutcome();
223
224         var resp = mgr.makeControlLoopResponse(outcome);
225         assertNotNull(resp);
226         assertEquals("DCAE", resp.getTarget());
227         assertEquals(event.getClosedLoopControlName(), resp.getClosedLoopControlName());
228         assertEquals(event.getPolicyName(), resp.getPolicyName());
229         assertEquals(event.getPolicyVersion(), resp.getPolicyVersion());
230         assertEquals(REQ_ID, resp.getRequestId());
231         assertEquals(event.getVersion(), resp.getVersion());
232     }
233
234     @Test
235     void testOnNewEvent() {
236         var event2 = new VirtualControlLoopEvent(event);
237         assertEquals(NewEventStatus.FIRST_ONSET, mgr.onNewEvent(event2));
238
239         event2.setPayload("other payload");
240         assertEquals(NewEventStatus.SUBSEQUENT_ONSET, mgr.onNewEvent(event2));
241         assertEquals(NewEventStatus.SUBSEQUENT_ONSET, mgr.onNewEvent(event2));
242         assertEquals(NewEventStatus.FIRST_ONSET, mgr.onNewEvent(event));
243
244         event2.setClosedLoopEventStatus(ControlLoopEventStatus.ABATED);
245         assertEquals(NewEventStatus.FIRST_ABATEMENT, mgr.onNewEvent(event2));
246
247         assertEquals(NewEventStatus.SUBSEQUENT_ABATEMENT, mgr.onNewEvent(event2));
248         assertEquals(NewEventStatus.SUBSEQUENT_ABATEMENT, mgr.onNewEvent(event2));
249
250         event2.setClosedLoopEventStatus(null);
251         assertEquals(NewEventStatus.SYNTAX_ERROR, mgr.onNewEvent(event2));
252     }
253
254     @Test
255     void testCheckEventSyntax() {
256         // initially, it's valid
257         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
258
259         event.setTarget(null);
260         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
261             .hasMessage("No target field");
262
263         // abated supersedes previous errors - so it shouldn't throw an exception
264         event.setClosedLoopEventStatus(ControlLoopEventStatus.ABATED);
265         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
266
267         event.setRequestId(null);
268         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
269             .hasMessage("No request ID");
270
271         event.setClosedLoopControlName(null);
272         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
273             .hasMessage("No control loop name");
274     }
275
276     @Test
277     void testValidateStatus() {
278         event.setClosedLoopEventStatus(ControlLoopEventStatus.ONSET);
279         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
280
281         event.setClosedLoopEventStatus(ControlLoopEventStatus.ABATED);
282         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
283
284         event.setClosedLoopEventStatus(null);
285         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
286             .hasMessage("Invalid value in closedLoopEventStatus");
287     }
288
289     private void loadPolicy(String fileName) throws CoderException {
290         var template = yamlCoder.decode(ResourceUtils.getResourceAsString(fileName), ToscaServiceTemplate.class);
291         ToscaPolicy tosca = template.getToscaTopologyTemplate().getPolicies().get(0).values().iterator().next();
292
293         params.setToscaPolicy(tosca);
294     }
295
296     private OperationOutcome makeCompletedOutcome() {
297         var outcome = makeOutcome();
298         outcome.setEnd(outcome.getStart());
299
300         return outcome;
301     }
302
303     private OperationOutcome makeOutcome() {
304         var outcome = new OperationOutcome();
305         outcome.setActor(SIMPLE_ACTOR);
306         outcome.setOperation(SIMPLE_OPERATION);
307         outcome.setMessage(OUTCOME_MSG);
308         outcome.setResult(OperationResult.SUCCESS);
309         outcome.setStart(Instant.now());
310         outcome.setTarget(MY_TARGET);
311
312         return outcome;
313     }
314
315
316     private class MyManager extends ClEventManagerWithEvent<MyStep> {
317         private static final long serialVersionUID = 1L;
318
319         public MyManager(EventManagerServices services, ControlLoopParams params, VirtualControlLoopEvent event,
320                          WorkingMemory workMem) throws ControlLoopException {
321
322             super(services, params, event, workMem);
323         }
324
325         @Override
326         protected ExecutorService getBlockingExecutor() {
327             return executor;
328         }
329
330         @Override
331         protected void makeLock(String targetEntity, String requestId, int holdSec, LockCallback callback) {
332             LockImpl lock = new LockImpl(LockState.ACTIVE, targetEntity, requestId, holdSec, callback);
333             locks.add(lock);
334             callback.lockAvailable(lock);
335         }
336
337         @Override
338         protected PolicyEngine getPolicyEngineManager() {
339             return engineMgr;
340         }
341
342         @Override
343         protected void loadPolicyStep(ControlLoopOperationParams params) {
344             getSteps().add(new MyStep(this, params, getEvent()));
345         }
346     }
347
348     private static class MyStep extends Step {
349         public MyStep(StepContext stepContext, ControlLoopOperationParams params, VirtualControlLoopEvent event) {
350             super(params, new AtomicReference<>());
351         }
352     }
353 }