Add Junits for kubernetes participant module 90/122390/4
authorrameshiyer27 <ramesh.murugan.iyer@est.tech>
Thu, 1 Jul 2021 06:50:45 +0000 (07:50 +0100)
committerrameshiyer27 <ramesh.murugan.iyer@est.tech>
Thu, 1 Jul 2021 16:39:25 +0000 (17:39 +0100)
Overall coverage for the module: 85%

Issue-ID: POLICY-3241
Signed-off-by: zrrmmua <ramesh.murugan.iyer@est.tech>
Change-Id: I00c7866ae598b9768d1616468a4c9bfe4cc76166

22 files changed:
participant/participant-impl/participant-impl-kubernetes/pom.xml
participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/Application.java
participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/configurations/ParticipantConfig.java [moved from participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/configurations/BeanFactory.java with 98% similarity]
participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/controller/ChartController.java
participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/handler/ControlLoopElementHandler.java
participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/helm/HelmClient.java
participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/models/ChartList.java
participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/parameters/ParticipantK8sParameters.java
participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/service/ChartService.java
participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/controlloop/participant/kubernetes/service/ChartStore.java
participant/participant-impl/participant-impl-kubernetes/src/test/java/org.onap.policy.clamp.controlloop.participant.kubernetes/handler/ControlLoopElementHandlerTest.java [new file with mode: 0644]
participant/participant-impl/participant-impl-kubernetes/src/test/java/org.onap.policy.clamp.controlloop.participant.kubernetes/helm/HelmClientTest.java [new file with mode: 0644]
participant/participant-impl/participant-impl-kubernetes/src/test/java/org.onap.policy.clamp.controlloop.participant.kubernetes/parameters/CommonTestData.java [new file with mode: 0644]
participant/participant-impl/participant-impl-kubernetes/src/test/java/org.onap.policy.clamp.controlloop.participant.kubernetes/parameters/ParticipantK8sParametersTest.java [new file with mode: 0644]
participant/participant-impl/participant-impl-kubernetes/src/test/java/org.onap.policy.clamp.controlloop.participant.kubernetes/rest/ChartControllerTest.java [new file with mode: 0644]
participant/participant-impl/participant-impl-kubernetes/src/test/java/org.onap.policy.clamp.controlloop.participant.kubernetes/service/ChartServiceTest.java [new file with mode: 0644]
participant/participant-impl/participant-impl-kubernetes/src/test/java/org.onap.policy.clamp.controlloop.participant.kubernetes/service/ChartStoreTest.java [new file with mode: 0644]
participant/participant-impl/participant-impl-kubernetes/src/test/java/org.onap.policy.clamp.controlloop.participant.kubernetes/utils/TestUtils.java [new file with mode: 0644]
participant/participant-impl/participant-impl-kubernetes/src/test/resources/ChartList.json [new file with mode: 0644]
participant/participant-impl/participant-impl-kubernetes/src/test/resources/application_test.properties [new file with mode: 0644]
participant/participant-impl/participant-impl-kubernetes/src/test/resources/servicetemplates/KubernetesHelm.yaml [moved from participant/participant-impl/participant-impl-kubernetes/src/test/resources/KubernetesHelm.yaml with 100% similarity]
pom.xml

index 15e8e90..ebaa7a1 100644 (file)
     <name>${project.artifactId}</name>
     <description>Kubernetes participant, that allows k8s pods to partake in control loops</description>
 
+    <!-- Dependency added here to take precedence over Mockito-core in parent pom-->
+    <dependencies>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-inline</artifactId>
+            <version>3.8.0</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
     <build>
         <resources>
             <!-- Output the version of the control loop system -->
index 37ecf4e..5d9d203 100644 (file)
@@ -23,12 +23,15 @@ package org.onap.policy.clamp.controlloop.participant.kubernetes;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
+import org.springframework.context.annotation.ComponentScan;
 
 /**
  * Starter.
  *
  */
 @SpringBootApplication
