2 * ============LICENSE_START======================================================================
3 * Copyright (C) 2023 Nordix Foundation. 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.
16 * ============LICENSE_END========================================================================
19 package com.oransc.rappmanager.acm.service;
21 import static org.junit.jupiter.api.Assertions.assertEquals;
22 import static org.junit.jupiter.api.Assertions.assertFalse;
23 import static org.junit.jupiter.api.Assertions.assertNull;
24 import static org.junit.jupiter.api.Assertions.assertTrue;
25 import static org.mockito.ArgumentMatchers.any;
26 import static org.mockito.Mockito.never;
27 import static org.mockito.Mockito.times;
28 import static org.mockito.Mockito.verify;
29 import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
30 import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
31 import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus;
33 import com.fasterxml.jackson.core.JsonProcessingException;
34 import com.fasterxml.jackson.databind.ObjectMapper;
35 import com.oransc.rappmanager.acm.configuration.ACMConfiguration;
36 import com.oransc.rappmanager.models.cache.RappCacheService;
37 import com.oransc.rappmanager.models.csar.RappCsarConfigurationHandler;
38 import com.oransc.rappmanager.models.rapp.Rapp;
39 import com.oransc.rappmanager.models.rapp.RappEvent;
40 import com.oransc.rappmanager.models.rapp.RappResourceBuilder;
41 import com.oransc.rappmanager.models.rapp.RappResources;
42 import com.oransc.rappmanager.models.rapp.RappState;
43 import com.oransc.rappmanager.models.rappinstance.RappInstance;
44 import com.oransc.rappmanager.models.statemachine.RappInstanceStateMachine;
45 import com.oransc.rappmanager.models.statemachine.RappInstanceStateMachineConfig;
46 import java.io.IOException;
47 import java.util.UUID;
48 import org.junit.jupiter.api.BeforeAll;
49 import org.junit.jupiter.api.BeforeEach;
50 import org.junit.jupiter.api.Test;
51 import org.junit.jupiter.api.TestInstance;
52 import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
53 import org.onap.policy.clamp.models.acm.concepts.DeployState;
54 import org.onap.policy.clamp.models.acm.concepts.LockState;
55 import org.onap.policy.clamp.models.acm.messages.rest.commissioning.CommissioningResponse;
56 import org.onap.policy.clamp.models.acm.messages.rest.commissioning.PrimeOrder;
57 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.InstantiationResponse;
58 import org.springframework.beans.factory.annotation.Autowired;
59 import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
60 import org.springframework.boot.test.context.SpringBootTest;
61 import org.springframework.boot.test.mock.mockito.SpyBean;
62 import org.springframework.http.HttpMethod;
63 import org.springframework.http.HttpStatus;
64 import org.springframework.http.MediaType;
65 import org.springframework.test.web.client.ExpectedCount;
66 import org.springframework.test.web.client.MockRestServiceServer;
67 import org.springframework.web.client.RestTemplate;
69 @SpringBootTest(classes = {BeanTestConfiguration.class, ACMConfiguration.class, AcmDeployer.class,
70 RappCsarConfigurationHandler.class, RappCacheService.class, RappInstanceStateMachineConfig.class,
71 RappInstanceStateMachine.class})
72 @TestInstance(TestInstance.Lifecycle.PER_CLASS)
74 class AcmDeployerTest {
76 MockRestServiceServer mockServer;
78 AcmDeployer acmDeployer;
80 RestTemplate restTemplate;
82 ACMConfiguration acmConfiguration;
84 RappInstanceStateMachine rappInstanceStateMachine;
86 RappCsarConfigurationHandler rappCsarConfigurationHandler;
88 RappResourceBuilder rappResourceBuilder = new RappResourceBuilder();
89 private final String validRappFile = "valid-rapp-package.csar";
90 String validCsarFileLocation = "src/test/resources/";
91 ObjectMapper objectMapper = new ObjectMapper();
92 String URI_ACM_COMPOSITIONS, URI_ACM_COMPOSITION, URI_ACM_INSTANCES, URI_ACM_INSTANCE;
96 URI_ACM_COMPOSITIONS = acmConfiguration.getBaseUrl() + "compositions";
97 URI_ACM_COMPOSITION = acmConfiguration.getBaseUrl() + "compositions/%s";
98 URI_ACM_INSTANCES = acmConfiguration.getBaseUrl() + "compositions/%s/instances";
99 URI_ACM_INSTANCE = acmConfiguration.getBaseUrl() + "compositions/%s/instances/%s";
104 mockServer = MockRestServiceServer.createServer(restTemplate);
108 void testCreateComposition() throws IOException {
109 RappResources rappResources = rappResourceBuilder.getResources();
110 Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile)
111 .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED)
112 .rappResources(rappResources).build();
114 String compositionPayload = rappCsarConfigurationHandler.getAcmCompositionPayload(rapp);
116 CommissioningResponse commissioningResponseExpected = new CommissioningResponse();
117 commissioningResponseExpected.setCompositionId(UUID.randomUUID());
118 mockServer.expect(ExpectedCount.once(), requestTo(URI_ACM_COMPOSITIONS)).andExpect(method(HttpMethod.POST))
119 .andRespond(withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON)
120 .body(objectMapper.writeValueAsString(commissioningResponseExpected)));
121 CommissioningResponse commissioningResponseActual = acmDeployer.createComposition(compositionPayload);
123 assertEquals(commissioningResponseActual.getCompositionId(), commissioningResponseExpected.getCompositionId());
127 void testCreateCompositionFailure() {
128 RappResources rappResources = rappResourceBuilder.getResources();
129 Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile)
130 .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED)
131 .rappResources(rappResources).build();
132 String compositionPayload = rappCsarConfigurationHandler.getAcmCompositionPayload(rapp);
133 mockServer.expect(ExpectedCount.once(), requestTo(URI_ACM_COMPOSITIONS)).andExpect(method(HttpMethod.POST))
134 .andRespond(withStatus(HttpStatus.BAD_GATEWAY));
135 CommissioningResponse commissioningResponseActual = acmDeployer.createComposition(compositionPayload);
137 assertNull(commissioningResponseActual);
141 void testCompositionPriming() {
142 UUID compositionId = UUID.randomUUID();
143 mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId)))
144 .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.ACCEPTED));
146 acmDeployer.primeACMComposition(compositionId, PrimeOrder.PRIME);
151 void testDeployRappInstance() throws Exception {
153 UUID compositionId = UUID.randomUUID();
154 UUID rappId = UUID.randomUUID();
155 UUID instanceId = UUID.randomUUID();
156 Rapp rapp = Rapp.builder().name(rappId.toString()).packageName(validRappFile).compositionId(compositionId)
157 .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED).build();
158 InstantiationResponse instantiationResponse = new InstantiationResponse();
159 instantiationResponse.setInstanceId(instanceId);
160 mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_INSTANCES, compositionId)))
161 .andExpect(method(HttpMethod.POST)).andRespond(
162 withStatus(HttpStatus.ACCEPTED).contentType(MediaType.APPLICATION_JSON)
163 .body(objectMapper.writeValueAsString(instantiationResponse)));
164 mockServer.expect(ExpectedCount.once(),
165 requestTo(acmConfiguration.getBaseUrl() + "compositions/" + compositionId + "/instances/" + instanceId))
166 .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.ACCEPTED));
167 boolean rappDeployStateActual = acmDeployer.deployRappInstance(rapp, rappResourceBuilder.getRappInstance());
168 assertTrue(rappDeployStateActual);
173 void testDeployRappInstanceFailureWithNoInstanceId() throws JsonProcessingException {
174 UUID compositionId = UUID.randomUUID();
175 UUID rappId = UUID.randomUUID();
176 Rapp rapp = Rapp.builder().name(rappId.toString()).packageName(validRappFile).compositionId(compositionId)
177 .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED).build();
178 RappInstance rappInstance = rappResourceBuilder.getRappInstance();
179 rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId());
180 InstantiationResponse instantiationResponse = new InstantiationResponse();
181 mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_INSTANCES, compositionId)))
182 .andExpect(method(HttpMethod.POST)).andRespond(
183 withStatus(HttpStatus.ACCEPTED).contentType(MediaType.APPLICATION_JSON)
184 .body(objectMapper.writeValueAsString(instantiationResponse)));
186 boolean rappDeployStateActual = acmDeployer.deployRappInstance(rapp, rappInstance);
188 assertFalse(rappDeployStateActual);
192 void testDeployRappInstanceFailure() {
193 UUID compositionId = UUID.randomUUID();
194 UUID rappId = UUID.randomUUID();
195 Rapp rapp = Rapp.builder().name(rappId.toString()).packageName(validRappFile).compositionId(compositionId)
196 .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED).build();
197 RappInstance rappInstance = rappResourceBuilder.getRappInstance();
198 rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId());
199 mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_INSTANCES, compositionId)))
200 .andExpect(method(HttpMethod.POST)).andRespond(withStatus(HttpStatus.BAD_GATEWAY));
202 boolean rappDeployStateActual = acmDeployer.deployRappInstance(rapp, rappInstance);
204 assertFalse(rappDeployStateActual);
208 void testUndeployRappInstance() throws JsonProcessingException {
209 UUID compositionId = UUID.randomUUID();
210 UUID rappId = UUID.randomUUID();
211 UUID instanceId = UUID.randomUUID();
212 Rapp rapp = Rapp.builder().rappId(rappId).name("").packageName(validRappFile).compositionId(compositionId)
213 .state(RappState.PRIMED).build();
215 expectAcmGetInstanceToReturnState(compositionId, instanceId, DeployState.DEPLOYED, LockState.LOCKED,
216 ExpectedCount.once());
218 mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_INSTANCE, compositionId, instanceId)))
219 .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.ACCEPTED));
221 expectAcmGetInstanceToReturnState(compositionId, instanceId, DeployState.UNDEPLOYED, LockState.UNLOCKED,
222 ExpectedCount.once());
224 mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_INSTANCE, compositionId, instanceId)))
225 .andExpect(method(HttpMethod.DELETE)).andRespond(withStatus(HttpStatus.NO_CONTENT));
227 RappInstance rappInstance = rappResourceBuilder.getRappInstance();
228 rappInstance.getAcm().setAcmInstanceId(instanceId);
229 rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId());
230 boolean rappUndeployStateActual = acmDeployer.undeployRappInstance(rapp, rappInstance);
232 assertTrue(rappUndeployStateActual);
236 void testUndeployRappInstanceFailure() throws JsonProcessingException {
237 UUID compositionId = UUID.randomUUID();
238 UUID rappId = UUID.randomUUID();
239 UUID instanceId = UUID.randomUUID();
240 Rapp rapp = Rapp.builder().name(rappId.toString()).packageName(validRappFile).compositionId(compositionId)
241 .state(RappState.PRIMED).build();
243 expectAcmGetInstanceToReturnState(compositionId, instanceId, DeployState.DEPLOYED, LockState.LOCKED,
244 ExpectedCount.once());
246 mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_INSTANCE, compositionId, instanceId)))
247 .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.ACCEPTED));
249 expectAcmGetInstanceToReturnState(compositionId, instanceId, DeployState.UNDEPLOYING, LockState.UNLOCKING,
250 ExpectedCount.manyTimes());
252 RappInstance rappInstance = rappResourceBuilder.getRappInstance();
253 rappInstance.getAcm().setAcmInstanceId(instanceId);
254 rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId());
255 boolean rappUndeployStateActual = acmDeployer.undeployRappInstance(rapp, rappInstance);
257 assertFalse(rappUndeployStateActual);
261 void testSyncRappInstanceStatus() throws JsonProcessingException {
262 UUID compositionId = UUID.randomUUID();
263 UUID instanceId = UUID.randomUUID();
264 expectAcmGetInstanceToReturnState(compositionId, instanceId, DeployState.UNDEPLOYING, LockState.UNLOCKING,
265 ExpectedCount.once());
266 RappInstance rappInstance = rappResourceBuilder.getRappInstance();
267 rappInstance.getAcm().setAcmInstanceId(instanceId);
268 rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId());
269 acmDeployer.syncRappInstanceStatus(compositionId, rappInstance);
271 verify(rappInstanceStateMachine, times(1)).sendRappInstanceEvent(rappInstance, RappEvent.UNDEPLOYING);
275 void testSyncRappStatusFailure() {
276 UUID compositionId = UUID.randomUUID();
277 UUID instanceId = UUID.randomUUID();
279 mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_INSTANCE, compositionId, instanceId)))
280 .andExpect(method(HttpMethod.GET)).andRespond(withStatus(HttpStatus.BAD_GATEWAY));
282 RappInstance rappInstance = rappResourceBuilder.getRappInstance();
283 rappInstance.getAcm().setAcmInstanceId(instanceId);
284 rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId());
285 acmDeployer.syncRappInstanceStatus(compositionId, rappInstance);
287 verify(rappInstanceStateMachine, never()).sendRappInstanceEvent(any(), any());
291 void testPrimeRapp() throws JsonProcessingException {
292 UUID compositionId = UUID.randomUUID();
293 RappResources rappResources = rappResourceBuilder.getResources();
294 Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile)
295 .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED)
296 .compositionId(compositionId).rappResources(rappResources).build();
298 CommissioningResponse commissioningResponseExpected = new CommissioningResponse();
299 commissioningResponseExpected.setCompositionId(compositionId);
300 mockServer.expect(ExpectedCount.once(), requestTo(URI_ACM_COMPOSITIONS)).andExpect(method(HttpMethod.POST))
301 .andRespond(withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON)
302 .body(objectMapper.writeValueAsString(commissioningResponseExpected)));
303 mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId)))
304 .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.ACCEPTED));
305 boolean primeRapp = acmDeployer.primeRapp(rapp);
307 assertTrue(primeRapp);
311 void testPrimeRappFailure() throws JsonProcessingException {
312 UUID compositionId = UUID.randomUUID();
313 RappResources rappResources = rappResourceBuilder.getResources();
314 Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile)
315 .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED)
316 .compositionId(compositionId).rappResources(rappResources).build();
318 CommissioningResponse commissioningResponseExpected = new CommissioningResponse();
319 commissioningResponseExpected.setCompositionId(compositionId);
320 mockServer.expect(ExpectedCount.once(), requestTo(URI_ACM_COMPOSITIONS)).andExpect(method(HttpMethod.POST))
321 .andRespond(withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON)
322 .body(objectMapper.writeValueAsString(commissioningResponseExpected)));
323 mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId)))
324 .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.INTERNAL_SERVER_ERROR));
325 boolean primeRapp = acmDeployer.primeRapp(rapp);
327 assertFalse(primeRapp);
328 assertEquals(RappState.COMMISSIONED, rapp.getState());
332 void testPrimeRappFailureWithoutCompositionId() throws JsonProcessingException {
333 UUID compositionId = UUID.randomUUID();
334 RappResources rappResources = rappResourceBuilder.getResources();
335 Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile)
336 .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED)
337 .compositionId(compositionId).rappResources(rappResources).build();
339 CommissioningResponse commissioningResponseExpected = new CommissioningResponse();
340 mockServer.expect(ExpectedCount.once(), requestTo(URI_ACM_COMPOSITIONS)).andExpect(method(HttpMethod.POST))
341 .andRespond(withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON)
342 .body(objectMapper.writeValueAsString(commissioningResponseExpected)));
343 boolean primeRapp = acmDeployer.primeRapp(rapp);
345 assertFalse(primeRapp);
349 void testDeprimeRapp() throws JsonProcessingException {
350 UUID compositionId = UUID.randomUUID();
351 RappResources rappResources = rappResourceBuilder.getResources();
352 Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile)
353 .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED)
354 .compositionId(compositionId).rappResources(rappResources).build();
356 CommissioningResponse commissioningResponseExpected = new CommissioningResponse();
357 commissioningResponseExpected.setCompositionId(compositionId);
358 mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId)))
359 .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.OK));
360 mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId)))
361 .andExpect(method(HttpMethod.DELETE)).andRespond(
362 withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON)
363 .body(objectMapper.writeValueAsString(commissioningResponseExpected)));
365 boolean deprimeRapp = acmDeployer.deprimeRapp(rapp);
367 assertTrue(deprimeRapp);
371 void testDeprimeFailureRapp() {
372 UUID compositionId = UUID.randomUUID();
373 RappResources rappResources = rappResourceBuilder.getResources();
374 Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile)
375 .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED)
376 .compositionId(compositionId).rappResources(rappResources).build();
378 mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId)))
379 .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.OK));
380 mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId)))
381 .andExpect(method(HttpMethod.DELETE)).andRespond(withStatus(HttpStatus.INTERNAL_SERVER_ERROR));
383 boolean deprimeRapp = acmDeployer.deprimeRapp(rapp);
385 assertFalse(deprimeRapp);
389 void testDeprimeExceptionRapp() {
390 Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile)
391 .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED).build();
392 boolean deprimeRapp = acmDeployer.deprimeRapp(rapp);
393 assertFalse(deprimeRapp);
397 void testDeleteComposition() throws JsonProcessingException {
398 UUID compositionId = UUID.randomUUID();
399 CommissioningResponse commissioningResponseExpected = new CommissioningResponse();
400 commissioningResponseExpected.setCompositionId(compositionId);
401 mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId)))
402 .andExpect(method(HttpMethod.DELETE)).andRespond(
403 withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON)
404 .body(objectMapper.writeValueAsString(commissioningResponseExpected)));
405 CommissioningResponse commissioningResponse = acmDeployer.deleteComposition(compositionId);
407 assertEquals(commissioningResponse.getCompositionId(), compositionId);
411 void testDeleteCompositionFailure() {
412 UUID compositionId = UUID.randomUUID();
413 CommissioningResponse commissioningResponseExpected = new CommissioningResponse();
414 commissioningResponseExpected.setCompositionId(compositionId);
415 mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId)))
416 .andExpect(method(HttpMethod.DELETE)).andRespond(withStatus(HttpStatus.INTERNAL_SERVER_ERROR));
417 CommissioningResponse commissioningResponse = acmDeployer.deleteComposition(compositionId);
419 assertNull(commissioningResponse);
422 void expectAcmGetInstanceToReturnState(UUID compositionId, UUID instanceId, DeployState deployState,
423 LockState lockState, ExpectedCount expectedCount) throws JsonProcessingException {
424 AutomationComposition automationCompositionDeployed = new AutomationComposition();
425 automationCompositionDeployed.setCompositionId(compositionId);
426 automationCompositionDeployed.setInstanceId(instanceId);
427 automationCompositionDeployed.setDeployState(deployState);
428 automationCompositionDeployed.setLockState(lockState);
430 mockServer.expect(expectedCount, requestTo(String.format(URI_ACM_INSTANCE, compositionId, instanceId)))
431 .andExpect(method(HttpMethod.GET)).andRespond(
432 withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON)
433 .body(objectMapper.writeValueAsString(automationCompositionDeployed)));