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");