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.rollback(messageId, compositionElement, instanceElement, 0);
 
 133             verify(listener, timeout(TIMEOUT)).rollbackMigration(compositionElement, instanceElement, 0);
 
 136             clearInvocations(listener);
 
 137             threadHandler.undeploy(messageId, compositionElement, instanceElement);
 
 138             verify(listener, timeout(TIMEOUT)).undeploy(compositionElement, instanceElement);
 
 140             clearInvocations(listener);
 
 141             threadHandler.delete(messageId, compositionElement, instanceElement);
 
 142             verify(listener, timeout(TIMEOUT)).delete(compositionElement, instanceElement);
 
 147     void testDeployException() throws PfModelException, IOException {
 
 148         var listener = mock(AutomationCompositionElementListener.class);
 
 149         var intermediaryApi = mock(ParticipantIntermediaryApi.class);
 
 150         try (var threadHandler = createThreadHandler(listener, intermediaryApi)) {
 
 152             Map<String, Object> properties = Map.of("key", "value");
 
 153             var compositionElement = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(),
 
 154                 properties, properties);
 
 155             var instanceId = UUID.randomUUID();
 
 156             var elementId = UUID.randomUUID();
 
 157             var instanceElement = new InstanceElementDto(instanceId, elementId, properties, properties);
 
 158             var element = new AcElementDeploy();
 
 159             element.setId(elementId);
 
 160             doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
 
 161                 .deploy(compositionElement, instanceElement);
 
 162             var messageId = UUID.randomUUID();
 
 163             threadHandler.deploy(messageId, compositionElement, instanceElement);
 
 164             verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
 
 165                 DeployState.UNDEPLOYED, null, StateChangeResult.FAILED,
 
 166                     "Automation composition element deploy failed");
 
 168             clearInvocations(listener);
 
 169             var instanceElementUpdated = new InstanceElementDto(instanceElement.instanceId(),
 
 170                 instanceElement.elementId(), properties, properties);
 
 171             doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
 
 172                 .update(compositionElement, instanceElement, instanceElementUpdated);
 
 173             threadHandler.update(messageId, compositionElement, instanceElement, instanceElementUpdated);
 
 174             verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
 
 175                 DeployState.DEPLOYED, null, StateChangeResult.FAILED,
 
 176                 "Automation composition element update failed");
 
 178             clearInvocations(listener);
 
 179             var compositionTargetId = UUID.randomUUID();
 
 180             var compositionElementTarget = new CompositionElementDto(compositionTargetId, new ToscaConceptIdentifier(),
 
 181                 properties, properties);
 
 182             doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
 
 183                 .migrate(compositionElement, compositionElementTarget, instanceElement, instanceElementUpdated, 0);
 
 184             threadHandler.migrate(messageId, compositionElement, compositionElementTarget,
 
 185                 instanceElement, instanceElementUpdated, 0);
 
 186             verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
 
 187                 DeployState.DEPLOYED, null, StateChangeResult.FAILED,
 
 188                 "Automation composition element migrate failed");
 
 190             clearInvocations(listener);
 
 191             doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
 
 192                 .undeploy(compositionElement, instanceElement);
 
 193             threadHandler.undeploy(messageId, compositionElement, instanceElement);
 
 194             verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
 
 195                 DeployState.DEPLOYED, null, StateChangeResult.FAILED,
 
 196                 "Automation composition element undeploy failed");
 
 198             clearInvocations(listener);
 
 199             doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
 
 200                 .delete(compositionElement, instanceElement);
 
 201             threadHandler.delete(messageId, compositionElement, instanceElement);
 
 202             verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
 
 203                     DeployState.UNDEPLOYED, null, StateChangeResult.FAILED,
 
 204                     "Automation composition element delete failed");
 
 209     void testLock() throws PfModelException, IOException {
 
 210         var listener = mock(AutomationCompositionElementListener.class);
 
 211         var intermediaryApi = mock(ParticipantIntermediaryApi.class);
 
 212         try (var threadHandler = createThreadHandler(listener, intermediaryApi)) {
 
 214             Map<String, Object> properties = Map.of("key", "value");
 
 215             var compositionElement = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(),
 
 216                 properties, properties);
 
 217             var instanceElement = new InstanceElementDto(UUID.randomUUID(), UUID.randomUUID(),
 
 218                 properties, properties);
 
 219             var messageId = UUID.randomUUID();
 
 220             threadHandler.lock(messageId, compositionElement, instanceElement);
 
 221             verify(listener, timeout(TIMEOUT)).lock(compositionElement, instanceElement);
 
 223             clearInvocations(listener);
 
 224             threadHandler.unlock(messageId, compositionElement, instanceElement);
 
 225             verify(listener, timeout(TIMEOUT)).unlock(compositionElement, instanceElement);
 
 227             clearInvocations(listener);
 
 228             threadHandler.undeploy(messageId, compositionElement, instanceElement);
 
 229             verify(listener, timeout(TIMEOUT)).undeploy(compositionElement, instanceElement);
 
 234     void testLockException() throws PfModelException, IOException {
 
 235         var listener = mock(AutomationCompositionElementListener.class);
 
 236         var intermediaryApi = mock(ParticipantIntermediaryApi.class);
 
 237         try (var threadHandler = createThreadHandler(listener, intermediaryApi)) {
 
 239             Map<String, Object> properties = Map.of("key", "value");
 
 240             var compositionElement = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(),
 
 241                 properties, properties);
 
 242             var instanceId = UUID.randomUUID();
 
 243             var elementId = UUID.randomUUID();
 
 244             var instanceElement = new InstanceElementDto(instanceId, elementId, properties, properties);
 
 245             var element = new AcElementDeploy();
 
 246             element.setId(elementId);
 
 247             var messageId = UUID.randomUUID();
 
 248             doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
 
 249                 .lock(compositionElement, instanceElement);
 
 250             threadHandler.lock(messageId, compositionElement, instanceElement);
 
 251             verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
 
 252                 null, LockState.UNLOCKED, StateChangeResult.FAILED, "Automation composition element lock failed");
 
 254             clearInvocations(listener);
 
 255             doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
 
 256                 .unlock(compositionElement, instanceElement);
 
 257             threadHandler.unlock(messageId, compositionElement, instanceElement);
 
 258             verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
 
 259                 null, LockState.LOCKED, StateChangeResult.FAILED, "Automation composition element unlock failed");
 
 264     void testSubState() throws PfModelException, IOException {
 
 265         var listener = mock(AutomationCompositionElementListener.class);
 
 266         var intermediaryApi = mock(ParticipantIntermediaryApi.class);
 
 267         try (var threadHandler = createThreadHandler(listener, intermediaryApi)) {
 
 269             Map<String, Object> properties = Map.of("key", "value");
 
 270             var compositionElement = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(),
 
 271                 properties, properties);
 
 272             var instanceElement = new InstanceElementDto(UUID.randomUUID(), UUID.randomUUID(),
 
 273                 properties, properties);
 
 274             var messageId = UUID.randomUUID();
 
 275             threadHandler.prepare(messageId, compositionElement, instanceElement, 0);
 
 276             verify(listener, timeout(TIMEOUT)).prepare(compositionElement, instanceElement, 0);
 
 278             clearInvocations(listener);
 
 279             threadHandler.review(messageId, compositionElement, instanceElement);
 
 280             verify(listener, timeout(TIMEOUT)).review(compositionElement, instanceElement);
 
 282             clearInvocations(listener);
 
 283             var instanceElementMigrate = new InstanceElementDto(instanceElement.instanceId(),
 
 284                 instanceElement.elementId(), properties, properties);
 
 285             var compositionTargetId = UUID.randomUUID();
 
 286             var compositionElementTarget = new CompositionElementDto(compositionTargetId, new ToscaConceptIdentifier(),
 
 287                 properties, properties);
 
 288             threadHandler.migratePrecheck(messageId, compositionElement, compositionElementTarget,
 
 289                 instanceElement, instanceElementMigrate);
 
 290             verify(listener, timeout(TIMEOUT)).migratePrecheck(compositionElement, compositionElementTarget,
 
 291                 instanceElement, instanceElementMigrate);
 
 296     void testSubStateException() throws PfModelException, IOException {
 
 297         var listener = mock(AutomationCompositionElementListener.class);
 
 298         var intermediaryApi = mock(ParticipantIntermediaryApi.class);
 
 299         try (var threadHandler = createThreadHandler(listener, intermediaryApi)) {
 
 301             Map<String, Object> properties = Map.of("key", "value");
 
 302             var compositionElement = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(),
 
 303                 properties, properties);
 
 304             var instanceId = UUID.randomUUID();
 
 305             var elementId = UUID.randomUUID();
 
 306             var instanceElement = new InstanceElementDto(instanceId, elementId, properties, properties);
 
 307             var element = new AcElementDeploy();
 
 308             element.setId(elementId);
 
 309             doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
 
 310                 .prepare(compositionElement, instanceElement, 0);
 
 311             var messageId = UUID.randomUUID();
 
 312             threadHandler.prepare(messageId, compositionElement, instanceElement, 0);
 
 313             verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
 
 314                 DeployState.UNDEPLOYED, null, StateChangeResult.FAILED,
 
 315                 "Automation composition element prepare Pre Deploy failed");
 
 317             clearInvocations(listener);
 
 318             doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
 
 319                 .review(compositionElement, instanceElement);
 
 320             threadHandler.review(messageId, compositionElement, instanceElement);
 
 321             verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
 
 322                 DeployState.DEPLOYED, null, StateChangeResult.FAILED,
 
 323                 "Automation composition element Review failed");
 
 325             clearInvocations(listener);
 
 326             var compositionTargetId = UUID.randomUUID();
 
 327             var compositionElementTarget = new CompositionElementDto(compositionTargetId, new ToscaConceptIdentifier(),
 
 328                 properties, properties);
 
 329             var instanceElementMigrate = new InstanceElementDto(instanceElement.instanceId(),
 
 330                 instanceElement.elementId(), properties, properties);
 
 331             doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
 
 332                 .migratePrecheck(compositionElement, compositionElementTarget, instanceElement, instanceElementMigrate);
 
 333             threadHandler.migratePrecheck(messageId, compositionElement, compositionElementTarget,
 
 334                 instanceElement, instanceElementMigrate);
 
 335             verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
 
 336                 DeployState.DEPLOYED, null, StateChangeResult.FAILED,
 
 337                 "Automation composition element migrate precheck failed");
 
 339             clearInvocations(listener);
 
 340             doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
 
 341                     .rollbackMigration(compositionElement, instanceElement, 0);
 
 342             threadHandler.rollback(compositionElement.compositionId(), compositionElement, instanceElement, 0);
 
 343             verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
 
 344                     DeployState.DEPLOYED, null, StateChangeResult.FAILED,
 
 345                     "Automation composition element migrate precheck failed");