+@ComponentScan({"org.onap.policy.clamp.controlloop.participant.kubernetes",
+    "org.onap.policy.clamp.controlloop.participant.intermediary"})
 @ConfigurationPropertiesScan("org.onap.policy.clamp.controlloop.participant.kubernetes.parameters")
 public class Application {
     /**
index 5560e47..23605e6 100644 (file)
@@ -149,13 +149,12 @@ public class ChartController {
      * @param name name of the chart
      * @param version version of the chart
      * @return Status of operation
-     * @throws ServiceException in case of error.
      */
     @DeleteMapping(path = "/charts/{name}/{version}")
     @ApiOperation(value = "Delete the chart")
     @ApiResponses(value = {@ApiResponse(code = 204, message = "Chart Deleted")})
     public ResponseEntity<Object> deleteChart(@PathVariable("name") String name,
-            @PathVariable("version") String version) throws ServiceException {
+            @PathVariable("version") String version) {
 
         ChartInfo chart = chartService.getChart(name, version);
         if (chart == null) {
index 6257c3d..4f65483 100644 (file)
@@ -26,6 +26,8 @@ import java.lang.invoke.MethodHandles;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.UUID;
+import lombok.AccessLevel;
+import lombok.Getter;
 import lombok.Setter;
 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElement;
 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopOrderedState;
@@ -57,6 +59,7 @@ public class ControlLoopElementHandler implements ControlLoopElementListener {
     private ParticipantIntermediaryApi intermediaryApi;
 
     // Map of CLElement Id and installed Helm charts
+    @Getter(AccessLevel.PACKAGE)
     private final Map<UUID, ChartInfo> chartMap = new HashMap<>();
 
     /**
index 343a446..90d7218 100644 (file)
@@ -75,12 +75,33 @@ public class HelmClient {
      */
     public String findChartRepository(ChartInfo chart) throws ServiceException, IOException {
         updateHelmRepo();
-        logger.info("Looking for helm chart {} in all the configured helm repositories", chart.getChartName());
-        String repository = null;
+        String repository = verifyConfiguredRepo(chart);
+        if (repository != null) {
+            return repository;
+        }
+        var localHelmChartDir = chartStore.getAppPath(chart.getChartName(), chart.getVersion()).toString();
+        logger.info("Chart not found in helm repositories, verifying local repo {} ", localHelmChartDir);
+        if (verifyLocalHelmRepo(localHelmChartDir + "/" + chart.getChartName())) {
+            repository = localHelmChartDir;
+        }
 
-        var process = helmRepoVerifyCommand(chart.getChartName()).start();
+        return repository;
+    }
 
-        try (var reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
+    /**
+     * Verify helm chart in configured repositories.
+     * @param chart chartInfo
+     * @return repo name
+     * @throws IOException incase of error
+     * @throws ServiceException incase of error
+     */
+    public String verifyConfiguredRepo(ChartInfo chart) throws IOException, ServiceException {
+        logger.info("Looking for helm chart {} in all the configured helm repositories", chart.getChartName());
+        String repository = null;
+        var builder = helmRepoVerifyCommand(chart.getChartName());
+        String output = executeCommand(builder);
+        try (var reader = new BufferedReader(new InputStreamReader(IOUtils.toInputStream(output,
+                StandardCharsets.UTF_8)))) {
             String line = reader.readLine();
             while (line != null) {
                 if (line.contains(chart.getChartName())) {
@@ -91,13 +112,6 @@ public class HelmClient {
                 line = reader.readLine();
             }
         }
-
-        var localHelmChartDir = chartStore.getAppPath(chart.getChartName(), chart.getVersion()).toString();
-        logger.info("Chart not found in helm repositories, verifying local repo {} ", localHelmChartDir);
-        if (verifyLocalHelmRepo(localHelmChartDir + "/" + chart.getChartName())) {
-            repository = localHelmChartDir;
-        }
-
         return repository;
     }
 
@@ -111,7 +125,13 @@ public class HelmClient {
         executeCommand(prepareUnInstallCommand(chart));
     }
 
-    static String executeCommand(ProcessBuilder processBuilder) throws ServiceException {
+    /**
+     * Execute helm cli bash commands .
+     * @param processBuilder processbuilder
+     * @return string output
+     * @throws ServiceException incase of error.
+     */
+    public static String executeCommand(ProcessBuilder processBuilder) throws ServiceException {
         var commandStr = toString(processBuilder);
 
         try {
@@ -123,13 +143,15 @@ public class HelmClient {
                 var error = IOUtils.toString(process.getErrorStream(), StandardCharsets.UTF_8);
                 throw new ServiceException("Command execution failed: " + commandStr + " " + error);
             }
+
             var output = IOUtils.toString(process.getInputStream(), StandardCharsets.UTF_8);
             logger.debug("Command <{}> execution, output: {}", commandStr, output);
             return output;
+
         } catch (InterruptedException ie) {
             Thread.currentThread().interrupt();
             throw new ServiceException("Failed to execute the Command: " + commandStr + ", the command was interrupted",
-                ie);
+                    ie);
         } catch (Exception exc) {
             throw new ServiceException("Failed to execute the Command: " + commandStr, exc);
         }
@@ -140,12 +162,12 @@ public class HelmClient {
         // @formatter:off
         List<String> helmArguments = new ArrayList<>(
                 Arrays.asList(
-                    "helm",
-                    "install", chart.getReleaseName(), chart.getRepository() + "/" + chart.getChartName(),
-                    "--version", chart.getVersion(),
-                    "--namespace", chart.getNamespace()
+                        "helm",
+                        "install", chart.getReleaseName(), chart.getRepository() + "/" + chart.getChartName(),
+                        "--version", chart.getVersion(),
+                        "--namespace", chart.getNamespace()
                 )
-            );
+        );
         // @formatter:on
 
         // Verify if values.yaml available for the chart
@@ -176,9 +198,7 @@ public class HelmClient {
 
     private void updateHelmRepo() throws ServiceException {
         logger.info("Updating local helm repositories before verifying the chart");
-        List<String> helmArguments = Arrays.asList("helm", "repo", "update");
-
-        executeCommand(new ProcessBuilder().command(helmArguments));
+        executeCommand(new ProcessBuilder().command("helm", "repo", "update"));
         logger.debug("Helm repositories updated successfully");
     }
 
index c86bff5..7f46bbd 100644 (file)
@@ -18,7 +18,7 @@
 
 package org.onap.policy.clamp.controlloop.participant.kubernetes.models;
 
-import java.util.Collection;
+import java.util.List;
 import lombok.Builder;
 import lombok.Getter;
 import lombok.Setter;
@@ -27,5 +27,5 @@ import lombok.Setter;
 @Setter
 @Builder
 public class ChartList {
-    private Collection<ChartInfo> charts;
+    private List<ChartInfo> charts;
 }
index a5731da..3b2b373 100644 (file)
 
 package org.onap.policy.clamp.controlloop.participant.kubernetes.parameters;
 
+import javax.validation.Valid;
 import javax.validation.constraints.NotBlank;
 import javax.validation.constraints.NotNull;
 import lombok.Getter;
 import lombok.Setter;
 import org.onap.policy.clamp.controlloop.participant.intermediary.parameters.ParticipantIntermediaryParameters;
 import org.onap.policy.clamp.controlloop.participant.intermediary.parameters.ParticipantParameters;
-import org.onap.policy.common.parameters.annotations.Valid;
 import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.validation.annotation.Validated;
 
index 29a49a9..adb6cf0 100644 (file)
@@ -53,9 +53,8 @@ public class ChartService {
      * @param name name of the app
      * @param version version of the app
      * @return chart
-     * @throws ServiceException in case of error.
      */
-    public ChartInfo getChart(String name, String version) throws ServiceException {
+    public ChartInfo getChart(String name, String version) {
         return chartStore.getChart(name, version);
     }
 
index dcdff62..03b3516 100644 (file)
@@ -33,6 +33,8 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
+import lombok.AccessLevel;
+import lombok.Getter;
 import org.onap.policy.clamp.controlloop.participant.kubernetes.exception.ServiceException;
 import org.onap.policy.clamp.controlloop.participant.kubernetes.models.ChartInfo;
 import org.onap.policy.clamp.controlloop.participant.kubernetes.parameters.ParticipantK8sParameters;
@@ -52,9 +54,8 @@ public class ChartStore {
 
     private final ParticipantK8sParameters participantK8sParameters;
 
-    /**
-     * The chartStore map contains chart name as key & ChartInfo as value.
-     */
+    // ChartStore map contains chart name as key & ChartInfo as value.
+    @Getter(AccessLevel.PACKAGE)
     private Map<String, ChartInfo> localChartMap = new ConcurrentHashMap<>();
 
     /**
diff --git a/participant/participant-impl/participant-impl-kubernetes/src/test/java/org.onap.policy.clamp.controlloop.participant.kubernetes/handler/ControlLoopElementHandlerTest.java b/participant/participant-impl/participant-impl-kubernetes/src/test/java/org.onap.policy.clamp.controlloop.participant.kubernetes/handler/ControlLoopElementHandlerTest.java
new file mode 100644 (file)
index 0000000..f3d27a6
--- /dev/null
@@ -0,0 +1,130 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.controlloop.participant.kubernetes.handler;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doThrow;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.UUID;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElement;
+import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopOrderedState;
+import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopState;
+import org.onap.policy.clamp.controlloop.participant.intermediary.api.ParticipantIntermediaryApi;
+import org.onap.policy.clamp.controlloop.participant.kubernetes.exception.ServiceException;
+import org.onap.policy.clamp.controlloop.participant.kubernetes.models.ChartInfo;
+import org.onap.policy.clamp.controlloop.participant.kubernetes.models.ChartList;
+import org.onap.policy.clamp.controlloop.participant.kubernetes.service.ChartService;
+import org.onap.policy.clamp.controlloop.participant.kubernetes.utils.TestUtils;
+import org.onap.policy.common.utils.coder.Coder;
+import org.onap.policy.common.utils.coder.CoderException;
+import org.onap.policy.common.utils.coder.StandardCoder;
+import org.onap.policy.models.base.PfModelException;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+
+@ExtendWith(SpringExtension.class)
+class ControlLoopElementHandlerTest {
+
+    private static final Coder CODER = new StandardCoder();
+    private static final String CHART_INFO_YAML = "src/test/resources/ChartList.json";
+    private static final String KEY_NAME = "org.onap.domain.database.HelloWorld_K8SMicroserviceControlLoopElement";
+    private static List<ChartInfo> charts;
+    private static ToscaServiceTemplate toscaServiceTemplate;
+
+
+    @InjectMocks
+    private ControlLoopElementHandler controlLoopElementHandler = new ControlLoopElementHandler();
+
+    @Mock
+    private ChartService chartService;
+
+    @Mock
+    private ParticipantIntermediaryApi participantIntermediaryApi;
+
+    @BeforeAll
+    static void init() throws CoderException {
+        charts = CODER.decode(new File(CHART_INFO_YAML), ChartList.class).getCharts();
+        toscaServiceTemplate = TestUtils.testControlLoopRead();
+    }
+
+
+    @Test
+    void test_ControlLoopElementStateChange() throws ServiceException {
+        UUID controlLoopElementId1 = UUID.randomUUID();
+        UUID controlLoopElementId2 = UUID.randomUUID();
+
+        controlLoopElementHandler.getChartMap().put(controlLoopElementId1, charts.get(0));
+        controlLoopElementHandler.getChartMap().put(controlLoopElementId2, charts.get(1));
+
+        doNothing().when(chartService).uninstallChart(charts.get(0));
+
+        controlLoopElementHandler.controlLoopElementStateChange(controlLoopElementId1, ControlLoopState.PASSIVE,
+            ControlLoopOrderedState.UNINITIALISED);
+
+        doThrow(new ServiceException("Error uninstalling the chart")).when(chartService)
+            .uninstallChart(charts.get(0));
+
+        assertDoesNotThrow(() -> controlLoopElementHandler
+            .controlLoopElementStateChange(controlLoopElementId1, ControlLoopState.PASSIVE,
+                ControlLoopOrderedState.UNINITIALISED));
+
+        assertDoesNotThrow(() -> controlLoopElementHandler
+            .controlLoopElementStateChange(controlLoopElementId1, ControlLoopState.PASSIVE,
+                ControlLoopOrderedState.RUNNING));
+
+    }
+
+    @Test
+    void test_ControlLoopElementUpdate() throws PfModelException, IOException, ServiceException {
+
+        UUID elementId1 = UUID.randomUUID();
+        ControlLoopElement element = new ControlLoopElement();
+        element.setId(elementId1);
+        element.setDefinition(new ToscaConceptIdentifier(KEY_NAME, "1.0.1"));
+        element.setOrderedState(ControlLoopOrderedState.PASSIVE);
+
+        controlLoopElementHandler.controlLoopElementUpdate(element, toscaServiceTemplate);
+
+        assertThat(controlLoopElementHandler.getChartMap()).hasSize(1).containsKey(elementId1);
+
+        doThrow(new ServiceException("Error installing the chart")).when(chartService)
+            .installChart(Mockito.any());
+
+        UUID elementId2 = UUID.randomUUID();
+        element.setId(elementId2);
+        controlLoopElementHandler.controlLoopElementUpdate(element, toscaServiceTemplate);
+
+        assertThat(controlLoopElementHandler.getChartMap().containsKey(elementId2)).isFalse();
+    }
+}
diff --git a/participant/participant-impl/participant-impl-kubernetes/src/test/java/org.onap.policy.clamp.controlloop.participant.kubernetes/helm/HelmClientTest.java b/participant/participant-impl/participant-impl-kubernetes/src/test/java/org.onap.policy.clamp.controlloop.participant.kubernetes/helm/HelmClientTest.java
new file mode 100644 (file)
index 0000000..5f8b7dc
--- /dev/null
@@ -0,0 +1,122 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.controlloop.participant.kubernetes.helm;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.Assert.assertNull;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mockStatic;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+import org.mockito.Spy;
+import org.onap.policy.clamp.controlloop.participant.kubernetes.exception.ServiceException;
+import org.onap.policy.clamp.controlloop.participant.kubernetes.models.ChartInfo;
+import org.onap.policy.clamp.controlloop.participant.kubernetes.models.ChartList;
+import org.onap.policy.clamp.controlloop.participant.kubernetes.service.ChartStore;
+import org.onap.policy.common.utils.coder.Coder;
+import org.onap.policy.common.utils.coder.CoderException;
+import org.onap.policy.common.utils.coder.StandardCoder;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+
+@ExtendWith(SpringExtension.class)
+class HelmClientTest {
+
+    private static final Coder CODER = new StandardCoder();
+    private static final String CHART_INFO_YAML = "src/test/resources/ChartList.json";
+    private static List<ChartInfo> charts;
+
+    @InjectMocks
+    @Spy
+    private HelmClient helmClient = new HelmClient();
+
+    @Mock
+    ChartStore chartStore;
+
+    private static MockedStatic<HelmClient> mockedClient;
+
+    @BeforeAll
+    static void init() throws CoderException {
+        charts = CODER.decode(new File(CHART_INFO_YAML), ChartList.class).getCharts();
+        //Mock static method for bash command execution
+        mockedClient = mockStatic(HelmClient.class);
+    }
+
+    @Test
+    void test_installChart() throws IOException {
+        mockedClient.when(() -> HelmClient.executeCommand(any()))
+            .thenReturn("success");
+        doReturn(new File("/target/tmp/override.yaml")).when(chartStore)
+            .getOverrideFile(any());
+        assertDoesNotThrow(() -> helmClient.installChart(charts.get(0)));
+    }
+
+    @Test
+    void test_findChartRepository() throws IOException, ServiceException {
+        mockedClient.when(() -> HelmClient.executeCommand(Mockito.any()))
+            .thenReturn("nginx-stable/nginx-ingress\t0.9.3\t1.11.3"
+                + " \tNGINX Ingress Controller");
+        String configuredRepo = helmClient.findChartRepository(charts.get(1));
+
+        assertThat(configuredRepo).isEqualTo("nginx-stable");
+
+        doReturn(Path.of("/target/tmp/dummyChart/1.0")).when(chartStore).getAppPath(charts.get(1).getChartName(),
+            charts.get(1).getVersion());
+
+        doReturn(null).when(helmClient).verifyConfiguredRepo(charts.get(1));
+
+        String localRepoName = helmClient.findChartRepository(charts.get(1));
+        assertNotNull(localRepoName);
+        assertThat(localRepoName).endsWith(charts.get(0).getVersion());
+    }
+
+    @Test
+    void test_uninstallChart() throws ServiceException {
+        helmClient.uninstallChart(charts.get(0));
+        mockedClient.when(() -> HelmClient.executeCommand(any())).thenThrow(new ServiceException("error in execution"));
+
+        assertThatThrownBy(() -> helmClient.uninstallChart(charts.get(0)))
+            .isInstanceOf(ServiceException.class);
+    }
+
+    @Test
+    void test_verifyConfiguredRepoForInvalidChart() throws IOException, ServiceException {
+        mockedClient.when(() -> HelmClient.executeCommand(Mockito.any()))
+            .thenReturn("");
+        String configuredRepo = helmClient.verifyConfiguredRepo(charts.get(1));
+        assertNull(configuredRepo);
+    }
+
+}
diff --git a/participant/participant-impl/participant-impl-kubernetes/src/test/java/org.onap.policy.clamp.controlloop.participant.kubernetes/parameters/CommonTestData.java b/participant/participant-impl/participant-impl-kubernetes/src/test/java/org.onap.policy.clamp.controlloop.participant.kubernetes/parameters/CommonTestData.java
new file mode 100644 (file)
index 0000000..d8d477d
--- /dev/null
@@ -0,0 +1,152 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.controlloop.participant.kubernetes.parameters;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import org.onap.policy.common.endpoints.parameters.TopicParameters;
+import org.onap.policy.common.utils.coder.Coder;
+import org.onap.policy.common.utils.coder.CoderException;
+import org.onap.policy.common.utils.coder.StandardCoder;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
+
+public class CommonTestData {
+
+    public static final String PARTICIPANT_GROUP_NAME = "ControlLoopParticipantGroup";
+    public static final String DESCRIPTION = "Participant description";
+    public static final long TIME_INTERVAL = 2000;
+    public static final List<TopicParameters> TOPIC_PARAMS = Arrays.asList(getTopicParams());
+    public static final Coder CODER = new StandardCoder();
+
+
+    /**
+     * Get ParticipantK8sParameters.
+     *
+     * @return ParticipantK8sParameters
+     */
+    public ParticipantK8sParameters getParticipantK8sParameters() {
+        try {
+            return CODER.convert(getParticipantK8sParametersMap(PARTICIPANT_GROUP_NAME),
+                ParticipantK8sParameters.class);
+        } catch (final CoderException e) {
+            throw new RuntimeException("cannot create ParticipantK8sParameters from map", e);
+        }
+    }
+
+    /**
+     * Returns a property map for a ParticipantK8sParameters map for test cases.
+     *
+     * @param name name of the parameters
+     *
+     * @return a property map suitable for constructing an object
+     */
+    public Map<String, Object> getParticipantK8sParametersMap(final String name) {
+        final Map<String, Object> map = new TreeMap<>();
+
+        map.put("name", name);
+        map.put("intermediaryParameters", getIntermediaryParametersMap(false));
+        map.put("localChartDirectory", getLocalChartDir());
+        map.put("infoFileName", getInfoFileName());
+        return map;
+    }
+
+
+    /**
+     * Returns string value of local chart Directory.
+     * @return a string value
+     */
+    public String getLocalChartDir() {
+        return "/var/helm-manager/local-charts";
+    }
+
+    /**
+     * Returns string value of Info file name.
+     * @return string value
+     */
+    public String getInfoFileName() {
+        return "CHART-INFO.json";
+    }
+
+
+
+    /**
+     * Returns a property map for a intermediaryParameters map for test cases.
+     *
+     * @param isEmpty boolean value to represent that object created should be empty or not
+     * @return a property map suitable for constructing an object
+     */
+    public Map<String, Object> getIntermediaryParametersMap(final boolean isEmpty) {
+        final Map<String, Object> map = new TreeMap<>();
+        if (!isEmpty) {
+            map.put("name", "Participant parameters");
+            map.put("reportingTimeInterval", TIME_INTERVAL);
+            map.put("description", DESCRIPTION);
+            map.put("participantId", getParticipantId());
+            map.put("participantType", getParticipantId());
+            map.put("clampControlLoopTopics", getTopicParametersMap(false));
+        }
+
+        return map;
+    }
+
+    /**
+     * Returns participantId for test cases.
+     *
+     * @return participant Id
+     */
+    public static ToscaConceptIdentifier getParticipantId() {
+        final ToscaConceptIdentifier participantId = new ToscaConceptIdentifier();
+        participantId.setName("K8sParticipant0");
+        participantId.setVersion("1.0.0");
+        return participantId;
+    }
+
+
+    /**
+     * Returns a property map for a TopicParameters map for test cases.
+     *
+     * @param isEmpty boolean value to represent that object created should be empty or not
+     * @return a property map suitable for constructing an object
+     */
+    public Map<String, Object> getTopicParametersMap(final boolean isEmpty) {
+        final Map<String, Object> map = new TreeMap<>();
+        if (!isEmpty) {
+            map.put("topicSources", TOPIC_PARAMS);
+            map.put("topicSinks", TOPIC_PARAMS);
+        }
+        return map;
+    }
+
+    /**
+     * Returns topic parameters for test cases.
+     *
+     * @return topic parameters
+     */
+    public static TopicParameters getTopicParams() {
+        final TopicParameters topicParams = new TopicParameters();
+        topicParams.setTopic("POLICY-CLRUNTIME-PARTICIPANT");
+        topicParams.setTopicCommInfrastructure("dmaap");
+        topicParams.setServers(Arrays.asList("localhost"));
+        return topicParams;
+    }
+}
diff --git a/participant/participant-impl/participant-impl-kubernetes/src/test/java/org.onap.policy.clamp.controlloop.participant.kubernetes/parameters/ParticipantK8sParametersTest.java b/participant/participant-impl/participant-impl-kubernetes/src/test/java/org.onap.policy.clamp.controlloop.participant.kubernetes/parameters/ParticipantK8sParametersTest.java
new file mode 100644 (file)
index 0000000..177d7bc
--- /dev/null
@@ -0,0 +1,88 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.controlloop.participant.kubernetes.parameters;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.Set;
+import javax.validation.ConstraintViolation;
+import javax.validation.Validation;
+import javax.validation.ValidatorFactory;
+import org.junit.jupiter.api.Test;
+
+class ParticipantK8sParametersTest {
+
+    private CommonTestData commonTestData = new CommonTestData();
+    private ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
+
+    @Test
+    void testParticipantPolicyParameters() {
+        final ParticipantK8sParameters participantParameters = commonTestData.getParticipantK8sParameters();
+        assertThat(validatorFactory.getValidator().validate(participantParameters)).isNullOrEmpty();
+    }
+
+    @Test
+    void testParticipantK8sParameters_NullTopicSinks() {
+        final ParticipantK8sParameters participantParameters = commonTestData.getParticipantK8sParameters();
+        participantParameters.getIntermediaryParameters().getClampControlLoopTopics().setTopicSinks(null);
+        assertThat(validatorFactory.getValidator().validate(participantParameters)).isNotEmpty();
+    }
+
+    @Test
+    void testParticipantK8sParameters_NullTopicSources() {
+        final ParticipantK8sParameters participantParameters = commonTestData.getParticipantK8sParameters();
+        participantParameters.getIntermediaryParameters().getClampControlLoopTopics().setTopicSources(null);
+        assertThat(validatorFactory.getValidator().validate(participantParameters)).isNotEmpty();
+    }
+
+    @Test
+    void testParticipantK8sParameters_BlankLocalChartDirParameter() {
+        final ParticipantK8sParameters participantParameters = commonTestData.getParticipantK8sParameters();
+        participantParameters.setLocalChartDirectory(" ");
+        Set<ConstraintViolation<ParticipantK8sParameters>> violations = validatorFactory.getValidator()
+            .validate(participantParameters);
+        assertThat(violations.size()).isEqualTo(1);
+    }
+
+    @Test
+    void testParticipantK8sParameters_BlankInfoFileParameter() {
+        final ParticipantK8sParameters participantParameters = commonTestData.getParticipantK8sParameters();
+        participantParameters.setInfoFileName("");
+        Set<ConstraintViolation<ParticipantK8sParameters>> violations = validatorFactory.getValidator()
+            .validate(participantParameters);
+        assertThat(violations.size()).isEqualTo(1);
+    }
+
+    @Test
+    void testNoIntermediaryParameters() {
+        final ParticipantK8sParameters participantParameters = commonTestData.getParticipantK8sParameters();
+        participantParameters.setIntermediaryParameters(null);
+        assertThat(validatorFactory.getValidator().validate(participantParameters)).isNotEmpty();
+    }
+
+    @Test
+    void testNoParticipantId() {
+        final ParticipantK8sParameters participantParameters = commonTestData.getParticipantK8sParameters();
+        participantParameters.getIntermediaryParameters().setParticipantId(null);
+        assertThat(validatorFactory.getValidator().validate(participantParameters)).isNotEmpty();
+    }
+
+}
diff --git a/participant/participant-impl/participant-impl-kubernetes/src/test/java/org.onap.policy.clamp.controlloop.participant.kubernetes/rest/ChartControllerTest.java b/participant/participant-impl/participant-impl-kubernetes/src/test/java/org.onap.policy.clamp.controlloop.participant.kubernetes/rest/ChartControllerTest.java
new file mode 100644 (file)
index 0000000..1a1bdae
--- /dev/null
@@ -0,0 +1,230 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.controlloop.participant.kubernetes.rest;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.when;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import java.io.File;
+import java.util.List;
+import org.json.JSONObject;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.onap.policy.clamp.controlloop.participant.kubernetes.controller.ChartController;
+import org.onap.policy.clamp.controlloop.participant.kubernetes.models.ChartInfo;
+import org.onap.policy.clamp.controlloop.participant.kubernetes.models.ChartList;
+import org.onap.policy.clamp.controlloop.participant.kubernetes.parameters.ParticipantK8sParameters;
+import org.onap.policy.clamp.controlloop.participant.kubernetes.service.ChartService;
+import org.onap.policy.common.utils.coder.Coder;
+import org.onap.policy.common.utils.coder.CoderException;
+import org.onap.policy.common.utils.coder.StandardCoder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.http.MediaType;
+import org.springframework.mock.web.MockMultipartFile;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.RequestBuilder;
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+
+@ExtendWith(SpringExtension.class)
+@WebMvcTest(value = ChartController.class)
+@EnableConfigurationProperties(value = ParticipantK8sParameters.class)
+class ChartControllerTest {
+
+    private static final Coder CODER = new StandardCoder();
+    private static final String CHART_INFO_YAML = "src/test/resources/ChartList.json";
+    private static List<ChartInfo> charts;
+    private static String DEFAULT_CHART_URL = "/helm/charts";
+    private static String INSTALL_CHART_URL = "/helm/install";
+    private static String UNINSTALL_CHART_URL = "/helm/uninstall/";
+
+    @Autowired
+    private MockMvc mockMvc;
+
+    @MockBean
+    private ChartService chartService;
+
+    @Autowired
+    private WebApplicationContext context;
+
+    /**
+     * Read input chart info json.
+     * @throws Exception incase of error.
+     */
+    @BeforeAll
+    static void setupParams() throws CoderException {
+        charts = CODER.decode(new File(CHART_INFO_YAML), ChartList.class).getCharts();
+    }
+
+    /**
+     * Mock service layer in Controller.
+     * @throws Exception incase of error.
+     */
+    @BeforeEach
+    void mockServiceClass() {
+        when(chartService.getAllCharts()).thenReturn(charts);
+        when(chartService.getChart(charts.get(0).getChartName(), charts.get(0).getVersion()))
+            .thenReturn(charts.get(0));
+
+        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build();
+    }
+
+    /**
+     * Test endpoint for retrieving all charts.
+     * @throws Exception incase of error.
+     */
+    @Test
+    void retrieveAllCharts() throws Exception {
+        RequestBuilder requestBuilder;
+        requestBuilder = MockMvcRequestBuilders.get(DEFAULT_CHART_URL).accept(MediaType.APPLICATION_JSON_VALUE);
+
+        mockMvc.perform(requestBuilder).andExpect(status().isOk())
+            .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
+            .andExpect(jsonPath("$.charts.[0].chartName", is("HelloWorld")));
+    }
+
+    /**
+     * Test endpoint for installing a chart.
+     * @throws Exception incase of error.
+     */
+    @Test
+    void installChart() throws Exception {
+        RequestBuilder requestBuilder;
+
+        //Mocking successful installation for void install method
+        doNothing().when(chartService).installChart(charts.get(0));
+
+        requestBuilder = MockMvcRequestBuilders.post(INSTALL_CHART_URL).accept(MediaType.APPLICATION_JSON_VALUE)
+            .content(getInstallationJson(charts.get(0).getChartName(), charts.get(0).getVersion()))
+            .contentType(MediaType.APPLICATION_JSON_VALUE);
+
+        mockMvc.perform(requestBuilder).andExpect(status().isCreated());
+
+        //Install Invalid chart, expects HTTP status NOT_FOUND
+        requestBuilder = MockMvcRequestBuilders.post(INSTALL_CHART_URL).accept(MediaType.APPLICATION_JSON_VALUE)
+            .content(getInstallationJson("invalidName", "invalidVersion"))
+            .contentType(MediaType.APPLICATION_JSON_VALUE);
+
+        mockMvc.perform(requestBuilder).andExpect(status().isNotFound());
+    }
+
+    /**
+     * Test endpoint for uninstalling a chart.
+     * @throws Exception incase of error.
+     */
+    @Test
+    void uninstallChart() throws Exception {
+        RequestBuilder requestBuilder;
+
+        //Mocking successful scenario for void uninstall method
+        doNothing().when(chartService).uninstallChart(charts.get(0));
+
+        requestBuilder = MockMvcRequestBuilders.delete(UNINSTALL_CHART_URL + charts.get(0).getChartName()
+            + "/" + charts.get(0).getVersion()).accept(MediaType.APPLICATION_JSON_VALUE)
+            .contentType(MediaType.APPLICATION_JSON_VALUE);
+
+        mockMvc.perform(requestBuilder).andExpect(status().isNoContent());
+
+        //Invalid chart
+        requestBuilder = MockMvcRequestBuilders.delete(UNINSTALL_CHART_URL + "invalidName"
+            + "/" + "invalidVersion").accept(MediaType.APPLICATION_JSON_VALUE)
+            .contentType(MediaType.APPLICATION_JSON_VALUE);
+
+        mockMvc.perform(requestBuilder).andExpect(status().isNotFound());
+    }
+
+    /**
+     * Test endpoint for chart onboarding.
+     * @throws Exception incase of error.
+     */
+    @Test
+    void onboardChart() throws Exception {
+        RequestBuilder requestBuilder;
+        MockMultipartFile chartFile = new MockMultipartFile("chart", "hello.tgz",
+            MediaType.TEXT_PLAIN_VALUE, "Dummy data".getBytes());
+
+        MockMultipartFile overrideFile = new MockMultipartFile("values", "values.yaml",
+            MediaType.TEXT_PLAIN_VALUE, "Dummy data".getBytes());
+
+        //Mocking successful scenario for void uninstall method
+        when(chartService.saveChart(charts.get(0), chartFile, null)).thenReturn(charts.get(0));
+
+        requestBuilder = MockMvcRequestBuilders.multipart(DEFAULT_CHART_URL)
+            .file(chartFile).file(overrideFile).param("info", getChartInfoJson());
+
+        mockMvc.perform(requestBuilder).andExpect(status().isOk());
+    }
+
+    /**
+     * Test endpoint for deleting a chart.
+     * @throws Exception incase of error.
+     */
+    @Test
+    void deleteChart() throws Exception {
+        RequestBuilder requestBuilder;
+
+        //Mocking successful scenario for void uninstall method
+        doNothing().when(chartService).deleteChart(charts.get(0));
+
+        requestBuilder = MockMvcRequestBuilders.delete(DEFAULT_CHART_URL + "/" + charts.get(0).getChartName()
+            + "/" + charts.get(0).getVersion()).accept(MediaType.APPLICATION_JSON_VALUE)
+            .contentType(MediaType.APPLICATION_JSON_VALUE);
+
+        mockMvc.perform(requestBuilder).andExpect(status().isNoContent());
+        //Invalid chart
+        requestBuilder = MockMvcRequestBuilders.delete(UNINSTALL_CHART_URL + "invalidName"
+            + "/" + "invalidVersion").accept(MediaType.APPLICATION_JSON_VALUE)
+            .contentType(MediaType.APPLICATION_JSON_VALUE);
+
+        mockMvc.perform(requestBuilder).andExpect(status().isNotFound());
+
+    }
+
+    private String getInstallationJson(String name, String version) {
+        JSONObject jsonObj = new JSONObject();
+        jsonObj.put("name", name);
+        jsonObj.put("version", version);
+        return jsonObj.toString();
+    }
+
+    private String getChartInfoJson() {
+        JSONObject jsonObj = new JSONObject();
+        jsonObj.put("chartName", charts.get(0).getChartName());
+        jsonObj.put("version", charts.get(0).getVersion());
+        jsonObj.put("namespace", charts.get(0).getNamespace());
+        jsonObj.put("repository", charts.get(0).getRepository());
+        jsonObj.put("releaseName", charts.get(0).getReleaseName());
+        return jsonObj.toString();
+    }
+
+}
diff --git a/participant/participant-impl/participant-impl-kubernetes/src/test/java/org.onap.policy.clamp.controlloop.participant.kubernetes/service/ChartServiceTest.java b/participant/participant-impl/participant-impl-kubernetes/src/test/java/org.onap.policy.clamp.controlloop.participant.kubernetes/service/ChartServiceTest.java
new file mode 100644 (file)
index 0000000..957a69a
--- /dev/null
@@ -0,0 +1,147 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.controlloop.participant.kubernetes.service;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.onap.policy.clamp.controlloop.participant.kubernetes.exception.ServiceException;
+import org.onap.policy.clamp.controlloop.participant.kubernetes.helm.HelmClient;
+import org.onap.policy.clamp.controlloop.participant.kubernetes.models.ChartInfo;
+import org.onap.policy.clamp.controlloop.participant.kubernetes.models.ChartList;
+import org.onap.policy.common.utils.coder.Coder;
+import org.onap.policy.common.utils.coder.CoderException;
+import org.onap.policy.common.utils.coder.StandardCoder;
+import org.springframework.mock.web.MockMultipartFile;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+@ExtendWith(SpringExtension.class)
+class ChartServiceTest {
+
+    private static final Coder CODER = new StandardCoder();
+    private static final String CHART_INFO_YAML = "src/test/resources/ChartList.json";
+    private static List<ChartInfo> charts;
+
+    @InjectMocks
+    @Spy
+    private ChartService chartService = new ChartService();
+
+    @Mock
+    private ChartStore chartStore;
+
+    @Mock
+    private HelmClient helmClient;
+
+    @BeforeAll
+    static void init() throws CoderException {
+        charts = CODER.decode(new File(CHART_INFO_YAML), ChartList.class).getCharts();
+    }
+
+    @Test
+    void test_getAllCharts() {
+        assertThat(chartService.getAllCharts()).isEmpty();
+
+        doReturn(charts).when(chartStore).getAllCharts();
+        Collection<ChartInfo> result = chartService.getAllCharts();
+        assertNotNull(result);
+        assertThat(result).containsAll(charts);
+    }
+
+    @Test
+    void test_getChart() {
+        assertNull(chartService.getChart("dummyName", "dummyversion"));
+
+        doReturn(charts.get(0)).when(chartStore).getChart(any(), any());
+        ChartInfo chart = chartService.getChart(charts.get(0).getChartName(),
+            charts.get(0).getVersion());
+        assertNotNull(chart);
+        assertThat(chart.getNamespace()).isEqualTo(charts.get(0).getNamespace());
+    }
+
+    @Test
+    void test_saveChart() throws IOException, ServiceException {
+        doThrow(IOException.class).when(chartStore).saveChart(charts.get(0), null, null);
+        assertThatThrownBy(() -> chartService.saveChart(charts.get(0), null, null))
+            .isInstanceOf(IOException.class);
+
+        MockMultipartFile mockChartFile = new MockMultipartFile("chart", "dummy".getBytes());
+        MockMultipartFile mockOverrideFile = new MockMultipartFile("override", "dummy".getBytes());
+
+        doReturn(charts.get(0)).when(chartStore).saveChart(any(), any(), any());
+
+        ChartInfo chart = chartService.saveChart(charts.get(0), mockChartFile, mockOverrideFile);
+        assertNotNull(chart);
+        assertThat(chart.getChartName()).isEqualTo(charts.get(0).getChartName());
+
+    }
+
+    @Test
+    void test_installChart() throws IOException, ServiceException {
+        assertDoesNotThrow(() -> chartService.installChart(charts.get(0)));
+        doThrow(ServiceException.class).when(helmClient).installChart(any());
+        assertThatThrownBy(() -> chartService.installChart(charts.get(0))).isInstanceOf(ServiceException.class);
+
+        doReturn("dummyRepoName").when(chartService).findChartRepo(any());
+        doNothing().when(helmClient).installChart(any());
+        chartService.installChart(charts.get(1));
+        assertEquals("dummyRepoName", charts.get(1).getRepository());
+
+        ChartInfo testChart = charts.get(1);
+        testChart.setRepository(null);
+        doReturn(null).when(chartService).findChartRepo(any());
+        chartService.installChart(charts.get(1));
+    }
+
+    @Test
+    void test_UninstallChart() throws ServiceException {
+        assertDoesNotThrow(() -> chartService.uninstallChart(charts.get(0)));
+        doThrow(ServiceException.class).when(helmClient).uninstallChart(any());
+        assertThatThrownBy(() -> chartService.uninstallChart(charts.get(0))).isInstanceOf(ServiceException.class);
+    }
+
+    @Test
+    void test_findChartRepo() throws IOException, ServiceException {
+        assertDoesNotThrow(() -> chartService.findChartRepo(charts.get(0)));
+        doReturn("dummyRepoName").when(helmClient).findChartRepository(any());
+        assertEquals("dummyRepoName", chartService.findChartRepo(charts.get(1)));
+
+        doThrow(ServiceException.class).when(helmClient).findChartRepository(any());
+        assertThatThrownBy(() -> chartService.findChartRepo(charts.get(0))).isInstanceOf(ServiceException.class);
+    }
+}
diff --git a/participant/participant-impl/participant-impl-kubernetes/src/test/java/org.onap.policy.clamp.controlloop.participant.kubernetes/service/ChartStoreTest.java b/participant/participant-impl/participant-impl-kubernetes/src/test/java/org.onap.policy.clamp.controlloop.participant.kubernetes/service/ChartStoreTest.java
new file mode 100644 (file)
index 0000000..2d05a7a
--- /dev/null
@@ -0,0 +1,170 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.controlloop.participant.kubernetes.service;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.Assert.assertNull;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.mockito.junit.jupiter.MockitoSettings;
+import org.mockito.quality.Strictness;
+import org.onap.policy.clamp.controlloop.participant.kubernetes.exception.ServiceException;
+import org.onap.policy.clamp.controlloop.participant.kubernetes.models.ChartInfo;
+import org.onap.policy.clamp.controlloop.participant.kubernetes.models.ChartList;
+import org.onap.policy.clamp.controlloop.participant.kubernetes.parameters.ParticipantK8sParameters;
+import org.onap.policy.common.utils.coder.Coder;
+import org.onap.policy.common.utils.coder.CoderException;
+import org.onap.policy.common.utils.coder.StandardCoder;
+import org.springframework.mock.web.MockMultipartFile;
+import org.springframework.util.FileSystemUtils;
+
+
+@ExtendWith(MockitoExtension.class)
+@MockitoSettings(strictness = Strictness.LENIENT)
+class ChartStoreTest {
+
+    private static final Coder CODER = new StandardCoder();
+    private static final String CHART_INFO_YAML = "src/test/resources/ChartList.json";
+    private static List<ChartInfo> charts;
+
+    @Mock
+    private ParticipantK8sParameters parameters;
+
+    private ChartStore chartStore;
+
+
+    @BeforeAll
+    static void init() throws CoderException {
+        charts = CODER.decode(new File(CHART_INFO_YAML), ChartList.class).getCharts();
+    }
+
+    //Overriding the local chart dir parameter to a temp folder under target for testing java FILE IO operations.
+    @BeforeEach
+    void setup() {
+        Mockito.doReturn("target/tmp/").when(parameters).getLocalChartDirectory();
+        Mockito.doReturn("info.json").when(parameters).getInfoFileName();
+        chartStore = new ChartStore(parameters);
+    }
+
+    //Clean up the 'tmp' dir after each test case.
+    @AfterEach
+    void cleanUp() throws IOException {
+        FileSystemUtils.deleteRecursively(Path.of(parameters.getLocalChartDirectory()));
+        chartStore.getLocalChartMap().clear();
+    }
+
+    @Test
+    void test_getHelmChartFile() {
+        File file = chartStore.getHelmChartFile(charts.get(0));
+        assertNotNull(file);
+        assertThat(file.getPath()).endsWith(charts.get(0).getChartName());
+    }
+
+    @Test
+    void test_getOverrideFile() {
+        File file = chartStore.getOverrideFile(charts.get(0));
+        assertNotNull(file);
+        assertThat(file.getPath()).endsWith("values.yaml");
+    }
+
+    @Test
+    void test_saveChart() throws IOException, ServiceException {
+        MockMultipartFile mockChartFile = new MockMultipartFile("chart", "dummy".getBytes());
+        MockMultipartFile mockOverrideFile = new MockMultipartFile("override", "dummy".getBytes());
+        ChartInfo testChart = charts.get(0);
+        testChart.setChartName("testChart");
+        ChartInfo result = chartStore.saveChart(charts.get(0), mockChartFile, mockOverrideFile);
+
+        assertThat(result.getChartName()).isEqualTo("testChart");
+        assertThat(chartStore.getLocalChartMap()).hasSize(1);
+
+        assertThatThrownBy(() -> chartStore.saveChart(charts.get(0), mockChartFile, mockOverrideFile))
+            .isInstanceOf(ServiceException.class);
+    }
+
+
+    @Test
+    void test_getChart() {
+        assertNull(chartStore.getChart(charts.get(0).getChartName(), charts.get(0).getVersion()));
+        chartStore.getLocalChartMap().put(charts.get(0).getChartName() + "_" + charts.get(0).getVersion(),
+            charts.get(0));
+        ChartInfo chart = chartStore.getChart(charts.get(0).getChartName(), charts.get(0).getVersion());
+        assertThat(chart.getChartName()).isEqualTo(charts.get(0).getChartName());
+    }
+
+    @Test
+    void test_getAllChart() {
+        // When the chart store is empty before adding any charts
+        assertThat(chartStore.getAllCharts()).isEmpty();
+
+        for (ChartInfo chart : charts) {
+            chartStore.getLocalChartMap().put(chart.getChartName() + "_" + chart.getVersion(), chart);
+        }
+        List<ChartInfo> retrievedChartList = chartStore.getAllCharts();
+        assertThat(retrievedChartList).isNotEmpty();
+        assertThat(retrievedChartList.size()).isEqualTo(charts.size());
+    }
+
+    @Test
+    void test_deleteChart() {
+        chartStore.getLocalChartMap().put(charts.get(0).getChartName() + "_" + charts.get(0).getVersion(),
+            charts.get(0));
+        assertThat(chartStore.getLocalChartMap()).hasSize(1);
+        chartStore.deleteChart(charts.get(0));
+        assertThat(chartStore.getLocalChartMap()).isEmpty();
+    }
+
+    @Test
+    void test_getAppPath() {
+        Path path = chartStore.getAppPath(charts.get(0).getChartName(), charts.get(0).getVersion());
+        assertNotNull(path);
+        assertThat(path.toString()).endsWith(charts.get(0).getVersion());
+        assertThat(path.toString()).startsWith("target");
+    }
+
+    @Test
+    void test_chartSoreInstantiationWithExistingChartFiles() throws IOException, ServiceException {
+        MockMultipartFile mockChartFile = new MockMultipartFile("HelmChartFile", "dummyData".getBytes());
+        MockMultipartFile mockOverrideFile = new MockMultipartFile("overrideFile.yaml", "dummyData".getBytes());
+        ChartInfo testChart = charts.get(0);
+        testChart.setChartName("dummyChart");
+
+        //Creating a dummy chart in local dir.
+        chartStore.saveChart(charts.get(0), mockChartFile, mockOverrideFile);
+
+        //Instantiating a new chartStore object with pre available chart in local.
+        ChartStore chartStore2 = new ChartStore(parameters);
+        assertThat(chartStore2.getLocalChartMap()).hasSize(1).containsKey("dummyChart_" + charts.get(0).getVersion());
+    }
+}
diff --git a/participant/participant-impl/participant-impl-kubernetes/src/test/java/org.onap.policy.clamp.controlloop.participant.kubernetes/utils/TestUtils.java b/participant/participant-impl/participant-impl-kubernetes/src/test/java/org.onap.policy.clamp.controlloop.participant.kubernetes/utils/TestUtils.java
new file mode 100644 (file)
index 0000000..8585e9d
--- /dev/null
@@ -0,0 +1,43 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.controlloop.participant.kubernetes.utils;
+
+import org.onap.policy.common.utils.coder.YamlJsonTranslator;
+import org.onap.policy.common.utils.resources.ResourceUtils;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
+
+public class TestUtils {
+
+    private static final YamlJsonTranslator yamlTranslator = new YamlJsonTranslator();
+    private static final String TOSCA_TEMPLATE_YAML = "src/test/resources/servicetemplates/KubernetesHelm.yaml";
+
+
+    public static ToscaServiceTemplate testControlLoopRead() {
+        return testControlLoopYamlSerialization(TOSCA_TEMPLATE_YAML);
+    }
+
+
+    private static ToscaServiceTemplate testControlLoopYamlSerialization(String controlLoopFilePath) {
+        String controlLoopString = ResourceUtils.getResourceAsString(controlLoopFilePath);
+        ToscaServiceTemplate serviceTemplate = yamlTranslator.fromYaml(controlLoopString, ToscaServiceTemplate.class);
+        return serviceTemplate;
+    }
+}
diff --git a/participant/participant-impl/participant-impl-kubernetes/src/test/resources/ChartList.json b/participant/participant-impl/participant-impl-kubernetes/src/test/resources/ChartList.json
new file mode 100644 (file)
index 0000000..4e355c3
--- /dev/null
@@ -0,0 +1,15 @@
+{
+  "charts" : [
+    {
+      "chartName" : "HelloWorld",
+      "version" :   "1.0",
+      "namespace" : "onap",
+      "repository" : "chartMuseum"
+    },
+    {
+      "chartName" : "nginx",
+      "version" :   "1.1",
+      "namespace" : "onap"
+    }
+  ]
+}
diff --git a/participant/participant-impl/participant-impl-kubernetes/src/test/resources/application_test.properties b/participant/participant-impl/participant-impl-kubernetes/src/test/resources/application_test.properties
new file mode 100644 (file)
index 0000000..188623a
--- /dev/null
@@ -0,0 +1,26 @@
+spring.security.user.name=healthcheck
+spring.security.user.password=zb!XztG34
+
+server.servlet.context-path=/onap/participantsim
+server.error.path=/error
+server.http-port=8083
+
+participant.name=ControlLoopParticipant Kubernetes Test
+participant.intermediaryParameters.name=Participant parameters
+participant.intermediaryParameters.reportingTimeInterval=120000
+participant.intermediaryParameters.description=Participant Description
+participant.intermediaryParameters.participantId.name=K8sParticipant0
+participant.intermediaryParameters.participantId.version=1.0.0
+participant.intermediaryParameters.participantType.name=org.onap.k8s.controlloop.K8SControlLoopParticipant
+participant.intermediaryParameters.participantType.version=2.3.4
+participant.intermediaryParameters.clampControlLoopTopics.name=ControlLoop Topics
+participant.intermediaryParameters.clampControlLoopTopics.topicSources[0].topic=POLICY-CLRUNTIME-PARTICIPANT
+participant.intermediaryParameters.clampControlLoopTopics.topicSources[0].servers[0]=localhost
+participant.intermediaryParameters.clampControlLoopTopics.topicSources[0].topicCommInfrastructure=dmaap
+participant.intermediaryParameters.clampControlLoopTopics.topicSources[0].fetchTimeout=15000
+participant.intermediaryParameters.clampControlLoopTopics.topicSinks[0].topic=POLICY-CLRUNTIME-PARTICIPANT
+participant.intermediaryParameters.clampControlLoopTopics.topicSinks[0].servers[0]=localhost
+participant.intermediaryParameters.clampControlLoopTopics.topicSinks[0].topicCommInfrastructure=dmaap
+participant.intermediaryParameters.clampControlLoopTopics.topicSinks[1].topic=POLICY-NOTIFICATION
+participant.intermediaryParameters.clampControlLoopTopics.topicSinks[1].servers[0]=localhost
+participant.intermediaryParameters.clampControlLoopTopics.topicSinks[1].topicCommInfrastructure=dmaap
diff --git a/pom.xml b/pom.xml
index b330897..eb8b58a 100644 (file)
--- a/pom.xml
+++ b/pom.xml
             <groupId>ch.qos.logback</groupId>
             <artifactId>logback-classic</artifactId>
         </dependency>
+        <dependency>
+            <groupId>commons-fileupload</groupId>
+            <artifactId>commons-fileupload</artifactId>
+        </dependency>
     </dependencies>
 </project>