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.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.mockito.Mockito.when;
30 import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
31 import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
32 import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus;
33 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
35 import com.fasterxml.jackson.core.JsonProcessingException;
36 import com.fasterxml.jackson.databind.ObjectMapper;
37 import com.oransc.rappmanager.acm.configuration.ACMConfiguration;
38 import com.oransc.rappmanager.acm.service.AcmDeployer;
39 import com.oransc.rappmanager.configuration.RappManagerConfiguration;
40 import com.oransc.rappmanager.models.Rapp;
41 import com.oransc.rappmanager.models.RappEvent;
42 import com.oransc.rappmanager.models.RappState;
43 import com.oransc.rappmanager.models.cache.RappCacheService;
44 import com.oransc.rappmanager.models.statemachine.RappStateMachine;
46 import java.io.FileInputStream;
47 import java.io.IOException;
48 import java.nio.file.Files;
49 import java.nio.file.Path;
50 import java.util.UUID;
51 import org.apache.http.entity.ContentType;
52 import org.junit.jupiter.api.BeforeAll;
53 import org.junit.jupiter.api.BeforeEach;
54 import org.junit.jupiter.api.Test;
55 import org.junit.jupiter.api.TestInstance;
56 import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
57 import org.onap.policy.clamp.models.acm.concepts.DeployState;
58 import org.onap.policy.clamp.models.acm.concepts.LockState;
59 import org.onap.policy.clamp.models.acm.messages.rest.commissioning.CommissioningResponse;
60 import org.onap.policy.clamp.models.acm.messages.rest.commissioning.PrimeOrder;
61 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.InstantiationResponse;
62 import org.springframework.beans.factory.annotation.Autowired;
63 import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
64 import org.springframework.boot.test.context.SpringBootTest;
65 import org.springframework.boot.test.mock.mockito.SpyBean;
66 import org.springframework.http.HttpMethod;
67 import org.springframework.http.HttpStatus;
68 import org.springframework.http.MediaType;
69 import org.springframework.mock.web.MockMultipartFile;
70 import org.springframework.test.context.TestPropertySource;
71 import org.springframework.test.web.client.ExpectedCount;
72 import org.springframework.test.web.client.MockRestServiceServer;
73 import org.springframework.test.web.servlet.MockMvc;
74 import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
75 import org.springframework.web.client.RestTemplate;
77 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
78 @TestPropertySource(properties = "rappmanager.csarlocation=src/test/resources")
79 @TestInstance(TestInstance.Lifecycle.PER_CLASS)
81 public class AcmDeployerTest {
83 MockRestServiceServer mockServer;
85 AcmDeployer acmDeployer;
87 RestTemplate restTemplate;
89 ACMConfiguration acmConfiguration;
91 RappManagerConfiguration rappManagerConfiguration;
93 private MockMvc mockMvc;
95 RappCacheService rappCacheService;
97 RappStateMachine rappStateMachine;
98 private final String validRappFile = "valid-rapp-package.csar";
99 ObjectMapper objectMapper = new ObjectMapper();
100 String URI_ACM_COMPOSITIONS, URI_ACM_COMPOSITION, URI_ACM_INSTANCES, URI_ACM_INSTANCE;
104 URI_ACM_COMPOSITIONS = acmConfiguration.getBaseUrl() + "compositions";
105 URI_ACM_COMPOSITION = acmConfiguration.getBaseUrl() + "compositions/%s";
106 URI_ACM_INSTANCES = acmConfiguration.getBaseUrl() + "compositions/%s/instances";
107 URI_ACM_INSTANCE = acmConfiguration.getBaseUrl() + "compositions/%s/instances/%s";
113 mockServer = MockRestServiceServer.createServer(restTemplate);
117 void testCreateComposition() throws IOException {
118 String compositionPayload = Files.readString(Path.of(acmConfiguration.getCompositionDefinitionLocation()));
119 CommissioningResponse commissioningResponseExpected = new CommissioningResponse();
120 commissioningResponseExpected.setCompositionId(UUID.randomUUID());
121 mockServer.expect(ExpectedCount.once(), requestTo(URI_ACM_COMPOSITIONS)).andExpect(method(HttpMethod.POST))
122 .andRespond(withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON)
123 .body(objectMapper.writeValueAsString(commissioningResponseExpected)));
124 CommissioningResponse commissioningResponseActual = acmDeployer.createComposition(compositionPayload);
126 assertEquals(commissioningResponseActual.getCompositionId(), commissioningResponseExpected.getCompositionId());
130 void testCreateCompositionFailure() throws IOException {
131 String compositionPayload = Files.readString(Path.of(acmConfiguration.getCompositionDefinitionLocation()));
132 mockServer.expect(ExpectedCount.once(), requestTo(URI_ACM_COMPOSITIONS)).andExpect(method(HttpMethod.POST))
133 .andRespond(withStatus(HttpStatus.BAD_GATEWAY));
134 CommissioningResponse commissioningResponseActual = acmDeployer.createComposition(compositionPayload);
136 assertNull(commissioningResponseActual);
140 void testCompositionPriming() {
141 UUID compositionId = UUID.randomUUID();
142 mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_COMPOSITION, compositionId)))
143 .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.ACCEPTED));
145 acmDeployer.primeACMComposition(compositionId, PrimeOrder.PRIME);
150 void testDeployRapp() throws Exception {
152 UUID compositionId = UUID.randomUUID();
153 when(acmDeployer.getCompositionId()).thenReturn(compositionId);
154 UUID rappId = UUID.randomUUID();
155 UUID instanceId = UUID.randomUUID();
156 Rapp rapp = Rapp.builder().name(rappId.toString()).packageName(validRappFile).compositionId(compositionId)
157 .packageLocation(rappManagerConfiguration.getCsarLocation()).state(RappState.ONBOARDED)
159 onBoardRappCsar(rappId);
160 InstantiationResponse instantiationResponse = new InstantiationResponse();
161 instantiationResponse.setInstanceId(instanceId);
162 mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_INSTANCES, compositionId)))
163 .andExpect(method(HttpMethod.POST)).andRespond(
164 withStatus(HttpStatus.ACCEPTED).contentType(MediaType.APPLICATION_JSON)
165 .body(objectMapper.writeValueAsString(instantiationResponse)));
167 mockServer.expect(ExpectedCount.once(),
168 requestTo(acmConfiguration.getBaseUrl() + "compositions/" + compositionId + "/instances/" + instanceId))
169 .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.ACCEPTED));
172 boolean rappDeployStateActual = acmDeployer.deployRapp(rapp);
173 assertTrue(rappDeployStateActual);
178 void testDeployRappFailure() throws Exception {
179 UUID compositionId = UUID.randomUUID();
180 when(acmDeployer.getCompositionId()).thenReturn(compositionId);
181 UUID rappId = UUID.randomUUID();
182 UUID instanceId = UUID.randomUUID();
183 Rapp rapp = Rapp.builder().name(rappId.toString()).packageName(validRappFile).compositionId(compositionId)
184 .packageLocation(rappManagerConfiguration.getCsarLocation()).state(RappState.ONBOARDED)
186 onBoardRappCsar(rappId);
187 InstantiationResponse instantiationResponse = new InstantiationResponse();
188 instantiationResponse.setInstanceId(instanceId);
189 mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_INSTANCES, compositionId)))
190 .andExpect(method(HttpMethod.POST)).andRespond(withStatus(HttpStatus.BAD_GATEWAY));
192 boolean rappDeployStateActual = acmDeployer.deployRapp(rapp);
194 assertFalse(rappDeployStateActual);
198 void testUndeployRapp() throws JsonProcessingException {
199 UUID compositionId = UUID.randomUUID();
200 when(acmDeployer.getCompositionId()).thenReturn(compositionId);
201 UUID rappId = UUID.randomUUID();
202 UUID instanceId = UUID.randomUUID();
203 Rapp rapp = Rapp.builder().rappId(rappId).name(rappId.toString()).packageName(validRappFile)
204 .compositionId(compositionId).compositionInstanceId(instanceId).state(RappState.DEPLOYED)
206 rappCacheService.putRapp(rapp);
207 rappStateMachine.onboardRapp(rappId);
209 expectAcmGetInstanceToReturnDeployedState(compositionId, instanceId, DeployState.DEPLOYED, LockState.LOCKED,
210 ExpectedCount.once());
212 mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_INSTANCE, compositionId, instanceId)))
213 .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.ACCEPTED));
215 expectAcmGetInstanceToReturnDeployedState(compositionId, instanceId, DeployState.UNDEPLOYED, LockState.UNLOCKED,
216 ExpectedCount.once());
218 mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_INSTANCE, compositionId, instanceId)))
219 .andExpect(method(HttpMethod.DELETE)).andRespond(withStatus(HttpStatus.NO_CONTENT));
222 boolean rappUndeployStateActual = acmDeployer.undeployRapp(rapp);
224 assertTrue(rappUndeployStateActual);
228 void testUndeployRappFailure() throws JsonProcessingException {
229 UUID compositionId = UUID.randomUUID();
230 when(acmDeployer.getCompositionId()).thenReturn(compositionId);
231 UUID rappId = UUID.randomUUID();
232 UUID instanceId = UUID.randomUUID();
233 Rapp rapp = Rapp.builder().name(rappId.toString()).packageName(validRappFile).compositionId(compositionId)
234 .compositionInstanceId(instanceId).state(RappState.DEPLOYED).build();
235 rappCacheService.putRapp(rapp);
237 expectAcmGetInstanceToReturnDeployedState(compositionId, instanceId, DeployState.DEPLOYED, LockState.LOCKED,
238 ExpectedCount.once());
240 mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_INSTANCE, compositionId, instanceId)))
241 .andExpect(method(HttpMethod.PUT)).andRespond(withStatus(HttpStatus.ACCEPTED));
243 expectAcmGetInstanceToReturnDeployedState(compositionId, instanceId, DeployState.UNDEPLOYING,
244 LockState.UNLOCKING, ExpectedCount.manyTimes());
246 boolean rappUndeployStateActual = acmDeployer.undeployRapp(rapp);
248 assertFalse(rappUndeployStateActual);
252 void testSyncRappStatus() throws JsonProcessingException {
253 UUID compositionId = UUID.randomUUID();
254 when(acmDeployer.getCompositionId()).thenReturn(compositionId);
255 UUID rappId = UUID.randomUUID();
256 UUID instanceId = UUID.randomUUID();
257 Rapp rapp = Rapp.builder().rappId(rappId).name(rappId.toString()).packageName(validRappFile)
258 .compositionId(compositionId).compositionInstanceId(instanceId).state(RappState.DEPLOYED)
260 rappCacheService.putRapp(rapp);
261 rappStateMachine.onboardRapp(rappId);
263 expectAcmGetInstanceToReturnDeployedState(compositionId, instanceId, DeployState.UNDEPLOYING,
264 LockState.UNLOCKING, ExpectedCount.once());
266 acmDeployer.syncRappStatus(rapp);
268 verify(rappStateMachine, times(1)).sendRappEvent(rapp, RappEvent.UNDEPLOYING);
272 void testSyncRappStatusFailure() {
273 UUID compositionId = UUID.randomUUID();
274 when(acmDeployer.getCompositionId()).thenReturn(compositionId);
275 UUID rappId = UUID.randomUUID();
276 UUID instanceId = UUID.randomUUID();
277 Rapp rapp = Rapp.builder().name(rappId.toString()).packageName(validRappFile).compositionId(compositionId)
278 .compositionInstanceId(instanceId).state(RappState.DEPLOYED).build();
279 rappCacheService.putRapp(rapp);
281 mockServer.expect(ExpectedCount.once(), requestTo(String.format(URI_ACM_INSTANCE, compositionId, instanceId)))
282 .andExpect(method(HttpMethod.GET)).andRespond(withStatus(HttpStatus.BAD_GATEWAY));
284 acmDeployer.syncRappStatus(rapp);
286 verify(rappStateMachine, never()).sendRappEvent(any(), any());
289 void expectAcmGetInstanceToReturnDeployedState(UUID compositionId, UUID instanceId, DeployState deployState,
290 LockState lockState, ExpectedCount expectedCount) throws JsonProcessingException {
291 AutomationComposition automationCompositionDeployed = new AutomationComposition();
292 automationCompositionDeployed.setCompositionId(compositionId);
293 automationCompositionDeployed.setInstanceId(instanceId);
294 automationCompositionDeployed.setDeployState(deployState);
295 automationCompositionDeployed.setLockState(lockState);
297 mockServer.expect(expectedCount, requestTo(String.format(URI_ACM_INSTANCE, compositionId, instanceId)))
298 .andExpect(method(HttpMethod.GET)).andRespond(
299 withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON)
300 .body(objectMapper.writeValueAsString(automationCompositionDeployed)));
303 void onBoardRappCsar(UUID rappId) throws Exception {
304 String rappCsarPath = rappManagerConfiguration.getCsarLocation() + File.separator + validRappFile;
305 MockMultipartFile multipartFile =
306 new MockMultipartFile("file", validRappFile, ContentType.MULTIPART_FORM_DATA.getMimeType(),
307 new FileInputStream(rappCsarPath));
308 mockMvc.perform(MockMvcRequestBuilders.multipart("/rapps/{rapp_id}/onboard", rappId).file(multipartFile))
309 .andExpect(status().isAccepted());