17e81044d8d01aa162984bc5d9f66ae30e4d6d25
[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 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.assertThatThrownBy;
26 import static org.junit.jupiter.api.Assertions.assertEquals;
27 import static org.junit.jupiter.api.Assertions.assertFalse;
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.junit.jupiter.api.Assertions.assertTrue;
32 import static org.mockito.ArgumentMatchers.any;
33 import static org.mockito.Mockito.mock;
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.UUID;
40 import java.util.concurrent.ExecutorService;
41 import java.util.concurrent.atomic.AtomicReference;
42 import org.drools.core.WorkingMemory;
43 import org.drools.core.common.InternalFactHandle;
44 import org.junit.jupiter.api.BeforeEach;
45 import org.junit.jupiter.api.Test;
46 import org.onap.policy.common.utils.coder.Coder;
47 import org.onap.policy.common.utils.coder.CoderException;
48 import org.onap.policy.common.utils.coder.StandardYamlCoder;
49 import org.onap.policy.common.utils.resources.ResourceUtils;
50 import org.onap.policy.controlloop.ControlLoopException;
51 import org.onap.policy.controlloop.actorserviceprovider.Operation;
52 import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
53 import org.onap.policy.controlloop.actorserviceprovider.OperationResult;
54 import org.onap.policy.controlloop.actorserviceprovider.Operator;
55 import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
56 import org.onap.policy.controlloop.actorserviceprovider.spi.Actor;
57 import org.onap.policy.controlloop.drl.legacy.ControlLoopParams;
58 import org.onap.policy.drools.core.lock.LockCallback;
59 import org.onap.policy.drools.core.lock.LockImpl;
60 import org.onap.policy.drools.core.lock.LockState;
61 import org.onap.policy.drools.system.PolicyEngine;
62 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
63 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
64
65 class ClEventManagerWithOutcomeTest {
66     private static final UUID REQ_ID = UUID.randomUUID();
67     private static final String CL_NAME = "my-closed-loop-name";
68     private static final String POLICY_NAME = "my-policy-name";
69     private static final String POLICY_SCOPE = "my-scope";
70     private static final String POLICY_VERSION = "1.2.3";
71     private static final String SIMPLE_ACTOR = "First";
72     private static final String SIMPLE_OPERATION = "OperationA";
73     private static final String MY_TARGET = "my-target";
74     private static final String EVENT_MGR_MULTI_YAML =
75                     "../eventmanager/src/test/resources/eventManager/event-mgr-multi.yaml";
76     private static final String EVENT_MGR_SIMPLE_YAML =
77                     "../eventmanager/src/test/resources/eventManager/event-mgr-simple.yaml";
78     private static final Coder yamlCoder = new StandardYamlCoder();
79     private static final String OUTCOME_MSG = "my outcome message";
80
81     private final PolicyEngine engineMgr = mock(PolicyEngine.class);
82     private final WorkingMemory workMem = mock(WorkingMemory.class);
83     private final InternalFactHandle factHandle = mock(InternalFactHandle.class);
84     private final Operator policyOperator = mock(Operator.class);
85     private final Operation policyOperation = mock(Operation.class);
86     private final Actor policyActor = mock(Actor.class);
87     private final EventManagerServices services = mock(EventManagerServices.class);
88     private final ExecutorService executor = mock(ExecutorService.class);
89     private final MyStep stepa = mock(MyStep.class);
90     private final MyStep stepb = mock(MyStep.class);
91
92     private List<LockImpl> locks;
93     private ToscaPolicy tosca;
94     private ControlLoopParams params;
95     private ClEventManagerWithOutcome<MyStep> mgr;
96
97     /**
98      * Sets up.
99      */
100     @BeforeEach
101     public void setUp() throws ControlLoopException, CoderException {
102         when(workMem.getFactHandle(any())).thenReturn(factHandle);
103
104         params = new ControlLoopParams();
105         params.setClosedLoopControlName(CL_NAME);
106         params.setPolicyName(POLICY_NAME);
107         params.setPolicyScope(POLICY_SCOPE);
108         params.setPolicyVersion(POLICY_VERSION);
109
110         loadPolicy(EVENT_MGR_SIMPLE_YAML);
111
112         locks = new ArrayList<>();
113
114         mgr = new MyManager(services, params, REQ_ID, workMem);
115     }
116
117     @Test
118     void testConstructor() {
119         assertEquals(POLICY_NAME, mgr.getPolicyName());
120
121         // invalid
122         assertThatThrownBy(() -> new MyManager(services, params, null, workMem))
123                         .isInstanceOf(ControlLoopException.class);
124     }
125
126     @Test
127     void testLoadNextPolicy_testGetFullHistory_testGetPartialHistory() throws Exception {
128         loadPolicy(EVENT_MGR_MULTI_YAML);
129         mgr = new MyManager(services, params, REQ_ID, workMem);
130
131         // start and load step for first policy
132         mgr.start();
133         assertEquals("OperationA", mgr.getSteps().poll().getOperationName());
134         assertNull(mgr.getFinalResult());
135
136         // add an outcome
137         var outcome = makeOutcome();
138         mgr.addToHistory(outcome);
139
140         // indicate success and load next policy
141         mgr.loadNextPolicy(OperationResult.SUCCESS);
142         assertEquals("OperationB", mgr.getSteps().poll().getOperationName());
143         assertNull(mgr.getFinalResult());
144
145         // loadPolicy() should clear the partial history, but not the full history
146         assertThat(mgr.getPartialHistory()).isEmpty();
147         assertThat(mgr.getFullHistory()).hasSize(1);
148     }
149
150     @Test
151     void testExecuteStep() {
152         mgr.bumpAttempts();
153
154         // no steps to execute
155         assertFalse(mgr.executeStep());
156         assertEquals(0, mgr.getAttempts());
157     }
158
159     @Test
160     void testBumpAttempts() {
161         assertEquals(0, mgr.getAttempts());
162
163         mgr.bumpAttempts();
164         mgr.bumpAttempts();
165         assertEquals(2, mgr.getAttempts());
166     }
167
168     @Test
169     void testIsAbort() {
170         var outcome = makeCompletedOutcome();
171
172         outcome.setResult(OperationResult.FAILURE);
173         assertTrue(mgr.isAbort(outcome));
174
175         // no effect for success
176         outcome.setResult(OperationResult.SUCCESS);
177         assertFalse(mgr.isAbort(outcome));
178     }
179
180     @Test
181     void testAddToHistory() throws ControlLoopException {
182         mgr.start();
183
184         // add a "start" outcome
185         var outcome = makeOutcome();
186         mgr.addToHistory(outcome);
187
188         assertThat(mgr.getPartialHistory()).hasSize(1);
189         assertThat(mgr.getFullHistory()).hasSize(1);
190
191         // add a "completion" outcome - should replace the start
192         outcome = makeCompletedOutcome();
193         mgr.addToHistory(outcome);
194
195         assertThat(mgr.getPartialHistory()).hasSize(1);
196         assertThat(mgr.getFullHistory()).hasSize(1);
197         assertSame(outcome, mgr.getPartialHistory().peek().getOutcome());
198         assertSame(outcome, mgr.getFullHistory().peek().getOutcome());
199
200         // add another start
201         outcome = makeOutcome();
202         mgr.addToHistory(outcome);
203
204         assertThat(mgr.getPartialHistory()).hasSize(2);
205         assertThat(mgr.getFullHistory()).hasSize(2);
206         assertSame(outcome, mgr.getPartialHistory().peekLast().getOutcome());
207         assertSame(outcome, mgr.getFullHistory().peekLast().getOutcome());
208
209         // remove the last item from the full history and then add a "completion"
210         mgr.getFullHistory().removeLast();
211         outcome = makeCompletedOutcome();
212         mgr.addToHistory(outcome);
213         assertThat(mgr.getPartialHistory()).hasSize(2);
214         assertThat(mgr.getFullHistory()).hasSize(2);
215
216         // add another "start"
217         outcome = makeOutcome();
218         mgr.addToHistory(outcome);
219         assertThat(mgr.getPartialHistory()).hasSize(3);
220         assertThat(mgr.getFullHistory()).hasSize(3);
221
222         // add a "completion" for a different actor - should NOT replace the start
223         outcome = makeCompletedOutcome();
224         outcome.setActor("different-actor");
225         mgr.addToHistory(outcome);
226         assertThat(mgr.getPartialHistory()).hasSize(4);
227         assertThat(mgr.getFullHistory()).hasSize(4);
228         assertSame(outcome, mgr.getPartialHistory().peekLast().getOutcome());
229         assertSame(outcome, mgr.getFullHistory().peekLast().getOutcome());
230     }
231
232     @Test
233     void testMakeNotification() throws Exception {
234         loadPolicy(EVENT_MGR_MULTI_YAML);
235         mgr = new MyManager(services, params, REQ_ID, workMem);
236
237         // before started
238         assertNotNull(mgr.makeNotification());
239
240         mgr.start();
241
242         mgr.addToHistory(makeCompletedOutcome());
243         mgr.addToHistory(makeCompletedOutcome());
244         mgr.addToHistory(makeCompletedOutcome());
245
246         // check notification while running
247         var notif = mgr.makeNotification();
248         assertThat(notif.getMessage()).contains(SIMPLE_ACTOR);
249         assertThat(notif.getHistory()).hasSize(3);
250
251         // indicate success and load the next policy - should clear the partial history
252         mgr.loadNextPolicy(OperationResult.SUCCESS);
253
254         // check notification
255         notif = mgr.makeNotification();
256         assertNull(notif.getMessage());
257         assertThat(notif.getHistory()).isEmpty();
258
259         // add outcomes and check again
260         mgr.addToHistory(makeCompletedOutcome());
261         mgr.addToHistory(makeCompletedOutcome());
262
263         notif = mgr.makeNotification();
264         assertNotNull(notif.getMessage());
265
266         // should only have history for last two outcomes
267         assertThat(notif.getHistory()).hasSize(2);
268
269         // indicate failure - should go to final state
270         mgr.loadNextPolicy(OperationResult.FAILURE);
271
272         // check notification
273         notif = mgr.makeNotification();
274         assertNull(notif.getMessage());
275
276         // should be no history
277         assertThat(notif.getHistory()).isEmpty();
278
279         // null case
280         assertThatThrownBy(() -> mgr.loadNextPolicy(null)).isInstanceOf(NullPointerException.class)
281                         .hasMessageContaining("lastResult");
282     }
283
284     @Test
285     void testGetOperationMessage() throws ControlLoopException {
286         // no history yet
287         assertNull(mgr.getOperationMessage());
288
289         // add an outcome
290         mgr.start();
291         var outcome = makeOutcome();
292         mgr.addToHistory(outcome);
293
294         assertThat(mgr.getOperationMessage()).contains("actor=" + SIMPLE_ACTOR)
295                         .contains("operation=" + SIMPLE_OPERATION);
296     }
297
298     @Test
299     void testMakeControlLoopResponse() {
300         final var outcome = new OperationOutcome();
301         outcome.setActor(SIMPLE_ACTOR);
302
303         var resp = mgr.makeControlLoopResponse(outcome);
304         assertNotNull(resp);
305         assertEquals(SIMPLE_ACTOR, resp.getFrom());
306     }
307
308     private void loadPolicy(String fileName) throws CoderException {
309         var template = yamlCoder.decode(ResourceUtils.getResourceAsString(fileName), ToscaServiceTemplate.class);
310         tosca = template.getToscaTopologyTemplate().getPolicies().get(0).values().iterator().next();
311
312         params.setToscaPolicy(tosca);
313     }
314
315     private OperationOutcome makeCompletedOutcome() {
316         var outcome = makeOutcome();
317         outcome.setEnd(outcome.getStart());
318
319         return outcome;
320     }
321
322     private OperationOutcome makeOutcome() {
323         var outcome = new OperationOutcome();
324         outcome.setActor(SIMPLE_ACTOR);
325         outcome.setOperation(SIMPLE_OPERATION);
326         outcome.setMessage(OUTCOME_MSG);
327         outcome.setResult(OperationResult.SUCCESS);
328         outcome.setStart(Instant.now());
329         outcome.setTarget(MY_TARGET);
330
331         return outcome;
332     }
333
334
335     private class MyManager extends ClEventManagerWithOutcome<MyStep> {
336         private static final long serialVersionUID = 1L;
337
338         public MyManager(EventManagerServices services, ControlLoopParams params, UUID requestId, WorkingMemory workMem)
339                         throws ControlLoopException {
340
341             super(services, params, requestId, workMem);
342         }
343
344         @Override
345         protected ExecutorService getBlockingExecutor() {
346             return executor;
347         }
348
349         @Override
350         protected void makeLock(String targetEntity, String requestId, int holdSec, LockCallback callback) {
351             var lock = new LockImpl(LockState.ACTIVE, targetEntity, requestId, holdSec, callback);
352             locks.add(lock);
353             callback.lockAvailable(lock);
354         }
355
356         @Override
357         protected PolicyEngine getPolicyEngineManager() {
358             return engineMgr;
359         }
360
361         @Override
362         protected void loadPolicyStep(ControlLoopOperationParams params) {
363             getSteps().add(new MyStep(this, params));
364         }
365     }
366
367
368     private static class MyStep extends Step {
369         public MyStep(StepContext stepContext, ControlLoopOperationParams params) {
370             super(params, new AtomicReference<>());
371         }
372     }
373 }