2  * ============LICENSE_START=======================================================
 
   3  *  Copyright (C) 2023-2025 OpenInfra Foundation Europe. All rights reserved.
 
   4  * ================================================================================
 
   5  * Licensed under the Apache License, Version 2.0 (the "License");
 
   6  * you may not use this file except in compliance with the License.
 
   7  * You may obtain a copy of the License at
 
   9  *      http://www.apache.org/licenses/LICENSE-2.0
 
  11  * Unless required by applicable law or agreed to in writing, software
 
  12  * distributed under the License is distributed on an "AS IS" BASIS,
 
  13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
  14  * See the License for the specific language governing permissions and
 
  15  * limitations under the License.
 
  17  * SPDX-License-Identifier: Apache-2.0
 
  18  * ============LICENSE_END=========================================================
 
  21 package org.onap.policy.clamp.acm.participant.intermediary.handler;
 
  23 import static org.mockito.Mockito.clearInvocations;
 
  24 import static org.mockito.Mockito.doThrow;
 
  25 import static org.mockito.Mockito.mock;
 
  26 import static org.mockito.Mockito.timeout;
 
  27 import static org.mockito.Mockito.verify;
 
  29 import jakarta.ws.rs.core.Response.Status;
 
  30 import java.io.IOException;
 
  32 import java.util.UUID;
 
  33 import org.junit.jupiter.api.Test;
 
  34 import org.onap.policy.clamp.acm.participant.intermediary.api.AutomationCompositionElementListener;
 
  35 import org.onap.policy.clamp.acm.participant.intermediary.api.CompositionDto;
 
  36 import org.onap.policy.clamp.acm.participant.intermediary.api.CompositionElementDto;
 
  37 import org.onap.policy.clamp.acm.participant.intermediary.api.InstanceElementDto;
 
  38 import org.onap.policy.clamp.acm.participant.intermediary.api.ParticipantIntermediaryApi;
 
  39 import org.onap.policy.clamp.acm.participant.intermediary.main.parameters.CommonTestData;
 
  40 import org.onap.policy.clamp.models.acm.concepts.AcElementDeploy;
 
  41 import org.onap.policy.clamp.models.acm.concepts.AcTypeState;
 
  42 import org.onap.policy.clamp.models.acm.concepts.DeployState;
 
  43 import org.onap.policy.clamp.models.acm.concepts.LockState;
 
  44 import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
 
  45 import org.onap.policy.models.base.PfModelException;
 
  46 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
 
  48 class ThreadHandlerTest {
 
  50     private static final int TIMEOUT = 400;
 
  52     private ThreadHandler createThreadHandler(AutomationCompositionElementListener listener,
 
  53             ParticipantIntermediaryApi intermediaryApi) {
 
  54         return new ThreadHandler(listener, intermediaryApi, mock(CacheProvider.class),
 
  55                 CommonTestData.getParticipantParameters());
 
  59     void testPrime() throws PfModelException, IOException {
 
  60         var listener = mock(AutomationCompositionElementListener.class);
 
  61         var intermediaryApi = mock(ParticipantIntermediaryApi.class);
 
  62         try (var threadHandler = createThreadHandler(listener, intermediaryApi)) {
 
  64             var compositionId = UUID.randomUUID();
 
  65             var messageId = UUID.randomUUID();
 
  66             var composition = new CompositionDto(compositionId, Map.of(), Map.of());
 
  67             threadHandler.prime(messageId, composition);
 
  68             verify(listener, timeout(TIMEOUT)).prime(composition);
 
  70             clearInvocations(listener);
 
  71             threadHandler.deprime(messageId, composition);
 
  72             verify(listener, timeout(TIMEOUT)).deprime(composition);
 
  77     void testPrimeException() throws PfModelException, IOException {
 
  78         var listener = mock(AutomationCompositionElementListener.class);
 
  79         var intermediaryApi = mock(ParticipantIntermediaryApi.class);
 
  80         try (var threadHandler = createThreadHandler(listener, intermediaryApi)) {
 
  82             var compositionId = UUID.randomUUID();
 
  83             var composition = new CompositionDto(compositionId, Map.of(), Map.of());
 
  84             doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener).prime(composition);
 
  85             var messageId = UUID.randomUUID();
 
  86             threadHandler.prime(messageId, composition);
 
  87             verify(intermediaryApi, timeout(TIMEOUT)).updateCompositionState(compositionId, AcTypeState.COMMISSIONED,
 
  88                 StateChangeResult.FAILED, "Composition Defintion prime failed");
 
  90             clearInvocations(listener);
 
  91             doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener).deprime(composition);
 
  92             threadHandler.deprime(messageId, composition);
 
  93             verify(intermediaryApi, timeout(TIMEOUT)).updateCompositionState(compositionId, AcTypeState.PRIMED,
 
  94                 StateChangeResult.FAILED, "Composition Defintion deprime failed");
 
  99     void testDeploy() throws PfModelException, IOException {
 
 100         var listener = mock(AutomationCompositionElementListener.class);
 
 101         var intermediaryApi = mock(ParticipantIntermediaryApi.class);
 
 102         try (var threadHandler = createThreadHandler(listener, intermediaryApi)) {
 
 104             Map<String, Object> properties = Map.of("key", "value");
 
 105             var compositionElement = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(),
 
 106                 properties, properties);
 
 107             var instanceElement = new InstanceElementDto(UUID.randomUUID(), UUID.randomUUID(),
 
 108                 properties, properties);
 
 109             var messageId = UUID.randomUUID();
 
 110             threadHandler.deploy(messageId, compositionElement, instanceElement);
 
 111             verify(listener, timeout(TIMEOUT)).deploy(compositionElement, instanceElement);
 
 113             clearInvocations(listener);
 
 114             var element = new AcElementDeploy();
 
 115             var elementId = UUID.randomUUID();
 
 116             element.setId(elementId);
 
 117             var instanceElementUpdated = new InstanceElementDto(instanceElement.instanceId(),
 
 118                 instanceElement.elementId(), properties, properties);
 
 119             threadHandler.update(messageId, compositionElement, instanceElement, instanceElementUpdated);
 
 120             verify(listener, timeout(TIMEOUT)).update(compositionElement, instanceElement, instanceElementUpdated);
 
 122             clearInvocations(listener);
 
 123             var compositionTargetId = UUID.randomUUID();
 
 124             var compositionElementTarget = new CompositionElementDto(compositionTargetId, new ToscaConceptIdentifier(),
 
 125                 properties, properties);
 
 126             threadHandler.migrate(messageId, compositionElement, compositionElementTarget,
 
 127                 instanceElement, instanceElementUpdated, 0);
 
 128             verify(listener, timeout(TIMEOUT)).migrate(compositionElement, compositionElementTarget,
 
 129                 instanceElement, instanceElementUpdated, 0);
 
 131             clearInvocations(listener);
 
 132             threadHandler.undeploy(messageId, compositionElement, instanceElement);
 
 133             verify(listener, timeout(TIMEOUT)).undeploy(compositionElement, instanceElement);
 
 135             clearInvocations(listener);
 
 136             threadHandler.delete(messageId, compositionElement, instanceElement);
 
 137             verify(listener, timeout(TIMEOUT)).delete(compositionElement, instanceElement);
 
 142     void testDeployException() throws PfModelException, IOException {
 
 143         var listener = mock(AutomationCompositionElementListener.class);
 
 144         var intermediaryApi = mock(ParticipantIntermediaryApi.class);
 
 145         try (var threadHandler = createThreadHandler(listener, intermediaryApi)) {
 
 147             Map<String, Object> properties = Map.of("key", "value");
 
 148             var compositionElement = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(),
 
 149                 properties, properties);
 
 150             var instanceId = UUID.randomUUID();
 
 151             var elementId = UUID.randomUUID();
 
 152             var instanceElement = new InstanceElementDto(instanceId, elementId, properties, properties);
 
 153             var element = new AcElementDeploy();
 
 154             element.setId(elementId);
 
 155             doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
 
 156                 .deploy(compositionElement, instanceElement);
 
 157             var messageId = UUID.randomUUID();
 
 158             threadHandler.deploy(messageId, compositionElement, instanceElement);
 
 159             verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
 
 160                 DeployState.UNDEPLOYED, null, StateChangeResult.FAILED,
 
 161                     "Automation composition element deploy failed");
 
 163             clearInvocations(listener);
 
 164             var instanceElementUpdated = new InstanceElementDto(instanceElement.instanceId(),
 
 165                 instanceElement.elementId(), properties, properties);
 
 166             doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
 
 167                 .update(compositionElement, instanceElement, instanceElementUpdated);
 
 168             threadHandler.update(messageId, compositionElement, instanceElement, instanceElementUpdated);
 
 169             verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
 
 170                 DeployState.DEPLOYED, null, StateChangeResult.FAILED,
 
 171                 "Automation composition element update failed");
 
 173             clearInvocations(listener);
 
 174             var compositionTargetId = UUID.randomUUID();
 
 175             var compositionElementTarget = new CompositionElementDto(compositionTargetId, new ToscaConceptIdentifier(),
 
 176                 properties, properties);
 
 177             doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
 
 178                 .migrate(compositionElement, compositionElementTarget, instanceElement, instanceElementUpdated, 0);
 
 179             threadHandler.migrate(messageId, compositionElement, compositionElementTarget,
 
 180                 instanceElement, instanceElementUpdated, 0);
 
 181             verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
 
 182                 DeployState.DEPLOYED, null, StateChangeResult.FAILED,
 
 183                 "Automation composition element migrate failed");
 
 185             clearInvocations(listener);
 
 186             doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
 
 187                 .undeploy(compositionElement, instanceElement);
 
 188             threadHandler.undeploy(messageId, compositionElement, instanceElement);
 
 189             verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
 
 190                 DeployState.DEPLOYED, null, StateChangeResult.FAILED,
 
 191                 "Automation composition element undeploy failed");
 
 193             clearInvocations(listener);
 
 194             doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
 
 195                 .delete(compositionElement, instanceElement);
 
 196             threadHandler.delete(messageId, compositionElement, instanceElement);
 
 197             verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
 
 198                     DeployState.UNDEPLOYED, null, StateChangeResult.FAILED,
 
 199                     "Automation composition element delete failed");
 
 204     void testLock() throws PfModelException, IOException {
 
 205         var listener = mock(AutomationCompositionElementListener.class);
 
 206         var intermediaryApi = mock(ParticipantIntermediaryApi.class);
 
 207         try (var threadHandler = createThreadHandler(listener, intermediaryApi)) {
 
 209             Map<String, Object> properties = Map.of("key", "value");
 
 210             var compositionElement = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(),
 
 211                 properties, properties);
 
 212             var instanceElement = new InstanceElementDto(UUID.randomUUID(), UUID.randomUUID(),
 
 213                 properties, properties);
 
 214             var messageId = UUID.randomUUID();
 
 215             threadHandler.lock(messageId, compositionElement, instanceElement);
 
 216             verify(listener, timeout(TIMEOUT)).lock(compositionElement, instanceElement);
 
 218             clearInvocations(listener);
 
 219             threadHandler.unlock(messageId, compositionElement, instanceElement);
 
 220             verify(listener, timeout(TIMEOUT)).unlock(compositionElement, instanceElement);
 
 222             clearInvocations(listener);
 
 223             threadHandler.undeploy(messageId, compositionElement, instanceElement);
 
 224             verify(listener, timeout(TIMEOUT)).undeploy(compositionElement, instanceElement);
 
 229     void testLockException() throws PfModelException, IOException {
 
 230         var listener = mock(AutomationCompositionElementListener.class);
 
 231         var intermediaryApi = mock(ParticipantIntermediaryApi.class);
 
 232         try (var threadHandler = createThreadHandler(listener, intermediaryApi)) {
 
 234             Map<String, Object> properties = Map.of("key", "value");
 
 235             var compositionElement = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(),
 
 236                 properties, properties);
 
 237             var instanceId = UUID.randomUUID();
 
 238             var elementId = UUID.randomUUID();
 
 239             var instanceElement = new InstanceElementDto(instanceId, elementId, properties, properties);
 
 240             var element = new AcElementDeploy();
 
 241             element.setId(elementId);
 
 242             var messageId = UUID.randomUUID();
 
 243             doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
 
 244                 .lock(compositionElement, instanceElement);
 
 245             threadHandler.lock(messageId, compositionElement, instanceElement);
 
 246             verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
 
 247                 null, LockState.UNLOCKED, StateChangeResult.FAILED, "Automation composition element lock failed");
 
 249             clearInvocations(listener);
 
 250             doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
 
 251                 .unlock(compositionElement, instanceElement);
 
 252             threadHandler.unlock(messageId, compositionElement, instanceElement);
 
 253             verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
 
 254                 null, LockState.LOCKED, StateChangeResult.FAILED, "Automation composition element unlock failed");
 
 259     void testSubState() throws PfModelException, IOException {
 
 260         var listener = mock(AutomationCompositionElementListener.class);
 
 261         var intermediaryApi = mock(ParticipantIntermediaryApi.class);
 
 262         try (var threadHandler = createThreadHandler(listener, intermediaryApi)) {
 
 264             Map<String, Object> properties = Map.of("key", "value");
 
 265             var compositionElement = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(),
 
 266                 properties, properties);
 
 267             var instanceElement = new InstanceElementDto(UUID.randomUUID(), UUID.randomUUID(),
 
 268                 properties, properties);
 
 269             var messageId = UUID.randomUUID();
 
 270             threadHandler.prepare(messageId, compositionElement, instanceElement, 0);
 
 271             verify(listener, timeout(TIMEOUT)).prepare(compositionElement, instanceElement, 0);
 
 273             clearInvocations(listener);
 
 274             threadHandler.review(messageId, compositionElement, instanceElement);
 
 275             verify(listener, timeout(TIMEOUT)).review(compositionElement, instanceElement);
 
 277             clearInvocations(listener);
 
 278             var instanceElementMigrate = new InstanceElementDto(instanceElement.instanceId(),
 
 279                 instanceElement.elementId(), properties, properties);
 
 280             var compositionTargetId = UUID.randomUUID();
 
 281             var compositionElementTarget = new CompositionElementDto(compositionTargetId, new ToscaConceptIdentifier(),
 
 282                 properties, properties);
 
 283             threadHandler.migratePrecheck(messageId, compositionElement, compositionElementTarget,
 
 284                 instanceElement, instanceElementMigrate);
 
 285             verify(listener, timeout(TIMEOUT)).migratePrecheck(compositionElement, compositionElementTarget,
 
 286                 instanceElement, instanceElementMigrate);
 
 291     void testSubStateException() throws PfModelException, IOException {
 
 292         var listener = mock(AutomationCompositionElementListener.class);
 
 293         var intermediaryApi = mock(ParticipantIntermediaryApi.class);
 
 294         try (var threadHandler = createThreadHandler(listener, intermediaryApi)) {
 
 296             Map<String, Object> properties = Map.of("key", "value");
 
 297             var compositionElement = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(),
 
 298                 properties, properties);
 
 299             var instanceId = UUID.randomUUID();
 
 300             var elementId = UUID.randomUUID();
 
 301             var instanceElement = new InstanceElementDto(instanceId, elementId, properties, properties);
 
 302             var element = new AcElementDeploy();
 
 303             element.setId(elementId);
 
 304             doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
 
 305                 .prepare(compositionElement, instanceElement, 0);
 
 306             var messageId = UUID.randomUUID();
 
 307             threadHandler.prepare(messageId, compositionElement, instanceElement, 0);
 
 308             verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
 
 309                 DeployState.UNDEPLOYED, null, StateChangeResult.FAILED,
 
 310                 "Automation composition element prepare Pre Deploy failed");
 
 312             clearInvocations(listener);
 
 313             doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
 
 314                 .review(compositionElement, instanceElement);
 
 315             threadHandler.review(messageId, compositionElement, instanceElement);
 
 316             verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
 
 317                 DeployState.DEPLOYED, null, StateChangeResult.FAILED,
 
 318                 "Automation composition element Review failed");
 
 320             clearInvocations(listener);
 
 321             var compositionTargetId = UUID.randomUUID();
 
 322             var compositionElementTarget = new CompositionElementDto(compositionTargetId, new ToscaConceptIdentifier(),
 
 323                 properties, properties);
 
 324             var instanceElementMigrate = new InstanceElementDto(instanceElement.instanceId(),
 
 325                 instanceElement.elementId(), properties, properties);
 
 326             doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
 
 327                 .migratePrecheck(compositionElement, compositionElementTarget, instanceElement, instanceElementMigrate);
 
 328             threadHandler.migratePrecheck(messageId, compositionElement, compositionElementTarget,
 
 329                 instanceElement, instanceElementMigrate);
 
 330             verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
 
 331                 DeployState.DEPLOYED, null, StateChangeResult.FAILED,
 
 332                 "Automation composition element migrate precheck failed");