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.handler.cache.CacheProvider;
40 import org.onap.policy.clamp.acm.participant.intermediary.main.parameters.CommonTestData;
41 import org.onap.policy.clamp.models.acm.concepts.AcElementDeploy;
42 import org.onap.policy.clamp.models.acm.concepts.AcTypeState;
43 import org.onap.policy.clamp.models.acm.concepts.DeployState;
44 import org.onap.policy.clamp.models.acm.concepts.LockState;
45 import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
46 import org.onap.policy.models.base.PfModelException;
47 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
49 class ThreadHandlerTest {
51 private static final int TIMEOUT = 400;
53 private ThreadHandler createThreadHandler(AutomationCompositionElementListener listener,
54 ParticipantIntermediaryApi intermediaryApi) {
55 return new ThreadHandler(listener, intermediaryApi, mock(CacheProvider.class),
56 CommonTestData.getParticipantParameters());
60 void testPrime() throws PfModelException, IOException {
61 var listener = mock(AutomationCompositionElementListener.class);
62 var intermediaryApi = mock(ParticipantIntermediaryApi.class);
63 try (var threadHandler = createThreadHandler(listener, intermediaryApi)) {
65 var compositionId = UUID.randomUUID();
66 var messageId = UUID.randomUUID();
67 var composition = new CompositionDto(compositionId, Map.of(), Map.of());
68 threadHandler.prime(messageId, composition);
69 verify(listener, timeout(TIMEOUT)).prime(composition);
71 clearInvocations(listener);
72 threadHandler.deprime(messageId, composition);
73 verify(listener, timeout(TIMEOUT)).deprime(composition);
78 void testPrimeException() throws PfModelException, IOException {
79 var listener = mock(AutomationCompositionElementListener.class);
80 var intermediaryApi = mock(ParticipantIntermediaryApi.class);
81 try (var threadHandler = createThreadHandler(listener, intermediaryApi)) {
83 var compositionId = UUID.randomUUID();
84 var composition = new CompositionDto(compositionId, Map.of(), Map.of());
85 doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener).prime(composition);
86 var messageId = UUID.randomUUID();
87 threadHandler.prime(messageId, composition);
88 verify(intermediaryApi, timeout(TIMEOUT)).updateCompositionState(compositionId, AcTypeState.COMMISSIONED,
89 StateChangeResult.FAILED, "Composition Defintion prime failed");
91 clearInvocations(listener);
92 doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener).deprime(composition);
93 threadHandler.deprime(messageId, composition);
94 verify(intermediaryApi, timeout(TIMEOUT)).updateCompositionState(compositionId, AcTypeState.PRIMED,
95 StateChangeResult.FAILED, "Composition Defintion deprime failed");
100 void testDeploy() throws PfModelException, IOException {
101 var listener = mock(AutomationCompositionElementListener.class);
102 var intermediaryApi = mock(ParticipantIntermediaryApi.class);
103 try (var threadHandler = createThreadHandler(listener, intermediaryApi)) {
105 Map<String, Object> properties = Map.of("key", "value");
106 var compositionElement = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(),
107 properties, properties);
108 var instanceElement = new InstanceElementDto(UUID.randomUUID(), UUID.randomUUID(),
109 properties, properties);
110 var messageId = UUID.randomUUID();
111 threadHandler.deploy(messageId, compositionElement, instanceElement);
112 verify(listener, timeout(TIMEOUT)).deploy(compositionElement, instanceElement);
114 clearInvocations(listener);
115 var element = new AcElementDeploy();
116 var elementId = UUID.randomUUID();
117 element.setId(elementId);
118 var instanceElementUpdated = new InstanceElementDto(instanceElement.instanceId(),
119 instanceElement.elementId(), properties, properties);
120 threadHandler.update(messageId, compositionElement, instanceElement, instanceElementUpdated);
121 verify(listener, timeout(TIMEOUT)).update(compositionElement, instanceElement, instanceElementUpdated);
123 clearInvocations(listener);
124 var compositionTargetId = UUID.randomUUID();
125 var compositionElementTarget = new CompositionElementDto(compositionTargetId, new ToscaConceptIdentifier(),
126 properties, properties);
127 threadHandler.migrate(messageId, compositionElement, compositionElementTarget,
128 instanceElement, instanceElementUpdated, 0);
129 verify(listener, timeout(TIMEOUT)).migrate(compositionElement, compositionElementTarget,
130 instanceElement, instanceElementUpdated, 0);
132 clearInvocations(listener);
133 threadHandler.rollback(messageId, compositionElement, compositionElementTarget,
134 instanceElement, instanceElementUpdated, 0);
135 verify(listener, timeout(TIMEOUT)).rollbackMigration(compositionElement, compositionElementTarget,
136 instanceElement, instanceElementUpdated, 0);
138 clearInvocations(listener);
139 threadHandler.undeploy(messageId, compositionElement, instanceElement);
140 verify(listener, timeout(TIMEOUT)).undeploy(compositionElement, instanceElement);
142 clearInvocations(listener);
143 threadHandler.delete(messageId, compositionElement, instanceElement);
144 verify(listener, timeout(TIMEOUT)).delete(compositionElement, instanceElement);
149 void testDeployException() throws PfModelException, IOException {
150 var listener = mock(AutomationCompositionElementListener.class);
151 var intermediaryApi = mock(ParticipantIntermediaryApi.class);
152 try (var threadHandler = createThreadHandler(listener, intermediaryApi)) {
154 Map<String, Object> properties = Map.of("key", "value");
155 var compositionElement = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(),
156 properties, properties);
157 var instanceId = UUID.randomUUID();
158 var elementId = UUID.randomUUID();
159 var instanceElement = new InstanceElementDto(instanceId, elementId, properties, properties);
160 var element = new AcElementDeploy();
161 element.setId(elementId);
162 doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
163 .deploy(compositionElement, instanceElement);
164 var messageId = UUID.randomUUID();
165 threadHandler.deploy(messageId, compositionElement, instanceElement);
166 verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
167 DeployState.UNDEPLOYED, null, StateChangeResult.FAILED,
168 "Automation composition element deploy failed");
170 clearInvocations(listener);
171 var instanceElementUpdated = new InstanceElementDto(instanceElement.instanceId(),
172 instanceElement.elementId(), properties, properties);
173 doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
174 .update(compositionElement, instanceElement, instanceElementUpdated);
175 threadHandler.update(messageId, compositionElement, instanceElement, instanceElementUpdated);
176 verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
177 DeployState.DEPLOYED, null, StateChangeResult.FAILED,
178 "Automation composition element update failed");
180 clearInvocations(listener);
181 var compositionTargetId = UUID.randomUUID();
182 var compositionElementTarget = new CompositionElementDto(compositionTargetId, new ToscaConceptIdentifier(),
183 properties, properties);
184 doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
185 .migrate(compositionElement, compositionElementTarget, instanceElement, instanceElementUpdated, 0);
186 threadHandler.migrate(messageId, compositionElement, compositionElementTarget,
187 instanceElement, instanceElementUpdated, 0);
188 verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
189 DeployState.DEPLOYED, null, StateChangeResult.FAILED,
190 "Automation composition element migrate failed");
192 clearInvocations(listener);
193 doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
194 .undeploy(compositionElement, instanceElement);
195 threadHandler.undeploy(messageId, compositionElement, instanceElement);
196 verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
197 DeployState.DEPLOYED, null, StateChangeResult.FAILED,
198 "Automation composition element undeploy failed");
200 clearInvocations(listener);
201 doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
202 .delete(compositionElement, instanceElement);
203 threadHandler.delete(messageId, compositionElement, instanceElement);
204 verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
205 DeployState.UNDEPLOYED, null, StateChangeResult.FAILED,
206 "Automation composition element delete failed");
211 void testLock() throws PfModelException, IOException {
212 var listener = mock(AutomationCompositionElementListener.class);
213 var intermediaryApi = mock(ParticipantIntermediaryApi.class);
214 try (var threadHandler = createThreadHandler(listener, intermediaryApi)) {
216 Map<String, Object> properties = Map.of("key", "value");
217 var compositionElement = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(),
218 properties, properties);
219 var instanceElement = new InstanceElementDto(UUID.randomUUID(), UUID.randomUUID(),
220 properties, properties);
221 var messageId = UUID.randomUUID();
222 threadHandler.lock(messageId, compositionElement, instanceElement);
223 verify(listener, timeout(TIMEOUT)).lock(compositionElement, instanceElement);
225 clearInvocations(listener);
226 threadHandler.unlock(messageId, compositionElement, instanceElement);
227 verify(listener, timeout(TIMEOUT)).unlock(compositionElement, instanceElement);
229 clearInvocations(listener);
230 threadHandler.undeploy(messageId, compositionElement, instanceElement);
231 verify(listener, timeout(TIMEOUT)).undeploy(compositionElement, instanceElement);
236 void testLockException() throws PfModelException, IOException {
237 var listener = mock(AutomationCompositionElementListener.class);
238 var intermediaryApi = mock(ParticipantIntermediaryApi.class);
239 try (var threadHandler = createThreadHandler(listener, intermediaryApi)) {
241 Map<String, Object> properties = Map.of("key", "value");
242 var compositionElement = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(),
243 properties, properties);
244 var instanceId = UUID.randomUUID();
245 var elementId = UUID.randomUUID();
246 var instanceElement = new InstanceElementDto(instanceId, elementId, properties, properties);
247 var element = new AcElementDeploy();
248 element.setId(elementId);
249 var messageId = UUID.randomUUID();
250 doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
251 .lock(compositionElement, instanceElement);
252 threadHandler.lock(messageId, compositionElement, instanceElement);
253 verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
254 null, LockState.UNLOCKED, StateChangeResult.FAILED, "Automation composition element lock failed");
256 clearInvocations(listener);
257 doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
258 .unlock(compositionElement, instanceElement);
259 threadHandler.unlock(messageId, compositionElement, instanceElement);
260 verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
261 null, LockState.LOCKED, StateChangeResult.FAILED, "Automation composition element unlock failed");
266 void testSubState() throws PfModelException, IOException {
267 var listener = mock(AutomationCompositionElementListener.class);
268 var intermediaryApi = mock(ParticipantIntermediaryApi.class);
269 try (var threadHandler = createThreadHandler(listener, intermediaryApi)) {
271 Map<String, Object> properties = Map.of("key", "value");
272 var compositionElement = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(),
273 properties, properties);
274 var instanceElement = new InstanceElementDto(UUID.randomUUID(), UUID.randomUUID(),
275 properties, properties);
276 var messageId = UUID.randomUUID();
277 threadHandler.prepare(messageId, compositionElement, instanceElement, 0);
278 verify(listener, timeout(TIMEOUT)).prepare(compositionElement, instanceElement, 0);
280 clearInvocations(listener);
281 threadHandler.review(messageId, compositionElement, instanceElement);
282 verify(listener, timeout(TIMEOUT)).review(compositionElement, instanceElement);
284 clearInvocations(listener);
285 var instanceElementMigrate = new InstanceElementDto(instanceElement.instanceId(),
286 instanceElement.elementId(), properties, properties);
287 var compositionTargetId = UUID.randomUUID();
288 var compositionElementTarget = new CompositionElementDto(compositionTargetId, new ToscaConceptIdentifier(),
289 properties, properties);
290 threadHandler.migratePrecheck(messageId, compositionElement, compositionElementTarget,
291 instanceElement, instanceElementMigrate);
292 verify(listener, timeout(TIMEOUT)).migratePrecheck(compositionElement, compositionElementTarget,
293 instanceElement, instanceElementMigrate);
298 void testSubStateException() throws PfModelException, IOException {
299 var listener = mock(AutomationCompositionElementListener.class);
300 var intermediaryApi = mock(ParticipantIntermediaryApi.class);
301 try (var threadHandler = createThreadHandler(listener, intermediaryApi)) {
303 Map<String, Object> properties = Map.of("key", "value");
304 var compositionElement = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(),
305 properties, properties);
306 var instanceId = UUID.randomUUID();
307 var elementId = UUID.randomUUID();
308 var instanceElement = new InstanceElementDto(instanceId, elementId, properties, properties);
309 var element = new AcElementDeploy();
310 element.setId(elementId);
311 doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
312 .prepare(compositionElement, instanceElement, 0);
313 var messageId = UUID.randomUUID();
314 threadHandler.prepare(messageId, compositionElement, instanceElement, 0);
315 verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
316 DeployState.UNDEPLOYED, null, StateChangeResult.FAILED,
317 "Automation composition element prepare Pre Deploy failed");
319 clearInvocations(listener);
320 doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
321 .review(compositionElement, instanceElement);
322 threadHandler.review(messageId, compositionElement, instanceElement);
323 verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
324 DeployState.DEPLOYED, null, StateChangeResult.FAILED,
325 "Automation composition element Review failed");
327 clearInvocations(listener);
328 var compositionTargetId = UUID.randomUUID();
329 var compositionElementTarget = new CompositionElementDto(compositionTargetId, new ToscaConceptIdentifier(),
330 properties, properties);
331 var instanceElementMigrate = new InstanceElementDto(instanceElement.instanceId(),
332 instanceElement.elementId(), properties, properties);
333 doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
334 .migratePrecheck(compositionElement, compositionElementTarget, instanceElement, instanceElementMigrate);
335 threadHandler.migratePrecheck(messageId, compositionElement, compositionElementTarget,
336 instanceElement, instanceElementMigrate);
337 verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
338 DeployState.DEPLOYED, null, StateChangeResult.FAILED,
339 "Automation composition element migrate precheck failed");