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