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