2  * ============LICENSE_START=======================================================
 
   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
 
  11  *      http://www.apache.org/licenses/LICENSE-2.0
 
  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=========================================================
 
  21 package org.onap.policy.drools.apps.controller.usecases;
 
  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;
 
  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;
 
  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;
 
  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";
 
 116     private PolicyEngine engineMgr;
 
 118     private WorkingMemory workMem;
 
 120     private FactHandle factHandle;
 
 122     private Operator policyOperator;
 
 124     private Operation policyOperation;
 
 126     private Actor policyActor;
 
 128     private ActorService actors;
 
 130     private OperationHistoryDataManager dataMgr;
 
 132     private ExecutorService executor;
 
 138     private List<LockImpl> locks;
 
 139     private ToscaPolicy tosca;
 
 140     private ControlLoopParams params;
 
 141     private VirtualControlLoopEvent event;
 
 142     private UsecasesEventManager mgr;
 
 148     public void setUp() throws ControlLoopException, CoderException {
 
 149         MockitoAnnotations.initMocks(this);
 
 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());
 
 156         when(workMem.getFactHandle(any())).thenReturn(factHandle);
 
 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);
 
 166         params = new ControlLoopParams();
 
 167         params.setClosedLoopControlName(CL_NAME);
 
 168         params.setPolicyName(POLICY_NAME);
 
 169         params.setPolicyScope(POLICY_SCOPE);
 
 170         params.setPolicyVersion(POLICY_VERSION);
 
 172         loadPolicy(EVENT_MGR_SIMPLE_YAML);
 
 174         locks = new ArrayList<>();
 
 176         mgr = new MyManager(params, event, workMem);
 
 180     public void testConstructor() {
 
 181         assertEquals(POLICY_NAME, mgr.getPolicyName());
 
 182         assertSame(event, mgr.getEvent());
 
 184         Map<String, String> orig = event.getAai();
 
 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");
 
 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");
 
 196         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
 
 199         event.setTarget("unknown-target");
 
 200         assertThatThrownBy(() -> new UsecasesEventManager(params, event, workMem))
 
 201                         .isInstanceOf(ControlLoopException.class);
 
 205     public void testIsActive() throws Exception {
 
 206         mgr = new UsecasesEventManager(params, event, workMem);
 
 207         assertTrue(mgr.isActive());
 
 209         // deserialized manager should be inactive
 
 210         UsecasesEventManager mgr2 = Serializer.roundTrip(mgr);
 
 211         assertFalse(mgr2.isActive());
 
 215     public void testDestroy_testGetSteps() {
 
 216         // add some steps to the queue
 
 217         mgr.getSteps().add(stepa);
 
 218         mgr.getSteps().add(stepb);
 
 222         verify(stepa).cancel();
 
 223         verify(stepb).cancel();
 
 225         // if superclass destroy() was invoked, then freeLock() should have been submitted
 
 227         verify(executor).execute(any());
 
 231     public void testOnStart() throws ControlLoopException {
 
 232         OperationOutcome outcome = makeOutcome();
 
 235         mgr.onStart(outcome);
 
 237         assertSame(outcome, mgr.getOutcomes().poll());
 
 238         assertThat(mgr.getOutcomes()).isEmpty();
 
 240         verify(workMem).update(factHandle, mgr);
 
 244     public void testOnComplete() throws ControlLoopException {
 
 245         OperationOutcome outcome = makeCompletedOutcome();
 
 248         mgr.onComplete(outcome);
 
 250         assertSame(outcome, mgr.getOutcomes().poll());
 
 251         assertThat(mgr.getOutcomes()).isEmpty();
 
 253         verify(workMem).update(factHandle, mgr);
 
 257     public void testToString() {
 
 258         assertNotNull(mgr.toString());
 
 262     public void testStart() throws ControlLoopException {
 
 267         assertThatCode(() -> mgr.start()).isInstanceOf(IllegalStateException.class)
 
 268                         .hasMessage("manager already started");
 
 272      * Tests start() when the manager is not in working memory.
 
 275     public void testStartNotInWorkingMemory() throws ControlLoopException {
 
 276         when(workMem.getFactHandle(any())).thenReturn(null);
 
 278         assertThatCode(() -> mgr.start()).isInstanceOf(IllegalStateException.class)
 
 279                         .hasMessage("manager is not in working memory");
 
 283      * Tests start() when the manager is not active.
 
 286     public void testStartInactive() throws Exception {
 
 287         // make an inactive manager by deserializing it
 
 288         mgr = Serializer.roundTrip(new UsecasesEventManager(params, event, workMem));
 
 291         assertThatCode(() -> mgr.start()).isInstanceOf(IllegalStateException.class)
 
 292                         .hasMessage("manager is no longer active");
 
 296     public void testAbort() {
 
 297         mgr.abort(UsecasesEventManager.State.DONE, OperationFinalResult.FINAL_FAILURE_GUARD, "some message");
 
 299         assertEquals(UsecasesEventManager.State.DONE, mgr.getState());
 
 300         assertEquals(OperationFinalResult.FINAL_FAILURE_GUARD, mgr.getFinalResult());
 
 301         assertEquals("some message", mgr.getFinalMessage());
 
 304         assertThatThrownBy(() -> mgr.abort(null, OperationFinalResult.FINAL_FAILURE_GUARD, ""))
 
 305                         .isInstanceOf(NullPointerException.class).hasMessageContaining("finalState");
 
 309     public void testLoadNextPolicy_testGetFullHistory_testGetPartialHistory() throws Exception {
 
 310         loadPolicy(EVENT_MGR_MULTI_YAML);
 
 311         mgr = new MyManager(params, event, workMem);
 
 313         // start and load step for first policy
 
 315         assertEquals("OperationA", mgr.getSteps().poll().getOperationName());
 
 316         assertNull(mgr.getFinalResult());
 
 319         OperationOutcome outcome = makeOutcome();
 
 320         mgr.addToHistory(outcome);
 
 322         // indicate success and load next policy
 
 323         mgr.loadNextPolicy(OperationResult.SUCCESS);
 
 324         assertEquals("OperationB", mgr.getSteps().poll().getOperationName());
 
 325         assertNull(mgr.getFinalResult());
 
 327         // loadPolicy() should clear the partial history, but not the full history
 
 328         assertThat(mgr.getPartialHistory()).isEmpty();
 
 329         assertThat(mgr.getFullHistory()).hasSize(1);
 
 331         // indicate failure - should go to final failure
 
 332         mgr.loadNextPolicy(OperationResult.FAILURE);
 
 333         assertEquals(OperationFinalResult.FINAL_FAILURE, mgr.getFinalResult());
 
 337     public void testLoadPolicy() throws ControlLoopException {
 
 338         // start() will invoke loadPolicy()
 
 341         assertNull(mgr.getFinalResult());
 
 343         Step2 step = mgr.getSteps().peek();
 
 345         assertEquals("First", step.getActorName());
 
 346         assertEquals("OperationA", step.getOperationName());
 
 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());
 
 362     public void testLoadPreprocessorSteps() {
 
 363         stepa = new Step2(mgr, ControlLoopOperationParams.builder().build(), event) {
 
 365             public List<String> getPropertyNames() {
 
 366                 return List.of(OperationProperties.AAI_DEFAULT_CLOUD_REGION);
 
 370             protected Operation buildOperation() {
 
 371                 return policyOperation;
 
 375         mgr.getSteps().add(stepa);
 
 376         mgr.getSteps().add(stepb);
 
 378         mgr.loadPreprocessorSteps();
 
 380         Deque<Step2> steps = mgr.getSteps();
 
 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();
 
 393      * Tests loadPreprocessorSteps() when there are too many steps in the queue.
 
 396     public void testLoadPreprocessorStepsTooManySteps() {
 
 397         loadStepsWithProperties(OperationProperties.AAI_PNF);
 
 399         Deque<Step2> steps = mgr.getSteps();
 
 400         stepa = steps.getFirst();
 
 403         // load up a bunch of steps
 
 404         for (int nsteps = 0; nsteps < UsecasesEventManager.MAX_STEPS; ++nsteps) {
 
 409         assertThatIllegalStateException().isThrownBy(() -> mgr.loadPreprocessorSteps()).withMessage("too many steps");
 
 411         // add another step, should still fail
 
 413         assertThatIllegalStateException().isThrownBy(() -> mgr.loadPreprocessorSteps()).withMessage("too many steps");
 
 415         // remove two steps - should now succeed
 
 419         int nsteps = steps.size();
 
 421         mgr.loadPreprocessorSteps();
 
 422         assertEquals(nsteps + 1, steps.size());
 
 426      * Tests loadPreprocessorSteps() when no additional steps are needed.
 
 429     public void testLoadPreprocessorStepsNothingToLoad() {
 
 430         when(stepa.isPolicyStep()).thenReturn(false);
 
 431         when(stepa.getPropertyNames()).thenReturn(List.of("unknown-property"));
 
 433         Deque<Step2> steps = mgr.getSteps();
 
 438         mgr.loadPreprocessorSteps();
 
 440         assertSame(stepa, steps.poll());
 
 441         assertSame(stepb, steps.poll());
 
 442         assertThat(steps).isEmpty();
 
 446      * Tests loadPreprocessorSteps() when an A&AI custom query is needed.
 
 449     public void testLoadPreprocessorStepsCq() {
 
 450         loadStepsWithProperties(OperationProperties.AAI_DEFAULT_CLOUD_REGION, OperationProperties.AAI_DEFAULT_TENANT);
 
 453         mgr.loadPreprocessorSteps();
 
 455         Deque<Step2> steps = mgr.getSteps();
 
 457         assertThat(steps.poll()).isInstanceOf(AaiCqStep2.class);
 
 458         assertSame(stepa, steps.poll());
 
 459         assertSame(stepb, steps.poll());
 
 460         assertThat(steps).isEmpty();
 
 464      * Tests loadPreprocessorSteps() when an A&AI PNF query is needed.
 
 467     public void testLoadPreprocessorStepsPnf() {
 
 468         // doubling up the property to check both branches
 
 469         loadStepsWithProperties(OperationProperties.AAI_PNF, OperationProperties.AAI_PNF);
 
 472         mgr.loadPreprocessorSteps();
 
 474         Deque<Step2> steps = mgr.getSteps();
 
 476         assertThat(steps.poll()).isInstanceOf(AaiGetPnfStep2.class);
 
 477         assertSame(stepa, steps.poll());
 
 478         assertSame(stepb, steps.poll());
 
 479         assertThat(steps).isEmpty();
 
 483      * Tests loadPreprocessorSteps() when an A&AI Tenant query is needed.
 
 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);
 
 492         mgr.loadPreprocessorSteps();
 
 494         Deque<Step2> steps = mgr.getSteps();
 
 496         assertThat(steps.poll()).isInstanceOf(AaiGetTenantStep2.class);
 
 497         assertSame(stepa, steps.poll());
 
 498         assertSame(stepb, steps.poll());
 
 499         assertThat(steps).isEmpty();
 
 503      * Tests loadPreprocessorSteps() when the target entity is unset.
 
 506     public void testLoadPreprocessorStepsNeedTargetEntity() {
 
 507         stepa = new Step2(mgr,
 
 508                 ControlLoopOperationParams.builder()
 
 509                         .targetType(TargetType.toTargetType(event.getTargetType()))
 
 510                         .targetEntityIds(Map.of()).build(), event) {
 
 512             public List<String> getPropertyNames() {
 
 513                 return List.of(OperationProperties.AAI_TARGET_ENTITY);
 
 517             protected Operation buildOperation() {
 
 518                 return policyOperation;
 
 522             public boolean isPolicyStep() {
 
 527         Deque<Step2> steps = mgr.getSteps();
 
 531         mgr.loadPreprocessorSteps();
 
 533         Step2 entityStep = steps.poll();
 
 535         assertThat(entityStep).isInstanceOf(GetTargetEntityStep2.class);
 
 536         assertSame(stepa, steps.poll());
 
 537         assertSame(stepb, steps.poll());
 
 538         assertThat(steps).isEmpty();
 
 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();
 
 548     public void testExecuteStep() {
 
 551         // no steps to execute
 
 552         assertFalse(mgr.executeStep());
 
 553         assertEquals(0, mgr.getAttempts());
 
 555         // add a step to the queue
 
 556         mgr.getSteps().add(stepa);
 
 558         // step returns false
 
 559         when(stepa.start(anyLong())).thenReturn(false);
 
 560         assertFalse(mgr.executeStep());
 
 563         when(stepa.start(anyLong())).thenReturn(true);
 
 564         assertTrue(mgr.executeStep());
 
 568     public void testNextStep() {
 
 569         mgr.getSteps().add(stepa);
 
 573         assertThat(mgr.getSteps()).isEmpty();
 
 577     public void testBumpAttempts() {
 
 578         assertEquals(0, mgr.getAttempts());
 
 582         assertEquals(2, mgr.getAttempts());
 
 586     public void testIsAbort() {
 
 587         OperationOutcome outcome = makeCompletedOutcome();
 
 588         outcome.setResult(OperationResult.FAILURE);
 
 590         // closed loop timeout
 
 591         outcome.setActor(ActorConstants.CL_TIMEOUT_ACTOR);
 
 592         assertTrue(mgr.isAbort(outcome));
 
 595         outcome.setActor(ActorConstants.LOCK_ACTOR);
 
 596         assertTrue(mgr.isAbort(outcome));
 
 598         // no effect for success
 
 599         outcome.setResult(OperationResult.SUCCESS);
 
 600         assertFalse(mgr.isAbort(outcome));
 
 604     public void testAddToHistory() throws ControlLoopException {
 
 607         // add a "start" outcome
 
 608         OperationOutcome outcome = makeOutcome();
 
 609         mgr.addToHistory(outcome);
 
 611         assertThat(mgr.getPartialHistory()).hasSize(1);
 
 612         assertThat(mgr.getFullHistory()).hasSize(1);
 
 614         // add a "completion" outcome - should replace the start
 
 615         outcome = makeCompletedOutcome();
 
 616         mgr.addToHistory(outcome);
 
 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());
 
 624         outcome = makeOutcome();
 
 625         mgr.addToHistory(outcome);
 
 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());
 
 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);
 
 639         // add another "start"
 
 640         outcome = makeOutcome();
 
 641         mgr.addToHistory(outcome);
 
 642         assertThat(mgr.getPartialHistory()).hasSize(3);
 
 643         assertThat(mgr.getFullHistory()).hasSize(3);
 
 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());
 
 656     public void testMakeNotification() throws Exception {
 
 657         loadPolicy(EVENT_MGR_MULTI_YAML);
 
 658         mgr = new MyManager(params, event, workMem);
 
 661         assertNotNull(mgr.makeNotification());
 
 665         mgr.addToHistory(makeCompletedOutcome());
 
 666         mgr.addToHistory(makeCompletedOutcome());
 
 667         mgr.addToHistory(makeCompletedOutcome());
 
 669         // check notification while running
 
 670         VirtualControlLoopNotification notif = mgr.makeNotification();
 
 671         assertThat(notif.getMessage()).contains(SIMPLE_ACTOR);
 
 672         assertThat(notif.getHistory()).hasSize(3);
 
 674         // indicate success and load the next policy - should clear the partial history
 
 675         mgr.loadNextPolicy(OperationResult.SUCCESS);
 
 677         // check notification
 
 678         notif = mgr.makeNotification();
 
 679         assertNull(notif.getMessage());
 
 680         assertThat(notif.getHistory()).isEmpty();
 
 682         // add outcomes and check again
 
 683         mgr.addToHistory(makeCompletedOutcome());
 
 684         mgr.addToHistory(makeCompletedOutcome());
 
 686         notif = mgr.makeNotification();
 
 687         assertNotNull(notif.getMessage());
 
 689         // should only have history for last two outcomes
 
 690         assertThat(notif.getHistory()).hasSize(2);
 
 692         // indicate failure - should go to final state
 
 693         mgr.loadNextPolicy(OperationResult.FAILURE);
 
 695         // check notification
 
 696         notif = mgr.makeNotification();
 
 697         assertNull(notif.getMessage());
 
 699         // should be no history
 
 700         assertThat(notif.getHistory()).isEmpty();
 
 703         assertThatThrownBy(() -> mgr.loadNextPolicy(null)).isInstanceOf(NullPointerException.class)
 
 704                         .hasMessageContaining("lastResult");
 
 708     public void testDeliver() {
 
 709         mgr.deliver(MY_SINK, null, "null notification", "null rule");
 
 710         verify(engineMgr, never()).deliver(any(), any());
 
 712         mgr.deliver(MY_SINK, "publishA", "A notification", "A rule");
 
 713         verify(engineMgr).deliver(MY_SINK, "publishA");
 
 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();
 
 721     public void testGetOperationMessage() throws ControlLoopException {
 
 723         assertNull(mgr.getOperationMessage());
 
 727         OperationOutcome outcome = makeOutcome();
 
 728         mgr.addToHistory(outcome);
 
 730         assertThat(mgr.getOperationMessage()).contains("actor=" + SIMPLE_ACTOR)
 
 731                         .contains("operation=" + SIMPLE_OPERATION);
 
 735     public void testStoreInDataBase() throws ControlLoopException {
 
 737         OperationOutcome outcome = makeOutcome();
 
 738         mgr.addToHistory(outcome);
 
 740         mgr.storeInDataBase(mgr.getPartialHistory().peekLast());
 
 742         verify(dataMgr).store(REQ_ID.toString(), event, null, mgr.getPartialHistory().peekLast().getClOperation());
 
 746     public void testMakeControlLoopResponse() {
 
 747         final OperationOutcome outcome = new OperationOutcome();
 
 749         // no message - should return null
 
 750         checkResp(outcome, null);
 
 752         // not a PciMessage - should return null
 
 753         outcome.setResponse("not-a-pci-message");
 
 754         checkResp(outcome, null);
 
 757          * now work with a PciMessage
 
 759         PciMessage msg = new PciMessage();
 
 760         outcome.setResponse(msg);
 
 762         PciBody body = new PciBody();
 
 765         PciResponse output = new PciResponse();
 
 766         body.setOutput(output);
 
 768         output.setPayload("my-payload");
 
 770         // should generate a response, with a payload
 
 771         checkResp(outcome, "my-payload");
 
 774          * these should generate a response, with null payload
 
 776         output.setPayload(null);
 
 777         checkResp(outcome, null);
 
 779         body.setOutput(null);
 
 780         checkResp(outcome, null);
 
 783         checkResp(outcome, null);
 
 785         outcome.setResponse(null);
 
 786         checkResp(outcome, null);
 
 790     public void testOnNewEvent() {
 
 791         VirtualControlLoopEvent event2 = new VirtualControlLoopEvent(event);
 
 792         assertEquals(NewEventStatus.FIRST_ONSET, mgr.onNewEvent(event2));
 
 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));
 
 799         event2.setClosedLoopEventStatus(ControlLoopEventStatus.ABATED);
 
 800         assertEquals(NewEventStatus.FIRST_ABATEMENT, mgr.onNewEvent(event2));
 
 802         assertEquals(NewEventStatus.SUBSEQUENT_ABATEMENT, mgr.onNewEvent(event2));
 
 803         assertEquals(NewEventStatus.SUBSEQUENT_ABATEMENT, mgr.onNewEvent(event2));
 
 805         event2.setClosedLoopEventStatus(null);
 
 806         assertEquals(NewEventStatus.SYNTAX_ERROR, mgr.onNewEvent(event2));
 
 810     public void testCheckEventSyntax() {
 
 811         // initially, it's valid
 
 812         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
 
 814         event.setTarget("unknown-target");
 
 815         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
 
 816                         .hasMessage("target field invalid");
 
 818         event.setTarget(null);
 
 819         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
 
 820                         .hasMessage("No target field");
 
 822         // abated supersedes previous errors - so it shouldn't throw an exception
 
 823         event.setClosedLoopEventStatus(ControlLoopEventStatus.ABATED);
 
 824         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
 
 826         event.setRequestId(null);
 
 827         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
 
 828                         .hasMessage("No request ID");
 
 830         event.setClosedLoopControlName(null);
 
 831         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
 
 832                         .hasMessage("No control loop name");
 
 836     public void testValidateStatus() {
 
 837         event.setClosedLoopEventStatus(ControlLoopEventStatus.ONSET);
 
 838         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
 
 840         event.setClosedLoopEventStatus(ControlLoopEventStatus.ABATED);
 
 841         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
 
 843         event.setClosedLoopEventStatus(null);
 
 844         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
 
 845                         .hasMessage("Invalid value in closedLoopEventStatus");
 
 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");
 
 854         event.setTargetType(null);
 
 855         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
 
 856                         .hasMessage("The Target type is null");
 
 859         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
 
 860                         .hasMessage("AAI is null");
 
 863         event.setTargetType(ControlLoopTargetType.VM);
 
 864         event.setAai(Map.of(ControlLoopEventManager2.GENERIC_VNF_VNF_ID, MY_TARGET));
 
 865         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
 
 867         event.setAai(Map.of());
 
 868         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class);
 
 871         event.setTargetType(ControlLoopTargetType.VNF);
 
 872         event.setAai(Map.of(ControlLoopEventManager2.GENERIC_VNF_VNF_ID, MY_TARGET));
 
 873         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
 
 875         event.setAai(Map.of());
 
 876         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class);
 
 879         event.setTargetType(ControlLoopTargetType.PNF);
 
 880         event.setAai(Map.of(ControlLoopEventManager2.PNF_NAME, MY_TARGET));
 
 881         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
 
 883         event.setAai(Map.of());
 
 884         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class);
 
 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();
 
 893         event.setAai(Map.of(ControlLoopEventManager2.VSERVER_VSERVER_NAME, MY_TARGET));
 
 894         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
 
 896         event.setAai(Map.of(ControlLoopEventManager2.GENERIC_VNF_VNF_NAME, MY_TARGET));
 
 897         assertThatCode(() -> mgr.checkEventSyntax(event)).doesNotThrowAnyException();
 
 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");
 
 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();
 
 910         event.setAai(Map.of());
 
 911         assertThatCode(() -> mgr.checkEventSyntax(event)).isInstanceOf(ControlLoopException.class)
 
 912                         .hasMessage("AAI PNF object key pnf-name is missing");
 
 916     public void testIsClosedLoopDisabled() {
 
 917         Map<String, String> orig = event.getAai();
 
 919         event.setAai(addAai(orig, ControlLoopEventManager2.VSERVER_IS_CLOSED_LOOP_DISABLED, "true"));
 
 920         assertThatThrownBy(() -> new ControlLoopEventManager2(params, event, workMem))
 
 921                         .isInstanceOf(IllegalStateException.class);
 
 923         event.setAai(addAai(orig, ControlLoopEventManager2.GENERIC_VNF_IS_CLOSED_LOOP_DISABLED, "true"));
 
 924         assertThatThrownBy(() -> new ControlLoopEventManager2(params, event, workMem))
 
 925                         .isInstanceOf(IllegalStateException.class);
 
 927         event.setAai(addAai(orig, ControlLoopEventManager2.PNF_IS_IN_MAINT, "true"));
 
 928         assertThatThrownBy(() -> new ControlLoopEventManager2(params, event, workMem))
 
 929                         .isInstanceOf(IllegalStateException.class);
 
 933     public void testIsProvStatusInactive() {
 
 934         Map<String, String> orig = event.getAai();
 
 936         event.setAai(addAai(orig, ControlLoopEventManager2.VSERVER_PROV_STATUS, "ACTIVE"));
 
 937         assertThatCode(() -> new ControlLoopEventManager2(params, event, workMem)).doesNotThrowAnyException();
 
 939         event.setAai(addAai(orig, ControlLoopEventManager2.VSERVER_PROV_STATUS, "inactive"));
 
 940         assertThatThrownBy(() -> new ControlLoopEventManager2(params, event, workMem))
 
 941                         .isInstanceOf(IllegalStateException.class);
 
 943         event.setAai(addAai(orig, ControlLoopEventManager2.GENERIC_VNF_PROV_STATUS, "ACTIVE"));
 
 944         assertThatCode(() -> new ControlLoopEventManager2(params, event, workMem)).doesNotThrowAnyException();
 
 946         event.setAai(addAai(orig, ControlLoopEventManager2.GENERIC_VNF_PROV_STATUS, "inactive"));
 
 947         assertThatThrownBy(() -> new ControlLoopEventManager2(params, event, workMem))
 
 948                         .isInstanceOf(IllegalStateException.class);
 
 952     public void testIsAaiTrue() {
 
 953         Map<String, String> orig = event.getAai();
 
 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);
 
 961         event.setAai(addAai(orig, ControlLoopEventManager2.VSERVER_IS_CLOSED_LOOP_DISABLED, "false"));
 
 962         assertThatCode(() -> new ControlLoopEventManager2(params, event, workMem)).doesNotThrowAnyException();
 
 964         event.setAai(addAai(orig, ControlLoopEventManager2.VSERVER_IS_CLOSED_LOOP_DISABLED, "no"));
 
 965         assertThatCode(() -> new ControlLoopEventManager2(params, event, workMem)).doesNotThrowAnyException();
 
 969     private Map<String, String> addAai(Map<String, String> original, String key, String value) {
 
 970         Map<String, String> map = new TreeMap<>(original);
 
 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();
 
 980         params.setToscaPolicy(tosca);
 
 983     private void loadStepsWithProperties(String... properties) {
 
 984         stepa = new Step2(mgr, ControlLoopOperationParams.builder().build(), event) {
 
 987             public boolean isPolicyStep() {
 
 992             public List<String> getPropertyNames() {
 
 993                 return List.of(properties);
 
 997             protected Operation buildOperation() {
 
 998                 return policyOperation;
 
1002         mgr.getSteps().add(stepa);
 
1003         mgr.getSteps().add(stepb);
 
1006     private OperationOutcome makeCompletedOutcome() {
 
1007         OperationOutcome outcome = makeOutcome();
 
1008         outcome.setEnd(outcome.getStart());
 
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);
 
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());
 
1033      * Sets the target entity so a step doesn't have to be added to set it.
 
1035     private void setTargetEntity() {
 
1036         mgr.setProperty(OperationProperties.AAI_TARGET_ENTITY, MY_TARGET);
 
1040     private class MyManager extends UsecasesEventManager {
 
1041         private static final long serialVersionUID = 1L;
 
1043         public MyManager(ControlLoopParams params, VirtualControlLoopEvent event, WorkingMemory workMem)
 
1044                         throws ControlLoopException {
 
1046             super(params, event, workMem);
 
1050         protected ExecutorService getBlockingExecutor() {
 
1055         protected void makeLock(String targetEntity, String requestId, int holdSec, LockCallback callback) {
 
1056             LockImpl lock = new LockImpl(LockState.ACTIVE, targetEntity, requestId, holdSec, callback);
 
1058             callback.lockAvailable(lock);
 
1062         public ActorService getActorService() {
 
1067         public OperationHistoryDataManager getDataManager() {
 
1072         protected PolicyEngine getPolicyEngineManager() {