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