Merge "move all bash to ash shell scripts for drools-apps"
[policy/drools-applications.git] / controlloop / common / controller-usecases / src / test / java / org / onap / policy / drools / apps / controller / usecases / UsecasesEventManagerTest.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP
4  * ================================================================================
5  * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.policy.drools.apps.controller.usecases;
22
23 import static org.assertj.core.api.Assertions.assertThat;
24 import static org.assertj.core.api.Assertions.assertThatCode;
25 import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
26 import static org.assertj.core.api.Assertions.assertThatThrownBy;
27 import static org.junit.Assert.assertEquals;
28 import static org.junit.Assert.assertFalse;
29 import static org.junit.Assert.assertNotNull;
30 import static org.junit.Assert.assertNull;
31 import static org.junit.Assert.assertSame;
32 import static org.junit.Assert.assertTrue;
33 import static org.mockito.ArgumentMatchers.any;
34 import static org.mockito.ArgumentMatchers.anyLong;
35 import static org.mockito.Mockito.never;
36 import static org.mockito.Mockito.verify;
37 import static org.mockito.Mockito.when;
38
39 import java.time.Instant;
40 import java.util.ArrayList;
41 import java.util.Arrays;
42 import java.util.Collections;
43 import java.util.Deque;
44 import java.util.List;
45 import java.util.Map;
46 import java.util.TreeMap;
47 import java.util.UUID;
48 import java.util.concurrent.ExecutorService;
49 import java.util.concurrent.ForkJoinPool;
50 import org.drools.core.WorkingMemory;
51 import org.junit.Before;
52 import org.junit.Test;
53 import org.kie.api.runtime.rule.FactHandle;
54 import org.mockito.Mock;
55 import org.mockito.MockitoAnnotations;
56 import org.onap.policy.common.utils.coder.Coder;
57 import org.onap.policy.common.utils.coder.CoderException;
58 import org.onap.policy.common.utils.coder.StandardYamlCoder;
59 import org.onap.policy.common.utils.io.Serializer;
60 import org.onap.policy.common.utils.resources.ResourceUtils;
61 import org.onap.policy.controlloop.ControlLoopEventStatus;
62 import org.onap.policy.controlloop.ControlLoopException;
63 import org.onap.policy.controlloop.ControlLoopResponse;
64 import org.onap.policy.controlloop.ControlLoopTargetType;
65 import org.onap.policy.controlloop.VirtualControlLoopEvent;
66 import org.onap.policy.controlloop.VirtualControlLoopNotification;
67 import org.onap.policy.controlloop.actorserviceprovider.ActorService;
68 import org.onap.policy.controlloop.actorserviceprovider.Operation;
69 import org.onap.policy.controlloop.actorserviceprovider.OperationFinalResult;
70 import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
71 import org.onap.policy.controlloop.actorserviceprovider.OperationProperties;
72 import org.onap.policy.controlloop.actorserviceprovider.OperationResult;
73 import org.onap.policy.controlloop.actorserviceprovider.Operator;
74 import org.onap.policy.controlloop.actorserviceprovider.TargetType;
75 import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
76 import org.onap.policy.controlloop.actorserviceprovider.spi.Actor;
77 import org.onap.policy.controlloop.drl.legacy.ControlLoopParams;
78 import org.onap.policy.controlloop.eventmanager.ActorConstants;
79 import org.onap.policy.controlloop.ophistory.OperationHistoryDataManager;
80 import org.onap.policy.drools.apps.controller.usecases.UsecasesEventManager.NewEventStatus;
81 import org.onap.policy.drools.apps.controller.usecases.step.AaiCqStep2;
82 import org.onap.policy.drools.apps.controller.usecases.step.AaiGetPnfStep2;
83 import org.onap.policy.drools.apps.controller.usecases.step.AaiGetTenantStep2;
84 import org.onap.policy.drools.apps.controller.usecases.step.GetTargetEntityStep2;
85 import org.onap.policy.drools.apps.controller.usecases.step.GuardStep2;
86 import org.onap.policy.drools.apps.controller.usecases.step.Step2;
87 import org.onap.policy.drools.core.lock.LockCallback;
88 import org.onap.policy.drools.core.lock.LockImpl;
89 import org.onap.policy.drools.core.lock.LockState;
90 import org.onap.policy.drools.system.PolicyEngine;
91 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
92 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
93 import org.onap.policy.sdnr.PciBody;
94 import org.onap.policy.sdnr.PciMessage;
95 import org.onap.policy.sdnr.PciResponse;
96
97 public class UsecasesEventManagerTest {
98     private static final UUID REQ_ID = UUID.randomUUID();
99     private static final String CL_NAME = "my-closed-loop-name";
100     private static final String POLICY_NAME = "my-policy-name";
101     private static final String POLICY_SCOPE = "my-scope";
102     private static final String POLICY_VERSION = "1.2.3";
103     private static final String SIMPLE_ACTOR = "First";
104     private static final String SIMPLE_OPERATION = "OperationA";
105     private static final String MY_TARGET = "my-target";
106     private static final String EVENT_MGR_MULTI_YAML =
107                     "../eventmanager/src/test/resources/eventManager/event-mgr-multi.yaml";
108     private static final String EVENT_MGR_SIMPLE_YAML =
109                     "../eventmanager/src/test/resources/eventManager/event-mgr-simple.yaml";
110     private static final Coder yamlCoder = new StandardYamlCoder();
111     private static final String OUTCOME_MSG = "my outcome message";
112     private static final String MY_SINK = "my-topic-sink";
113
114     @Mock
115     private PolicyEngine engineMgr;
116     @Mock
117     private WorkingMemory workMem;
118     @Mock
119     private FactHandle factHandle;
120     @Mock
121     private Operator policyOperator;
122     @Mock
123     private Operation policyOperation;
124     @Mock
125     private Actor policyActor;
126     @Mock
127     private ActorService actors;
128     @Mock
129     private OperationHistoryDataManager dataMgr;
130     @Mock
131     private ExecutorService executor;
132     @Mock
133     private Step2 stepa;
134     @Mock
135     private Step2 stepb;
136
137     private List<LockImpl> locks;
138     private ToscaPolicy tosca;
139     private ControlLoopParams params;
140     private VirtualControlLoopEvent event;
141     private UsecasesEventManager mgr;
142
143     /**
144      * Sets up.
145      */
146     @Before
147     public void setUp() throws ControlLoopException, CoderException {
148         MockitoAnnotations.initMocks(this);
149
150         when(actors.getActor(SIMPLE_ACTOR)).thenReturn(policyActor);
151         when(policyActor.getOperator(SIMPLE_OPERATION)).thenReturn(policyOperator);
152         when(policyOperator.buildOperation(any())).thenReturn(policyOperation);
153         when(policyOperation.getPropertyNames()).thenReturn(Collections.emptyList());
154
155         when(workMem.getFactHandle(any())).thenReturn(factHandle);
156
157         event = new VirtualControlLoopEvent();
158         event.setRequestId(REQ_ID);
159         event.setTarget(UsecasesConstants.VSERVER_VSERVER_NAME);
160         event.setAai(new TreeMap<>(Map.of(UsecasesConstants.VSERVER_VSERVER_NAME, MY_TARGET)));
161         event.setClosedLoopEventStatus(ControlLoopEventStatus.ONSET);
162         event.setClosedLoopControlName(CL_NAME);
163         event.setTargetType(ControlLoopTargetType.VNF);
164
165         params = new ControlLoopParams();
166         params.setClosedLoopControlName(CL_NAME);
167         params.setPolicyName(POLICY_NAME);
168         params.setPolicyScope(POLICY_SCOPE);
169         params.setPolicyVersion(POLICY_VERSION);
170
171         loadPolicy(EVENT_MGR_SIMPLE_YAML);
172
173         locks = new ArrayList<>();
174
175         mgr = new MyManager(params, event, workMem);
176     }
177
178     @Test
179     public void testConstructor() {
180         assertEquals(POLICY_NAME, mgr.getPolicyName());
181         assertSame(event, mgr.getEvent());
182
183         Map<String, String> orig = event.getAai();
184
185         event.setAai(addAai(orig, UsecasesConstants.VSERVER_IS_CLOSED_LOOP_DISABLED, "true"));
186         assertThatThrownBy(() -> new UsecasesEventManager(params, event, workMem))
187                         .hasMessage("is-closed-loop-disabled is set to true on VServer or VNF");
188
189         // vserver ACTIVE
190         event.setAai(addAai(orig, UsecasesConstants.VSERVER_PROV_STATUS,
191                         UsecasesConstants.PROV_STATUS_ACTIVE.toUpperCase()));
192         assertThatCode(() -> new UsecasesEventManager(params, event, workMem)).doesNotThrowAnyException();
193
194         // vserver active
195         event.setAai(addAai(orig, UsecasesConstants.VSERVER_PROV_STATUS,
196                         UsecasesConstants.PROV_STATUS_ACTIVE.toLowerCase()));
197         assertThatCode(() -> new UsecasesEventManager(params, event, workMem)).doesNotThrowAnyException();
198
199         // vserver inactive
200         event.setAai(addAai(orig, UsecasesConstants.VSERVER_PROV_STATUS, "inactive"));
201         assertThatThrownBy(() -> new UsecasesEventManager(params, event, workMem))
202                         .hasMessage("prov-status is not ACTIVE on VServer or VNF");
203
204         // vnf ACTIVE
205         event.setAai(addAai(orig, UsecasesConstants.GENERIC_VNF_PROV_STATUS,
206                         UsecasesConstants.PROV_STATUS_ACTIVE.toUpperCase()));
207         assertThatCode(() -> new UsecasesEventManager(params, event, workMem)).doesNotThrowAnyException();
208
209         // vnf active
210         event.setAai(addAai(orig, UsecasesConstants.GENERIC_VNF_PROV_STATUS,
211                         UsecasesConstants.PROV_STATUS_ACTIVE.toLowerCase()));
212         assertThatCode(() -> new UsecasesEventManager(params, event, workMem)).doesNotThrowAnyException();
213
214         // vnf inactive
215         event.setAai(addAai(orig, UsecasesConstants.GENERIC_VNF_PROV_STATUS, "inactive"));
216         assertThatThrownBy(() -> new UsecasesEventManager(params, event, workMem))
217                         .hasMessage("prov-status is not ACTIVE on VServer or VNF");
218
219         // valid
220         event.setAai(orig);
221         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
222
223         // invalid
224         event.setTarget("unknown-target");
225         assertThatThrownBy(() -> new UsecasesEventManager(params, event, workMem))
226                         .isInstanceOf(ControlLoopException.class);
227     }
228
229     @Test
230     public void testIsActive() throws Exception {
231         mgr = new UsecasesEventManager(params, event, workMem);
232         assertTrue(mgr.isActive());
233
234         // deserialized manager should be inactive
235         UsecasesEventManager mgr2 = Serializer.roundTrip(mgr);
236         assertFalse(mgr2.isActive());
237     }
238
239     @Test
240     public void testDestroy_testGetSteps() {
241         // add some steps to the queue
242         mgr.getSteps().add(stepa);
243         mgr.getSteps().add(stepb);
244
245         mgr.destroy();
246
247         verify(stepa).cancel();
248         verify(stepb).cancel();
249
250         // if superclass destroy() was invoked, then freeLock() should have been submitted
251         // to the executor
252         verify(executor).execute(any());
253     }
254
255     @Test
256     public void testOnStart() throws ControlLoopException {
257         OperationOutcome outcome = makeOutcome();
258
259         mgr.start();
260         mgr.onStart(outcome);
261
262         assertSame(outcome, mgr.getOutcomes().poll());
263         assertThat(mgr.getOutcomes()).isEmpty();
264
265         verify(workMem).update(factHandle, mgr);
266     }
267
268     @Test
269     public void testOnComplete() throws ControlLoopException {
270         OperationOutcome outcome = makeCompletedOutcome();
271
272         mgr.start();
273         mgr.onComplete(outcome);
274
275         assertSame(outcome, mgr.getOutcomes().poll());
276         assertThat(mgr.getOutcomes()).isEmpty();
277
278         verify(workMem).update(factHandle, mgr);
279     }
280
281     @Test
282     public void testToString() {
283         assertNotNull(mgr.toString());
284     }
285
286     @Test
287     public void testStart() throws ControlLoopException {
288         // start it
289         mgr.start();
290
291         // cannot re-start
292         assertThatCode(() -> mgr.start()).isInstanceOf(IllegalStateException.class)
293                         .hasMessage("manager already started");
294     }
295
296     /**
297      * Tests start() when the manager is not in working memory.
298      */
299     @Test
300     public void testStartNotInWorkingMemory() throws ControlLoopException {
301         when(workMem.getFactHandle(any())).thenReturn(null);
302
303         assertThatCode(() -> mgr.start()).isInstanceOf(IllegalStateException.class)
304                         .hasMessage("manager is not in working memory");
305     }
306
307     /**
308      * Tests start() when the manager is not active.
309      */
310     @Test
311     public void testStartInactive() throws Exception {
312         // make an inactive manager by deserializing it
313         mgr = Serializer.roundTrip(new UsecasesEventManager(params, event, workMem));
314
315         // cannot re-start
316         assertThatCode(() -> mgr.start()).isInstanceOf(IllegalStateException.class)
317                         .hasMessage("manager is no longer active");
318     }
319
320     @Test
321     public void testAbort() {
322         mgr.abort(UsecasesEventManager.State.DONE, OperationFinalResult.FINAL_FAILURE_GUARD, "some message");
323
324         assertEquals(UsecasesEventManager.State.DONE, mgr.getState());
325         assertEquals(OperationFinalResult.FINAL_FAILURE_GUARD, mgr.getFinalResult());
326         assertEquals("some message", mgr.getFinalMessage());
327
328         // try null state
329         assertThatThrownBy(() -> mgr.abort(null, OperationFinalResult.FINAL_FAILURE_GUARD, ""))
330                         .isInstanceOf(NullPointerException.class).hasMessageContaining("finalState");
331     }
332
333     @Test
334     public void testLoadNextPolicy_testGetFullHistory_testGetPartialHistory() throws Exception {
335         loadPolicy(EVENT_MGR_MULTI_YAML);
336         mgr = new MyManager(params, event, workMem);
337
338         // start and load step for first policy
339         mgr.start();
340         assertEquals("OperationA", mgr.getSteps().poll().getOperationName());
341         assertNull(mgr.getFinalResult());
342
343         // add an outcome
344         OperationOutcome outcome = makeOutcome();
345         mgr.addToHistory(outcome);
346
347         // indicate success and load next policy
348         mgr.loadNextPolicy(OperationResult.SUCCESS);
349         assertEquals("OperationB", mgr.getSteps().poll().getOperationName());
350         assertNull(mgr.getFinalResult());
351
352         // loadPolicy() should clear the partial history, but not the full history
353         assertThat(mgr.getPartialHistory()).isEmpty();
354         assertThat(mgr.getFullHistory()).hasSize(1);
355
356         // indicate failure - should go to final failure
357         mgr.loadNextPolicy(OperationResult.FAILURE);
358         assertEquals(OperationFinalResult.FINAL_FAILURE, mgr.getFinalResult());
359     }
360
361     @Test
362     public void testLoadPolicy() throws ControlLoopException {
363         // start() will invoke loadPolicy()
364         mgr.start();
365
366         assertNull(mgr.getFinalResult());
367
368         Step2 step = mgr.getSteps().peek();
369         assertNotNull(step);
370         assertEquals("First", step.getActorName());
371         assertEquals("OperationA", step.getOperationName());
372
373         ControlLoopOperationParams params2 = step.getParams();
374         assertSame(actors, params2.getActorService());
375         assertSame(REQ_ID, params2.getRequestId());
376         assertSame(ForkJoinPool.commonPool(), params2.getExecutor());
377         assertNotNull(params2.getTargetType());
378         assertNotNull(params2.getTargetEntityIds());
379         assertEquals(Integer.valueOf(300), params2.getTimeoutSec());
380         assertEquals(Integer.valueOf(0), params2.getRetry());
381         assertThat(params2.getPayload()).isEmpty();
382         assertNotNull(params2.getStartCallback());
383         assertNotNull(params2.getCompleteCallback());
384     }
385
386     @Test
387     public void testLoadPreprocessorSteps() {
388         stepa = new Step2(mgr, ControlLoopOperationParams.builder().build(), event) {
389             @Override
390             public List<String> getPropertyNames() {
391                 return List.of(OperationProperties.AAI_DEFAULT_CLOUD_REGION);
392             }
393
394             @Override
395             protected Operation buildOperation() {
396                 return policyOperation;
397             }
398         };
399
400         mgr.getSteps().add(stepa);
401         mgr.getSteps().add(stepb);
402
403         mgr.loadPreprocessorSteps();
404
405         Deque<Step2> steps = mgr.getSteps();
406
407         Step2 lockStep = steps.poll();
408         assertNotNull(lockStep);
409         assertEquals(ActorConstants.LOCK_ACTOR, lockStep.getActorName());
410         assertThat(steps.poll()).isInstanceOf(AaiCqStep2.class);
411         assertThat(steps.poll()).isInstanceOf(GuardStep2.class);
412         assertSame(stepa, steps.poll());
413         assertSame(stepb, steps.poll());
414         assertThat(steps).isEmpty();
415     }
416
417     /**
418      * Tests loadPreprocessorSteps() when there are too many steps in the queue.
419      */
420     @Test
421     public void testLoadPreprocessorStepsTooManySteps() {
422         loadStepsWithProperties(OperationProperties.AAI_PNF);
423
424         Deque<Step2> steps = mgr.getSteps();
425         stepa = steps.getFirst();
426         steps.clear();
427
428         // load up a bunch of steps
429         for (int nsteps = 0; nsteps < UsecasesEventManager.MAX_STEPS; ++nsteps) {
430             steps.add(stepa);
431         }
432
433         // should fail
434         assertThatIllegalStateException().isThrownBy(() -> mgr.loadPreprocessorSteps()).withMessage("too many steps");
435
436         // add another step, should still fail
437         steps.add(stepa);
438         assertThatIllegalStateException().isThrownBy(() -> mgr.loadPreprocessorSteps()).withMessage("too many steps");
439
440         // remove two steps - should now succeed
441         steps.remove();
442         steps.remove();
443
444         int nsteps = steps.size();
445
446         mgr.loadPreprocessorSteps();
447         assertEquals(nsteps + 1, steps.size());
448     }
449
450     /**
451      * Tests loadPreprocessorSteps() when no additional steps are needed.
452      */
453     @Test
454     public void testLoadPreprocessorStepsNothingToLoad() {
455         when(stepa.isPolicyStep()).thenReturn(false);
456         when(stepa.getPropertyNames()).thenReturn(List.of("unknown-property"));
457
458         Deque<Step2> steps = mgr.getSteps();
459         steps.add(stepa);
460         steps.add(stepb);
461
462         setTargetEntity();
463         mgr.loadPreprocessorSteps();
464
465         assertSame(stepa, steps.poll());
466         assertSame(stepb, steps.poll());
467         assertThat(steps).isEmpty();
468     }
469
470     /**
471      * Tests loadPreprocessorSteps() when an A&AI custom query is needed.
472      */
473     @Test
474     public void testLoadPreprocessorStepsCq() {
475         loadStepsWithProperties(OperationProperties.AAI_DEFAULT_CLOUD_REGION, OperationProperties.AAI_DEFAULT_TENANT);
476
477         setTargetEntity();
478         mgr.loadPreprocessorSteps();
479
480         Deque<Step2> steps = mgr.getSteps();
481
482         assertThat(steps.poll()).isInstanceOf(AaiCqStep2.class);
483         assertSame(stepa, steps.poll());
484         assertSame(stepb, steps.poll());
485         assertThat(steps).isEmpty();
486     }
487
488     /**
489      * Tests loadPreprocessorSteps() when an A&AI PNF query is needed.
490      */
491     @Test
492     public void testLoadPreprocessorStepsPnf() {
493         // doubling up the property to check both branches
494         loadStepsWithProperties(OperationProperties.AAI_PNF, OperationProperties.AAI_PNF);
495
496         setTargetEntity();
497         mgr.loadPreprocessorSteps();
498
499         Deque<Step2> steps = mgr.getSteps();
500
501         assertThat(steps.poll()).isInstanceOf(AaiGetPnfStep2.class);
502         assertSame(stepa, steps.poll());
503         assertSame(stepb, steps.poll());
504         assertThat(steps).isEmpty();
505     }
506
507     /**
508      * Tests loadPreprocessorSteps() when an A&AI Tenant query is needed.
509      */
510     @Test
511     public void testLoadPreprocessorStepsTenant() {
512         // doubling up the property to check both branches
513         event.getAai().put(Step2.VSERVER_VSERVER_NAME, "my-vserver");
514         loadStepsWithProperties(OperationProperties.AAI_VSERVER_LINK, OperationProperties.AAI_VSERVER_LINK);
515
516         setTargetEntity();
517         mgr.loadPreprocessorSteps();
518
519         Deque<Step2> steps = mgr.getSteps();
520
521         assertThat(steps.poll()).isInstanceOf(AaiGetTenantStep2.class);
522         assertSame(stepa, steps.poll());
523         assertSame(stepb, steps.poll());
524         assertThat(steps).isEmpty();
525     }
526
527     /**
528      * Tests loadPreprocessorSteps() when the target entity is unset.
529      */
530     @Test
531     public void testLoadPreprocessorStepsNeedTargetEntity() {
532         stepa = new Step2(mgr,
533                 ControlLoopOperationParams.builder()
534                         .targetType(TargetType.toTargetType(event.getTargetType()))
535                         .targetEntityIds(Map.of()).build(), event) {
536             @Override
537             public List<String> getPropertyNames() {
538                 return List.of(OperationProperties.AAI_TARGET_ENTITY);
539             }
540
541             @Override
542             protected Operation buildOperation() {
543                 return policyOperation;
544             }
545
546             @Override
547             public boolean isPolicyStep() {
548                 return false;
549             }
550         };
551
552         Deque<Step2> steps = mgr.getSteps();
553         steps.add(stepa);
554         steps.add(stepb);
555
556         mgr.loadPreprocessorSteps();
557
558         Step2 entityStep = steps.poll();
559
560         assertThat(entityStep).isInstanceOf(GetTargetEntityStep2.class);
561         assertSame(stepa, steps.poll());
562         assertSame(stepb, steps.poll());
563         assertThat(steps).isEmpty();
564
565         // put get-target-entity back onto the queue and ensure nothing else is added
566         steps.add(entityStep);
567         mgr.loadPreprocessorSteps();
568         assertSame(entityStep, steps.poll());
569         assertThat(steps).isEmpty();
570     }
571
572     @Test
573     public void testExecuteStep() {
574         mgr.bumpAttempts();
575
576         // no steps to execute
577         assertFalse(mgr.executeStep());
578         assertEquals(0, mgr.getAttempts());
579
580         // add a step to the queue
581         mgr.getSteps().add(stepa);
582
583         // step returns false
584         when(stepa.start(anyLong())).thenReturn(false);
585         assertFalse(mgr.executeStep());
586
587         // step returns true
588         when(stepa.start(anyLong())).thenReturn(true);
589         assertTrue(mgr.executeStep());
590     }
591
592     @Test
593     public void testNextStep() {
594         mgr.getSteps().add(stepa);
595
596         mgr.nextStep();
597
598         assertThat(mgr.getSteps()).isEmpty();
599     }
600
601     @Test
602     public void testBumpAttempts() {
603         assertEquals(0, mgr.getAttempts());
604
605         mgr.bumpAttempts();
606         mgr.bumpAttempts();
607         assertEquals(2, mgr.getAttempts());
608     }
609
610     @Test
611     public void testIsAbort() {
612         OperationOutcome outcome = makeCompletedOutcome();
613         outcome.setResult(OperationResult.FAILURE);
614
615         // closed loop timeout
616         outcome.setActor(ActorConstants.CL_TIMEOUT_ACTOR);
617         assertTrue(mgr.isAbort(outcome));
618
619         // lost lock
620         outcome.setActor(ActorConstants.LOCK_ACTOR);
621         assertTrue(mgr.isAbort(outcome));
622
623         // no effect for success
624         outcome.setResult(OperationResult.SUCCESS);
625         assertFalse(mgr.isAbort(outcome));
626     }
627
628     @Test
629     public void testAddToHistory() throws ControlLoopException {
630         mgr.start();
631
632         // add a "start" outcome
633         OperationOutcome outcome = makeOutcome();
634         mgr.addToHistory(outcome);
635
636         assertThat(mgr.getPartialHistory()).hasSize(1);
637         assertThat(mgr.getFullHistory()).hasSize(1);
638
639         // add a "completion" outcome - should replace the start
640         outcome = makeCompletedOutcome();
641         mgr.addToHistory(outcome);
642
643         assertThat(mgr.getPartialHistory()).hasSize(1);
644         assertThat(mgr.getFullHistory()).hasSize(1);
645         assertSame(outcome, mgr.getPartialHistory().peek().getOutcome());
646         assertSame(outcome, mgr.getFullHistory().peek().getOutcome());
647
648         // add another start
649         outcome = makeOutcome();
650         mgr.addToHistory(outcome);
651
652         assertThat(mgr.getPartialHistory()).hasSize(2);
653         assertThat(mgr.getFullHistory()).hasSize(2);
654         assertSame(outcome, mgr.getPartialHistory().peekLast().getOutcome());
655         assertSame(outcome, mgr.getFullHistory().peekLast().getOutcome());
656
657         // remove the last item from the full history and then add a "completion"
658         mgr.getFullHistory().removeLast();
659         outcome = makeCompletedOutcome();
660         mgr.addToHistory(outcome);
661         assertThat(mgr.getPartialHistory()).hasSize(2);
662         assertThat(mgr.getFullHistory()).hasSize(2);
663
664         // add another "start"
665         outcome = makeOutcome();
666         mgr.addToHistory(outcome);
667         assertThat(mgr.getPartialHistory()).hasSize(3);
668         assertThat(mgr.getFullHistory()).hasSize(3);
669
670         // add a "completion" for a different actor - should NOT replace the start
671         outcome = makeCompletedOutcome();
672         outcome.setActor("different-actor");
673         mgr.addToHistory(outcome);
674         assertThat(mgr.getPartialHistory()).hasSize(4);
675         assertThat(mgr.getFullHistory()).hasSize(4);
676         assertSame(outcome, mgr.getPartialHistory().peekLast().getOutcome());
677         assertSame(outcome, mgr.getFullHistory().peekLast().getOutcome());
678     }
679
680     @Test
681     public void testMakeNotification() throws Exception {
682         loadPolicy(EVENT_MGR_MULTI_YAML);
683         mgr = new MyManager(params, event, workMem);
684
685         // before started
686         assertNotNull(mgr.makeNotification());
687
688         mgr.start();
689
690         mgr.addToHistory(makeCompletedOutcome());
691         mgr.addToHistory(makeCompletedOutcome());
692         mgr.addToHistory(makeCompletedOutcome());
693
694         // check notification while running
695         VirtualControlLoopNotification notif = mgr.makeNotification();
696         assertThat(notif.getMessage()).contains(SIMPLE_ACTOR);
697         assertThat(notif.getHistory()).hasSize(3);
698
699         // indicate success and load the next policy - should clear the partial history
700         mgr.loadNextPolicy(OperationResult.SUCCESS);
701
702         // check notification
703         notif = mgr.makeNotification();
704         assertNull(notif.getMessage());
705         assertThat(notif.getHistory()).isEmpty();
706
707         // add outcomes and check again
708         mgr.addToHistory(makeCompletedOutcome());
709         mgr.addToHistory(makeCompletedOutcome());
710
711         notif = mgr.makeNotification();
712         assertNotNull(notif.getMessage());
713
714         // should only have history for last two outcomes
715         assertThat(notif.getHistory()).hasSize(2);
716
717         // indicate failure - should go to final state
718         mgr.loadNextPolicy(OperationResult.FAILURE);
719
720         // check notification
721         notif = mgr.makeNotification();
722         assertNull(notif.getMessage());
723
724         // should be no history
725         assertThat(notif.getHistory()).isEmpty();
726
727         // null case
728         assertThatThrownBy(() -> mgr.loadNextPolicy(null)).isInstanceOf(NullPointerException.class)
729                         .hasMessageContaining("lastResult");
730     }
731
732     @Test
733     public void testDeliver() {
734         mgr.deliver(MY_SINK, null, "null notification", "null rule");
735         verify(engineMgr, never()).deliver(any(), any());
736
737         mgr.deliver(MY_SINK, "publishA", "A notification", "A rule");
738         verify(engineMgr).deliver(MY_SINK, "publishA");
739
740         // cause deliver() to throw an exception
741         when(engineMgr.deliver(any(), any())).thenThrow(new IllegalStateException("expected exception"));
742         assertThatCode(() -> mgr.deliver(MY_SINK, "publishB", "B notification", "B rule")).doesNotThrowAnyException();
743     }
744
745     @Test
746     public void testGetOperationMessage() throws ControlLoopException {
747         // no history yet
748         assertNull(mgr.getOperationMessage());
749
750         // add an outcome
751         mgr.start();
752         OperationOutcome outcome = makeOutcome();
753         mgr.addToHistory(outcome);
754
755         assertThat(mgr.getOperationMessage()).contains("actor=" + SIMPLE_ACTOR)
756                         .contains("operation=" + SIMPLE_OPERATION);
757     }
758
759     @Test
760     public void testStoreInDataBase() throws ControlLoopException {
761         mgr.start();
762         OperationOutcome outcome = makeOutcome();
763         mgr.addToHistory(outcome);
764
765         mgr.storeInDataBase(mgr.getPartialHistory().peekLast());
766
767         verify(dataMgr).store(REQ_ID.toString(), event, null, mgr.getPartialHistory().peekLast().getClOperation());
768     }
769
770     @Test
771     public void testMakeControlLoopResponse() {
772         final OperationOutcome outcome = new OperationOutcome();
773
774         // no message - should return null
775         checkResp(outcome, null);
776
777         // not a PciMessage - should return null
778         outcome.setResponse("not-a-pci-message");
779         checkResp(outcome, null);
780
781         /*
782          * now work with a PciMessage
783          */
784         PciMessage msg = new PciMessage();
785         outcome.setResponse(msg);
786
787         PciBody body = new PciBody();
788         msg.setBody(body);
789
790         PciResponse output = new PciResponse();
791         body.setOutput(output);
792
793         output.setPayload("my-payload");
794
795         // should generate a response, with a payload
796         checkResp(outcome, "my-payload");
797
798         /*
799          * these should generate a response, with null payload
800          */
801         output.setPayload(null);
802         checkResp(outcome, null);
803
804         body.setOutput(null);
805         checkResp(outcome, null);
806
807         msg.setBody(null);
808         checkResp(outcome, null);
809
810         outcome.setResponse(null);
811         checkResp(outcome, null);
812     }
813
814     @Test
815     public void testOnNewEvent() {
816         VirtualControlLoopEvent event2 = new VirtualControlLoopEvent(event);
817         assertEquals(NewEventStatus.FIRST_ONSET, mgr.onNewEvent(event2));
818
819         event2.setPayload("other payload");
820         assertEquals(NewEventStatus.SUBSEQUENT_ONSET, mgr.onNewEvent(event2));
821         assertEquals(NewEventStatus.SUBSEQUENT_ONSET, mgr.onNewEvent(event2));
822         assertEquals(NewEventStatus.FIRST_ONSET, mgr.onNewEvent(event));
823
824         event2.setClosedLoopEventStatus(ControlLoopEventStatus.ABATED);
825         assertEquals(NewEventStatus.FIRST_ABATEMENT, mgr.onNewEvent(event2));
826
827         assertEquals(NewEventStatus.SUBSEQUENT_ABATEMENT, mgr.onNewEvent(event2));
828         assertEquals(NewEventStatus.SUBSEQUENT_ABATEMENT, mgr.onNewEvent(event2));
829
830         event2.setClosedLoopEventStatus(null);
831         assertEquals(NewEventStatus.SYNTAX_ERROR, mgr.onNewEvent(event2));
832     }
833
834     @Test
835     public void testCheckEventSyntax() {
836         // initially, it's valid
837         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
838
839         event.setTarget("unknown-target");
840         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
841                         .hasMessage("target field invalid");
842
843         event.setTarget(null);
844         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
845                         .hasMessage("No target field");
846
847         // abated supersedes previous errors - so it shouldn't throw an exception
848         event.setClosedLoopEventStatus(ControlLoopEventStatus.ABATED);
849         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
850
851         event.setRequestId(null);
852         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
853                         .hasMessage("No request ID");
854
855         event.setClosedLoopControlName(null);
856         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
857                         .hasMessage("No control loop name");
858     }
859
860     @Test
861     public void testValidateStatus() {
862         event.setClosedLoopEventStatus(ControlLoopEventStatus.ONSET);
863         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
864
865         event.setClosedLoopEventStatus(ControlLoopEventStatus.ABATED);
866         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
867
868         event.setClosedLoopEventStatus(null);
869         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
870                         .hasMessage("Invalid value in closedLoopEventStatus");
871     }
872
873     @Test
874     public void testValidateAaiData() {
875         event.setTargetType("unknown-target-type");
876         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
877                         .hasMessage("The target type is not supported");
878
879         event.setTargetType(null);
880         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
881                         .hasMessage("The Target type is null");
882
883         event.setAai(null);
884         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
885                         .hasMessage("AAI is null");
886
887         // VM case
888         event.setTargetType(ControlLoopTargetType.VM);
889         event.setAai(Map.of(UsecasesConstants.GENERIC_VNF_VNF_ID, MY_TARGET));
890         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
891
892         event.setAai(Map.of());
893         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class);
894
895         // VNF case
896         event.setTargetType(ControlLoopTargetType.VNF);
897         event.setAai(Map.of(UsecasesConstants.GENERIC_VNF_VNF_ID, MY_TARGET));
898         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
899
900         event.setAai(Map.of());
901         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class);
902
903         // PNF case
904         event.setTargetType(ControlLoopTargetType.PNF);
905         event.setAai(Map.of(UsecasesConstants.PNF_NAME, MY_TARGET));
906         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
907
908         event.setAai(Map.of());
909         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class);
910     }
911
912     @Test
913     public void testValidateAaiVmVnfData() {
914         event.setTargetType(ControlLoopTargetType.VM);
915         event.setAai(Map.of(UsecasesConstants.GENERIC_VNF_VNF_ID, MY_TARGET));
916         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
917
918         event.setAai(Map.of(UsecasesConstants.VSERVER_VSERVER_NAME, MY_TARGET));
919         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
920
921         event.setAai(Map.of(UsecasesConstants.GENERIC_VNF_VNF_NAME, MY_TARGET));
922         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
923
924         event.setAai(Map.of());
925         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class).hasMessage(
926                         "generic-vnf.vnf-id or generic-vnf.vnf-name or vserver.vserver-name information missing");
927     }
928
929     @Test
930     public void testValidateAaiPnfData() {
931         event.setTargetType(ControlLoopTargetType.PNF);
932         event.setAai(Map.of(UsecasesConstants.PNF_NAME, MY_TARGET));
933         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
934
935         event.setAai(Map.of());
936         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
937                         .hasMessage("AAI PNF object key pnf-name is missing");
938     }
939
940     @Test
941     public void testIsClosedLoopDisabled() {
942         Map<String, String> orig = event.getAai();
943
944         event.setAai(addAai(orig, UsecasesConstants.VSERVER_IS_CLOSED_LOOP_DISABLED, "true"));
945         assertThatThrownBy(() -> new UsecasesEventManager(params, event, workMem))
946                         .isInstanceOf(IllegalStateException.class);
947
948         event.setAai(addAai(orig, UsecasesConstants.GENERIC_VNF_IS_CLOSED_LOOP_DISABLED, "true"));
949         assertThatThrownBy(() -> new UsecasesEventManager(params, event, workMem))
950                         .isInstanceOf(IllegalStateException.class);
951
952         event.setAai(addAai(orig, UsecasesConstants.PNF_IS_IN_MAINT, "true"));
953         assertThatThrownBy(() -> new UsecasesEventManager(params, event, workMem))
954                         .isInstanceOf(IllegalStateException.class);
955     }
956
957     @Test
958     public void testIsProvStatusInactive() {
959         Map<String, String> orig = event.getAai();
960
961         event.setAai(addAai(orig, UsecasesConstants.VSERVER_PROV_STATUS, "ACTIVE"));
962         assertThatCode(() -> new UsecasesEventManager(params, event, workMem)).doesNotThrowAnyException();
963
964         event.setAai(addAai(orig, UsecasesConstants.VSERVER_PROV_STATUS, "inactive"));
965         assertThatThrownBy(() -> new UsecasesEventManager(params, event, workMem))
966                         .isInstanceOf(IllegalStateException.class);
967
968         event.setAai(addAai(orig, UsecasesConstants.GENERIC_VNF_PROV_STATUS, "ACTIVE"));
969         assertThatCode(() -> new UsecasesEventManager(params, event, workMem)).doesNotThrowAnyException();
970
971         event.setAai(addAai(orig, UsecasesConstants.GENERIC_VNF_PROV_STATUS, "inactive"));
972         assertThatThrownBy(() -> new UsecasesEventManager(params, event, workMem))
973                         .isInstanceOf(IllegalStateException.class);
974     }
975
976     @Test
977     public void testIsAaiTrue() {
978         Map<String, String> orig = event.getAai();
979
980         for (String value : Arrays.asList("yes", "y", "true", "t", "yEs", "trUe")) {
981             event.setAai(addAai(orig, UsecasesConstants.VSERVER_IS_CLOSED_LOOP_DISABLED, value));
982             assertThatThrownBy(() -> new UsecasesEventManager(params, event, workMem))
983                             .isInstanceOf(IllegalStateException.class);
984         }
985
986         event.setAai(addAai(orig, UsecasesConstants.VSERVER_IS_CLOSED_LOOP_DISABLED, "false"));
987         assertThatCode(() -> new UsecasesEventManager(params, event, workMem)).doesNotThrowAnyException();
988
989         event.setAai(addAai(orig, UsecasesConstants.VSERVER_IS_CLOSED_LOOP_DISABLED, "no"));
990         assertThatCode(() -> new UsecasesEventManager(params, event, workMem)).doesNotThrowAnyException();
991     }
992
993
994     private Map<String, String> addAai(Map<String, String> original, String key, String value) {
995         Map<String, String> map = new TreeMap<>(original);
996         map.put(key, value);
997         return map;
998     }
999
1000     private void loadPolicy(String fileName) throws CoderException {
1001         ToscaServiceTemplate template =
1002                         yamlCoder.decode(ResourceUtils.getResourceAsString(fileName), ToscaServiceTemplate.class);
1003         tosca = template.getToscaTopologyTemplate().getPolicies().get(0).values().iterator().next();
1004
1005         params.setToscaPolicy(tosca);
1006     }
1007
1008     private void loadStepsWithProperties(String... properties) {
1009         stepa = new Step2(mgr, ControlLoopOperationParams.builder().build(), event) {
1010
1011             @Override
1012             public boolean isPolicyStep() {
1013                 return false;
1014             }
1015
1016             @Override
1017             public List<String> getPropertyNames() {
1018                 return List.of(properties);
1019             }
1020
1021             @Override
1022             protected Operation buildOperation() {
1023                 return policyOperation;
1024             }
1025         };
1026
1027         mgr.getSteps().add(stepa);
1028         mgr.getSteps().add(stepb);
1029     }
1030
1031     private OperationOutcome makeCompletedOutcome() {
1032         OperationOutcome outcome = makeOutcome();
1033         outcome.setEnd(outcome.getStart());
1034
1035         return outcome;
1036     }
1037
1038     private OperationOutcome makeOutcome() {
1039         OperationOutcome outcome = new OperationOutcome();
1040         outcome.setActor(SIMPLE_ACTOR);
1041         outcome.setOperation(SIMPLE_OPERATION);
1042         outcome.setMessage(OUTCOME_MSG);
1043         outcome.setResult(OperationResult.SUCCESS);
1044         outcome.setStart(Instant.now());
1045         outcome.setTarget(MY_TARGET);
1046
1047         return outcome;
1048     }
1049
1050     private void checkResp(OperationOutcome outcome, String expectedPayload) {
1051         ControlLoopResponse resp = mgr.makeControlLoopResponse(outcome);
1052         assertNotNull(resp);
1053         assertEquals(REQ_ID, resp.getRequestId());
1054         assertEquals(expectedPayload, resp.getPayload());
1055     }
1056
1057     /**
1058      * Sets the target entity so a step doesn't have to be added to set it.
1059      */
1060     private void setTargetEntity() {
1061         mgr.setProperty(OperationProperties.AAI_TARGET_ENTITY, MY_TARGET);
1062     }
1063
1064
1065     private class MyManager extends UsecasesEventManager {
1066         private static final long serialVersionUID = 1L;
1067
1068         public MyManager(ControlLoopParams params, VirtualControlLoopEvent event, WorkingMemory workMem)
1069                         throws ControlLoopException {
1070
1071             super(params, event, workMem);
1072         }
1073
1074         @Override
1075         protected ExecutorService getBlockingExecutor() {
1076             return executor;
1077         }
1078
1079         @Override
1080         protected void makeLock(String targetEntity, String requestId, int holdSec, LockCallback callback) {
1081             LockImpl lock = new LockImpl(LockState.ACTIVE, targetEntity, requestId, holdSec, callback);
1082             locks.add(lock);
1083             callback.lockAvailable(lock);
1084         }
1085
1086         @Override
1087         public ActorService getActorService() {
1088             return actors;
1089         }
1090
1091         @Override
1092         public OperationHistoryDataManager getDataManager() {
1093             return dataMgr;
1094         }
1095
1096         @Override
1097         protected PolicyEngine getPolicyEngineManager() {
1098             return engineMgr;
1099         }
1100     }
1101 }