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.rapp.Rapp;
37 import com.oransc.rappmanager.models.csar.RappCsarConfigurationHandler;
38 import com.oransc.rappmanager.models.rapp.RappEvent;
39 import com.oransc.rappmanager.models.rappinstance.RappInstance;
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.cache.RappCacheService;
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 testDeployRappInstanceFailure() throws Exception {
174 UUID compositionId = UUID.randomUUID();
175 UUID rappId = UUID.randomUUID();
176 UUID instanceId = UUID.randomUUID();
177 Rapp rapp = Rapp.builder().name(rappId.toString()).packageName(validRappFile).compositionId(compositionId)
178 .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED).build();
179 InstantiationResponse instantiationResponse = new InstantiationResponse();
180 instantiationResponse.setInstanceId(instanceId);
181 mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_INSTANCES, compositionId)))
182 .andExpect(method(HttpMethod.POST)).andRespond(withStatus(HttpStatus.BAD_GATEWAY));
184 boolean rappDeployStateActual = acmDeployer.deployRappInstance(rapp, rappResourceBuilder.getRappInstance());
186 assertFalse(rappDeployStateActual);
190 void testUndeployRappInstance() throws JsonProcessingException {
191 UUID compositionId = UUID.randomUUID();
192 UUID rappId = UUID.randomUUID();
193 UUID instanceId = UUID.randomUUID();
194 Rapp rapp = Rapp.builder().rappId(rappId).name("").packageName(validRappFile).compositionId(compositionId)
195 .state(RappState.PRIMED).build();
197 expectAcmGetInstanceToReturnState(compositionId, instanceId, DeployState.DEPLOYED, LockState.LOCKED,
198 ExpectedCount.once());
200 mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_INSTANCE, compositionId, instanceId)))
201 .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.ACCEPTED));
203 expectAcmGetInstanceToReturnState(compositionId, instanceId, DeployState.UNDEPLOYED, LockState.UNLOCKED,
204 ExpectedCount.once());
206 mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_INSTANCE, compositionId, instanceId)))
207 .andExpect(method(HttpMethod.DELETE)).andRespond(withStatus(HttpStatus.NO_CONTENT));
209 RappInstance rappInstance = rappResourceBuilder.getRappInstance();
210 rappInstance.getAcm().setAcmInstanceId(instanceId);
211 rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId());
212 boolean rappUndeployStateActual = acmDeployer.undeployRappInstance(rapp, rappInstance);
214 assertTrue(rappUndeployStateActual);
218 void testUndeployRappInstanceFailure() throws JsonProcessingException {
219 UUID compositionId = UUID.randomUUID();
220 UUID rappId = UUID.randomUUID();
221 UUID instanceId = UUID.randomUUID();
222 Rapp rapp = Rapp.builder().name(rappId.toString()).packageName(validRappFile).compositionId(compositionId)
223 .state(RappState.PRIMED).build();
225 expectAcmGetInstanceToReturnState(compositionId, instanceId, DeployState.DEPLOYED, LockState.LOCKED,
226 ExpectedCount.once());
228 mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_INSTANCE, compositionId, instanceId)))
229 .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.ACCEPTED));
231 expectAcmGetInstanceToReturnState(compositionId, instanceId, DeployState.UNDEPLOYING, LockState.UNLOCKING,
232 ExpectedCount.manyTimes());
234 RappInstance rappInstance = rappResourceBuilder.getRappInstance();
235 rappInstance.getAcm().setAcmInstanceId(instanceId);
236 rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId());
237 boolean rappUndeployStateActual = acmDeployer.undeployRappInstance(rapp, rappInstance);
239 assertFalse(rappUndeployStateActual);
243 void testSyncRappInstanceStatus() throws JsonProcessingException {
244 UUID compositionId = UUID.randomUUID();
245 UUID instanceId = UUID.randomUUID();
246 expectAcmGetInstanceToReturnState(compositionId, instanceId, DeployState.UNDEPLOYING, LockState.UNLOCKING,
247 ExpectedCount.once());
248 RappInstance rappInstance = rappResourceBuilder.getRappInstance();
249 rappInstance.getAcm().setAcmInstanceId(instanceId);
250 rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId());
251 acmDeployer.syncRappInstanceStatus(compositionId, rappInstance);
253 verify(rappInstanceStateMachine, times(1)).sendRappInstanceEvent(rappInstance, RappEvent.UNDEPLOYING);
257 void testSyncRappStatusFailure() {
258 UUID compositionId = UUID.randomUUID();
259 UUID instanceId = UUID.randomUUID();
261 mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_INSTANCE, compositionId, instanceId)))
262 .andExpect(method(HttpMethod.GET)).andRespond(withStatus(HttpStatus.BAD_GATEWAY));
264 RappInstance rappInstance = rappResourceBuilder.getRappInstance();
265 rappInstance.getAcm().setAcmInstanceId(instanceId);
266 rappInstanceStateMachine.onboardRappInstance(rappInstance.getRappInstanceId());
267 acmDeployer.syncRappInstanceStatus(compositionId, rappInstance);
269 verify(rappInstanceStateMachine, never()).sendRappInstanceEvent(any(), any());
273 void testPrimeRapp() throws JsonProcessingException {
274 UUID compositionId = UUID.randomUUID();
275 RappResources rappResources = rappResourceBuilder.getResources();
276 Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile)
277 .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED)
278 .compositionId(compositionId).rappResources(rappResources).build();
280 CommissioningResponse commissioningResponseExpected = new CommissioningResponse();
281 commissioningResponseExpected.setCompositionId(compositionId);
282 mockServer.expect(ExpectedCount.once(), requestTo(URI_ACM_COMPOSITIONS)).andExpect(method(HttpMethod.POST))
283 .andRespond(withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON)
284 .body(objectMapper.writeValueAsString(commissioningResponseExpected)));
285 mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId)))
286 .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.ACCEPTED));
287 boolean primeRapp = acmDeployer.primeRapp(rapp);
289 assertTrue(primeRapp);
293 void testPrimeRappFailure() throws JsonProcessingException {
294 UUID compositionId = UUID.randomUUID();
295 RappResources rappResources = rappResourceBuilder.getResources();
296 Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile)
297 .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED)
298 .compositionId(compositionId).rappResources(rappResources).build();
300 CommissioningResponse commissioningResponseExpected = new CommissioningResponse();
301 commissioningResponseExpected.setCompositionId(compositionId);
302 mockServer.expect(ExpectedCount.once(), requestTo(URI_ACM_COMPOSITIONS)).andExpect(method(HttpMethod.POST))
303 .andRespond(withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON)
304 .body(objectMapper.writeValueAsString(commissioningResponseExpected)));
305 mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId)))
306 .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.INTERNAL_SERVER_ERROR));
307 boolean primeRapp = acmDeployer.primeRapp(rapp);
309 assertFalse(primeRapp);
310 assertEquals(RappState.COMMISSIONED, rapp.getState());
314 void testDeprimeRapp() throws JsonProcessingException {
315 UUID compositionId = UUID.randomUUID();
316 RappResources rappResources = rappResourceBuilder.getResources();
317 Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile)
318 .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED)
319 .compositionId(compositionId).rappResources(rappResources).build();
321 CommissioningResponse commissioningResponseExpected = new CommissioningResponse();
322 commissioningResponseExpected.setCompositionId(compositionId);
323 mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId)))
324 .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.OK));
325 mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId)))
326 .andExpect(method(HttpMethod.DELETE)).andRespond(
327 withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON)
328 .body(objectMapper.writeValueAsString(commissioningResponseExpected)));
330 boolean deprimeRapp = acmDeployer.deprimeRapp(rapp);
332 assertTrue(deprimeRapp);
336 void testDeprimeFailureRapp() {
337 UUID compositionId = UUID.randomUUID();
338 RappResources rappResources = rappResourceBuilder.getResources();
339 Rapp rapp = Rapp.builder().rappId(UUID.randomUUID()).name("").packageName(validRappFile)
340 .packageLocation(validCsarFileLocation).state(RappState.COMMISSIONED)
341 .compositionId(compositionId).rappResources(rappResources).build();
343 mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId)))
344 .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.OK));
345 mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId)))
346 .andExpect(method(HttpMethod.DELETE)).andRespond(withStatus(HttpStatus.INTERNAL_SERVER_ERROR));
348 boolean deprimeRapp = acmDeployer.deprimeRapp(rapp);
350 assertFalse(deprimeRapp);
354 void testDeleteComposition() throws JsonProcessingException {
355 UUID compositionId = UUID.randomUUID();
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.DELETE)).andRespond(
360 withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON)
361 .body(objectMapper.writeValueAsString(commissioningResponseExpected)));
362 CommissioningResponse commissioningResponse = acmDeployer.deleteComposition(compositionId);
364 assertEquals(commissioningResponse.getCompositionId(), compositionId);
368 void testDeleteCompositionFailure() {
369 UUID compositionId = UUID.randomUUID();
370 CommissioningResponse commissioningResponseExpected = new CommissioningResponse();
371 commissioningResponseExpected.setCompositionId(compositionId);
372 mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId)))
373 .andExpect(method(HttpMethod.DELETE)).andRespond(withStatus(HttpStatus.INTERNAL_SERVER_ERROR));
374 CommissioningResponse commissioningResponse = acmDeployer.deleteComposition(compositionId);
376 assertNull(commissioningResponse);
379 void expectAcmGetInstanceToReturnState(UUID compositionId, UUID instanceId, DeployState deployState,
380 LockState lockState, ExpectedCount expectedCount) throws JsonProcessingException {
381 AutomationComposition automationCompositionDeployed = new AutomationComposition();
382 automationCompositionDeployed.setCompositionId(compositionId);
383 automationCompositionDeployed.setInstanceId(instanceId);
384 automationCompositionDeployed.setDeployState(deployState);
385 automationCompositionDeployed.setLockState(lockState);
387 mockServer.expect(expectedCount, requestTo(String.format(URI_ACM_INSTANCE, compositionId, instanceId)))
388 .andExpect(method(HttpMethod.GET)).andRespond(
389 withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON)
390 .body(objectMapper.writeValueAsString(automationCompositionDeployed)));