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