Refactor participant-Kubernetes 55/142955/5
authorFrancescoFioraEst <francesco.fiora@est.tech>
Mon, 19 Jan 2026 11:49:12 +0000 (11:49 +0000)
committerFrancescoFioraEst <francesco.fiora@est.tech>
Mon, 26 Jan 2026 08:38:45 +0000 (08:38 +0000)
Refactor participant-Kubernetes and remove duplication
of Spring config file.

Issue-ID: POLICY-5533
Change-Id: I3cfe04d4eadde82b476f6131eecb7958c311b5fa
Signed-off-by: FrancescoFioraEst <francesco.fiora@est.tech>
examples/src/main/resources/clamp/acm/acelement-helm/acelement/values.yaml
participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/acm/participant/kubernetes/handler/AutomationCompositionElementHandler.java
participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/acm/participant/kubernetes/helm/HelmClient.java
participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/acm/participant/kubernetes/helm/PodStatusValidator.java
participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/acm/participant/kubernetes/parameters/HelmRepositoryConfig.java [moved from participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/acm/participant/kubernetes/configurations/HelmRepositoryConfig.java with 62% similarity]
participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/acm/participant/kubernetes/service/ChartService.java
participant/participant-impl/participant-impl-kubernetes/src/main/resources/config/application.yaml
participant/participant-impl/participant-impl-kubernetes/src/test/java/org/onap/policy/clamp/acm/participant/kubernetes/handler/AutomationCompositionElementHandlerTest.java
participant/participant-impl/participant-impl-kubernetes/src/test/java/org/onap/policy/clamp/acm/participant/kubernetes/helm/HelmClientTest.java
participant/participant-impl/participant-impl-kubernetes/src/test/java/org/onap/policy/clamp/acm/participant/kubernetes/helm/PodStatusValidatorTest.java
participant/participant-impl/participant-impl-kubernetes/src/test/java/org/onap/policy/clamp/acm/participant/kubernetes/service/ChartServiceTest.java

index 2d1fd1d..e78fac1 100644 (file)
@@ -1,5 +1,5 @@
 # ============LICENSE_START=======================================================
-# Copyright (C) 2022-2024 Nordix Foundation.
+# Copyright (C) 2022-2024,2026 OpenInfra Foundation Europe. All rights reserved.
 # ================================================================================
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -26,7 +26,7 @@ image:
   repository: nexus3.onap.org:10001
   name: onap/policy-clamp-acm-element-impl
   pullPolicy: IfNotPresent
-  tag: "8.0.1"
+  tag: "8.2.2"
 
 nameOverride: "ac-element-impl"
 
index 451ed22..79cac55 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- *  Copyright (C) 2021-2024 Nordix Foundation.
+ *  Copyright (C) 2021-2024,2026 OpenInfra Foundation Europe. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -42,7 +42,6 @@ import org.onap.policy.common.utils.coder.StandardCoder;
 import org.onap.policy.models.base.PfModelException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 /**
@@ -55,10 +54,20 @@ public class AutomationCompositionElementHandler extends AcElementListenerV3 {
     private static final Coder CODER = new StandardCoder();
 
     private final ChartService chartService;
+    private final PodStatusValidator podStatusValidator;
 
-    public AutomationCompositionElementHandler(ParticipantIntermediaryApi intermediaryApi, ChartService chartService) {
+    /**
+     * Constructor.
+     *
+     * @param intermediaryApi the ParticipantIntermediaryApi
+     * @param chartService the ChartService
+     * @param podStatusValidator the PodStatusValidator
+     */
+    public AutomationCompositionElementHandler(ParticipantIntermediaryApi intermediaryApi, ChartService chartService,
+            PodStatusValidator podStatusValidator) {
         super(intermediaryApi);
         this.chartService = chartService;
+        this.podStatusValidator = podStatusValidator;
     }
 
 
@@ -153,8 +162,7 @@ public class AutomationCompositionElementHandler extends AcElementListenerV3 {
             int podStatusCheckInterval, InstanceElementDto instanceElement) throws InterruptedException,
             PfModelException {
 
-        var result = new PodStatusValidator(chart, timeout, podStatusCheckInterval);
-        result.run();
+        podStatusValidator.run(timeout, podStatusCheckInterval, chart);
         LOGGER.info("Pod Status Validator Completed");
         intermediaryApi.updateAutomationCompositionElementState(automationCompositionId, elementId,
                 DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Deployed");
index da8d455..043bb2b 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ========================LICENSE_START=================================
- * Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved.
+ * Copyright (C) 2021-2026 OpenInfra Foundation Europe. All rights reserved.
  * ======================================================================
  * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
  * ======================================================================
@@ -26,7 +26,7 @@ import java.lang.invoke.MethodHandles;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
-import lombok.NoArgsConstructor;
+import lombok.RequiredArgsConstructor;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.onap.policy.clamp.acm.participant.kubernetes.exception.ServiceException;
@@ -35,17 +35,16 @@ import org.onap.policy.clamp.acm.participant.kubernetes.models.HelmRepository;
 import org.onap.policy.clamp.acm.participant.kubernetes.service.ChartStore;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 /**
  * Client to talk with Helm cli. Supports helm3 + version
  */
 @Component
-@NoArgsConstructor
+@RequiredArgsConstructor
 public class HelmClient {
 
-    private ChartStore chartStore;
+    private final ChartStore chartStore;
 
     private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
     private static final String PATH_DELIMITER = "/";
@@ -53,11 +52,6 @@ public class HelmClient {
     private static final String COMMAND_HELM = "/usr/local/bin/helm";
     public static final String COMMAND_KUBECTL = "/usr/local/bin/kubectl";
 
-    @Autowired
-    public HelmClient(ChartStore chartStore) {
-        this.chartStore = chartStore;
-    }
-
     /**
      * Install a chart.
      *
@@ -95,7 +89,6 @@ public class HelmClient {
         return false;
     }
 
-
     /**
      * Finds helm chart repository for the chart.
      *
@@ -129,11 +122,9 @@ public class HelmClient {
      */
     public String verifyConfiguredRepo(ChartInfo chart) throws ServiceException {
         logger.info("Looking for helm chart {} in all the configured helm repositories", chart.getChartId().getName());
-        String repository;
         var builder = helmRepoVerifyCommand(chart.getChartId().getName());
-        String output = executeCommand(builder);
-        repository = verifyOutput(output, chart.getChartId().getName());
-        return repository;
+        var output = executeCommand(builder);
+        return verifyOutput(output, chart.getChartId().getName());
     }
 
     /**
@@ -146,7 +137,6 @@ public class HelmClient {
         executeCommand(prepareUnInstallCommand(chart));
     }
 
-
     /**
      * Execute helm cli bash commands.
      *
@@ -184,7 +174,7 @@ public class HelmClient {
 
     private boolean checkNamespaceExists(String namespace) throws ServiceException {
         logger.info("Check if namespace {} exists on the cluster", namespace);
-        String output = executeCommand(prepareVerifyNamespaceCommand(namespace));
+        var output = executeCommand(prepareVerifyNamespaceCommand(namespace));
         return !output.isEmpty();
     }
 
@@ -218,7 +208,7 @@ public class HelmClient {
         try {
             logger.debug("Verify the repo already exist in helm repositories");
             var helmArguments = List.of(COMMAND_SH, "-c", COMMAND_HELM + " repo list | grep " + repo.getRepoName());
-            String response = executeCommand(new ProcessBuilder().command(helmArguments));
+            var response = executeCommand(new ProcessBuilder().command(helmArguments));
             if (StringUtils.isEmpty(response)) {
                 return false;
             }
index 3eba942..5e3f465 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ========================LICENSE_START=================================
- * Copyright (C) 2021-2024 Nordix Foundation. All rights reserved.
+ * Copyright (C) 2021-2024,2026 OpenInfra Foundation Europe. All rights reserved.
  * ======================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@ import java.lang.invoke.MethodHandles;
 import java.nio.charset.StandardCharsets;
 import java.util.HashMap;
 import java.util.Map;
+import lombok.RequiredArgsConstructor;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.onap.policy.clamp.acm.participant.kubernetes.exception.ServiceException;
@@ -33,57 +34,41 @@ import org.onap.policy.clamp.acm.participant.kubernetes.models.ChartInfo;
 import org.onap.policy.models.base.PfModelException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
 
-
+@Component
+@RequiredArgsConstructor
 public class PodStatusValidator {
 
     private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
-    private final int statusCheckInterval;
-
-    //Timeout for the thread to exit.
-    private final int timeout;
-
-    private ChartInfo chart;
-
-    private HelmClient client = new HelmClient();
-
-    /**
-     * Constructor for PodStatusValidator.
-     *
-     * @param chart chartInfo
-     * @param timeout timeout for the thread to exit
-     * @param statusCheckInterval Interval to check pod status
-     */
-    public PodStatusValidator(ChartInfo chart, int timeout, int statusCheckInterval) {
-        this.chart = chart;
-        this.timeout = timeout;
-        this.statusCheckInterval = statusCheckInterval;
-    }
+    private final HelmClient client;
 
     /**
      * Run the execution.
      *
      * @throws InterruptedException in case of an exception
-     * @throws ServiceException in case of an exception
+     * @throws PfModelException in case of an exception
      */
-    public void run() throws InterruptedException, PfModelException {
+    public void run(int timeout, int statusCheckInterval, ChartInfo chart)
+            throws InterruptedException, PfModelException {
         logger.info("Polling the status of deployed pods for the chart {}", chart.getChartId().getName());
 
         try {
-            verifyPodStatus();
+            verifyPodStatus(timeout, statusCheckInterval, chart);
         } catch (IOException | ServiceException e) {
             throw new PfModelException(Response.Status.BAD_REQUEST, "Error verifying the status of the pod. Exiting");
         }
     }
 
-    private void verifyPodStatus() throws ServiceException, IOException, InterruptedException, PfModelException {
+    private void verifyPodStatus(int timeout, int statusCheckInterval, ChartInfo chart)
+            throws ServiceException, IOException, InterruptedException, PfModelException {
         var isVerified = false;
         long endTime = System.currentTimeMillis() + (timeout * 1000L);
 
         while (!isVerified && System.currentTimeMillis() < endTime) {
             var output = client.executeCommand(verifyPodStatusCommand(chart));
-            var podStatusMap = mapPodStatus(output);
+            var podStatusMap = mapPodStatus(chart, output);
             isVerified = !podStatusMap.isEmpty()
                     && podStatusMap.values().stream().allMatch("Running"::equals);
             if (!isVerified) {
@@ -103,17 +88,17 @@ public class PodStatusValidator {
 
     private ProcessBuilder verifyPodStatusCommand(ChartInfo chart) {
         String cmd = HelmClient.COMMAND_KUBECTL
-            + " get pods --namespace " + chart.getNamespace() + " | grep " + getPodName();
+            + " get pods --namespace " + chart.getNamespace() + " | grep " + getPodName(chart);
         return new ProcessBuilder(HelmClient.COMMAND_SH, "-c", cmd);
     }
 
-    private String getPodName() {
+    private String getPodName(ChartInfo chart) {
         return StringUtils.isNotEmpty(chart.getPodName()) ? chart.getPodName() : chart.getChartId().getName();
     }
 
-    private Map<String, String> mapPodStatus(String output) throws IOException {
+    private Map<String, String> mapPodStatus(ChartInfo chart, String output) throws IOException {
         Map<String, String> podStatusMap = new HashMap<>();
-        var podName = getPodName();
+        var podName = getPodName(chart);
         try (var reader = new BufferedReader(new InputStreamReader(IOUtils.toInputStream(output,
             StandardCharsets.UTF_8)))) {
             var line = reader.readLine();
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- *  Copyright (C) 2022 Nordix Foundation.
+ *  Copyright (C) 2022, 2026 OpenInfra Foundation Europe. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * ============LICENSE_END=========================================================
  */
 
-package org.onap.policy.clamp.acm.participant.kubernetes.configurations;
+package org.onap.policy.clamp.acm.participant.kubernetes.parameters;
 
-import java.lang.invoke.MethodHandles;
-import java.util.ArrayList;
-import java.util.List;
 import lombok.Data;
-import org.onap.policy.clamp.acm.participant.kubernetes.models.HelmRepository;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.stereotype.Component;
 
-@Component
 @ConfigurationProperties(prefix = "helm")
 @Data
 public class HelmRepositoryConfig {
 
-    private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+    private String repos = "[]";
 
-    private List<HelmRepository> repos = new ArrayList<>();
-
-    private List<String> protocols = new ArrayList<>();
+    private String protocols = "";
 }
index 2274dd7..8d7ad04 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ========================LICENSE_START=================================
- * Copyright (C) 2021-2022, 2025 OpenInfra Foundation Europe. All rights reserved.
+ * Copyright (C) 2021-2022, 2025-2026 OpenInfra Foundation Europe. All rights reserved.
  * ======================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -20,43 +20,35 @@ package org.onap.policy.clamp.acm.participant.kubernetes.service;
 
 import java.io.IOException;
 import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
-import lombok.NoArgsConstructor;
-import org.onap.policy.clamp.acm.participant.kubernetes.configurations.HelmRepositoryConfig;
+import lombok.RequiredArgsConstructor;
 import org.onap.policy.clamp.acm.participant.kubernetes.exception.ServiceException;
 import org.onap.policy.clamp.acm.participant.kubernetes.helm.HelmClient;
 import org.onap.policy.clamp.acm.participant.kubernetes.models.ChartInfo;
 import org.onap.policy.clamp.acm.participant.kubernetes.models.HelmRepository;
+import org.onap.policy.clamp.acm.participant.kubernetes.parameters.HelmRepositoryConfig;
+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.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.web.multipart.MultipartFile;
 
 @Service
-@NoArgsConstructor
+@RequiredArgsConstructor
 public class ChartService {
     private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
-    private ChartStore chartStore;
+    private final ChartStore chartStore;
 
-    private HelmClient helmClient;
+    private final HelmClient helmClient;
 
-    private HelmRepositoryConfig helmRepositoryConfig;
+    private final HelmRepositoryConfig helmRepositoryConfig;
 
-    /**
-     * Instantiates the ChartService with the specified dependencies.
-     *
-     * @param chartStore the store for managing Helm chart information
-     * @param helmClient the client for interacting with Helm
-     * @param helmRepositoryConfig the configuration for Helm repositories
-     */
-    @Autowired
-    public ChartService(ChartStore chartStore, HelmClient helmClient, HelmRepositoryConfig helmRepositoryConfig) {
-        this.chartStore = chartStore;
-        this.helmClient = helmClient;
-        this.helmRepositoryConfig = helmRepositoryConfig;
-    }
+    private static final Coder coder = new StandardCoder();
 
     /**
      * Get all the installed charts.
@@ -98,6 +90,8 @@ public class ChartService {
         chartStore.deleteChart(chart);
     }
 
+    private static class Repositories extends ArrayList<HelmRepository> {}
+
     /**
      * Install a helm chart.
      * @param chart name and version.
@@ -108,22 +102,28 @@ public class ChartService {
     public boolean installChart(ChartInfo chart) throws ServiceException, IOException {
         boolean permittedRepo = false;
         if (chart.getRepository() == null) {
-            String repoName = findChartRepo(chart);
+            var repoName = findChartRepo(chart);
             if (repoName == null) {
                 logger.error("Chart repository could not be found. Skipping chart Installation "
                     + "for the chart {} ", chart.getChartId().getName());
                 return false;
             } else {
-                HelmRepository repo = HelmRepository.builder().repoName(repoName).build();
+                var repo = HelmRepository.builder().repoName(repoName).build();
                 chart.setRepository(repo);
             }
         } else {
             // Add a remote repository if passed via TOSCA
             // and check whether the repo is permitted
-            for (HelmRepository repo : helmRepositoryConfig.getRepos()) {
+            Repositories repos = null;
+            try {
+                repos = coder.decode(helmRepositoryConfig.getRepos(), Repositories.class);
+            } catch (CoderException e) {
+                throw new ServiceException(e.getMessage());
+            }
+            for (var repo : repos) {
+                var protocols = Arrays.stream(helmRepositoryConfig.getProtocols().split(",")).toList();
                 if (repo.getAddress().equals(chart.getRepository().getAddress())
-                        && helmRepositoryConfig.getProtocols()
-                    .contains(chart.getRepository().getAddress().split(":")[0])) {
+                        && protocols.contains(chart.getRepository().getAddress().split(":")[0])) {
                     configureRepository(chart.getRepository());
                     permittedRepo = true;
                     break;
index 60d4796..a03a626 100644 (file)
@@ -130,17 +130,8 @@ chart:
     enabled: false
 # Update the config here for permitting repositories and protocols
 helm:
-  repos:
-    -
-      repoName: kong
-      address: https://charts.konghq.com
-    -
-      repoName: bitnami
-      address: https://charts.bitnami.com/bitnami
-
-  protocols:
-    - http
-    - https
+  repos: [{"repoName": "kong", "address": "https://charts.konghq.com"},{"repoName": "bitnami", "address": "https://charts.bitnami.com/bitnami"}]
+  protocols: http,https
 
 tracing:
   enabled: ${allowTracing:false}
index d6f5142..a6394d4 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- *  Copyright (C) 2021-2024 Nordix Foundation.
+ *  Copyright (C) 2021-2024,2026 OpenInfra Foundation Europe. All rights reserved.
  *  Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,12 +24,9 @@ package org.onap.policy.clamp.acm.participant.kubernetes.handler;
 import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
 
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.ObjectMapper;
@@ -40,10 +37,10 @@ import java.util.Map;
 import java.util.UUID;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
-import org.mockito.Mockito;
 import org.onap.policy.clamp.acm.participant.intermediary.api.CompositionDto;
 import org.onap.policy.clamp.acm.participant.intermediary.api.ParticipantIntermediaryApi;
 import org.onap.policy.clamp.acm.participant.kubernetes.exception.ServiceException;
+import org.onap.policy.clamp.acm.participant.kubernetes.helm.PodStatusValidator;
 import org.onap.policy.clamp.acm.participant.kubernetes.models.ChartInfo;
 import org.onap.policy.clamp.acm.participant.kubernetes.models.ChartList;
 import org.onap.policy.clamp.acm.participant.kubernetes.parameters.CommonTestData;
@@ -65,8 +62,6 @@ class AutomationCompositionElementHandlerTest {
             "org.onap.domain.database.PMSH_K8SMicroserviceAutomationCompositionElement";
     private final CommonTestData commonTestData = new CommonTestData();
 
-
-
     @BeforeAll
     static void init() throws CoderException {
         charts = CODER.decode(new File(CHART_INFO_YAML), ChartList.class).getCharts();
@@ -75,137 +70,111 @@ class AutomationCompositionElementHandlerTest {
 
     @Test
     void test_AutomationCompositionElementStateChange() throws ServiceException, PfModelException {
-        var chartService = Mockito.mock(ChartService.class);
-        var automationCompositionElementHandler =
-                new AutomationCompositionElementHandler(mock(ParticipantIntermediaryApi.class), chartService);
+        var chartService = mock(ChartService.class);
+        var acElementHandler = new AutomationCompositionElementHandler(
+                mock(ParticipantIntermediaryApi.class), chartService, mock(PodStatusValidator.class));
 
-        doNothing().when(chartService).uninstallChart(charts.get(0));
-
-        ObjectMapper objectMapper = new ObjectMapper();
+        var objectMapper = new ObjectMapper();
         Map<String, Object> inPropertiesMap = objectMapper.convertValue(charts.get(0), new TypeReference<>() {});
 
-        automationCompositionElementHandler.undeploy(commonTestData.createCompositionElementDto(),
+        acElementHandler.undeploy(commonTestData.createCompositionElementDto(),
                 commonTestData.createInstanceElementDto(Map.of("chart", inPropertiesMap)));
 
         doThrow(new ServiceException("Error uninstalling the chart")).when(chartService).uninstallChart(charts.get(0));
 
-        assertDoesNotThrow(() -> automationCompositionElementHandler
-                .undeploy(commonTestData.createCompositionElementDto(),
+        assertDoesNotThrow(() -> acElementHandler.undeploy(commonTestData.createCompositionElementDto(),
                         commonTestData.createInstanceElementDto(inPropertiesMap)));
     }
 
     @Test
     void test_AutomationCompositionElementUpdate()
-            throws PfModelException, IOException, ServiceException, InterruptedException {
-        var chartService = Mockito.mock(ChartService.class);
-        var automationCompositionElementHandler =
-                spy(new AutomationCompositionElementHandler(mock(ParticipantIntermediaryApi.class), chartService));
+            throws PfModelException, IOException, ServiceException {
+        var chartService = mock(ChartService.class);
+        var acElementHandler = new AutomationCompositionElementHandler(mock(ParticipantIntermediaryApi.class),
+                chartService, mock(PodStatusValidator.class));
 
-        doNothing().when(automationCompositionElementHandler).checkPodStatus(any(), any(), any(), anyInt(), anyInt(),
-                any());
         var nodeTemplatesMap = toscaServiceTemplate.getToscaTopologyTemplate().getNodeTemplates();
         var instanceElementDto = commonTestData.createInstanceElementDto(nodeTemplatesMap
                 .get(K8S_AUTOMATION_COMPOSITION_ELEMENT).getProperties());
         var compositionElementDto = commonTestData.createCompositionElementDto();
 
         doReturn(false).when(chartService).installChart(any());
-        assertThrows(PfModelException.class, () -> automationCompositionElementHandler.deploy(compositionElementDto,
-                instanceElementDto));
+        assertThrows(PfModelException.class, () -> acElementHandler.deploy(compositionElementDto, instanceElementDto));
 
         doReturn(true).when(chartService).installChart(any());
-        automationCompositionElementHandler.deploy(compositionElementDto, instanceElementDto);
-
-        doThrow(new ServiceException("Error installing the chart")).when(chartService).installChart(Mockito.any());
+        acElementHandler.deploy(compositionElementDto, instanceElementDto);
 
-        assertThrows(PfModelException.class,
-                () -> automationCompositionElementHandler.deploy(compositionElementDto, instanceElementDto));
+        doThrow(new ServiceException("Error installing the chart")).when(chartService).installChart(any());
 
-    }
-
-    @Test
-    void test_checkPodStatus() {
-        var chartService = Mockito.mock(ChartService.class);
-        var automationCompositionElementHandler =
-                new AutomationCompositionElementHandler(mock(ParticipantIntermediaryApi.class), chartService);
-
-        var chartInfo = charts.get(0);
-        var automationCompositionId = UUID.randomUUID();
-        assertThrows(PfModelException.class, () -> automationCompositionElementHandler
-                .checkPodStatus(automationCompositionId, UUID.randomUUID(), chartInfo, 1, 1,
-                        commonTestData.createInstanceElementDto(Map.of())));
+        assertThrows(PfModelException.class, () -> acElementHandler.deploy(compositionElementDto, instanceElementDto));
     }
 
     @Test
     void testUpdate() {
-        var chartService = Mockito.mock(ChartService.class);
-        var automationCompositionElementHandler =
-                new AutomationCompositionElementHandler(mock(ParticipantIntermediaryApi.class), chartService);
+        var chartService = mock(ChartService.class);
+        var acElementHandler = new AutomationCompositionElementHandler(
+                mock(ParticipantIntermediaryApi.class), chartService, mock(PodStatusValidator.class));
         assertDoesNotThrow(
-                () -> automationCompositionElementHandler.update(commonTestData.createCompositionElementDto(),
+                () -> acElementHandler.update(commonTestData.createCompositionElementDto(),
                         commonTestData.createInstanceElementDto(Map.of()),
                         commonTestData.createInstanceElementDto(Map.of())));
     }
 
     @Test
     void testLock() {
-        var chartService = Mockito.mock(ChartService.class);
-        var automationCompositionElementHandler =
-                new AutomationCompositionElementHandler(mock(ParticipantIntermediaryApi.class), chartService);
+        var chartService = mock(ChartService.class);
+        var acElementHandler = new AutomationCompositionElementHandler(
+                mock(ParticipantIntermediaryApi.class), chartService, mock(PodStatusValidator.class));
 
-        assertDoesNotThrow(() -> automationCompositionElementHandler.lock(commonTestData.createCompositionElementDto(),
+        assertDoesNotThrow(() -> acElementHandler.lock(commonTestData.createCompositionElementDto(),
                 commonTestData.createInstanceElementDto(Map.of())));
     }
 
     @Test
     void testUnlock() {
-        var chartService = Mockito.mock(ChartService.class);
-        var automationCompositionElementHandler =
-                new AutomationCompositionElementHandler(mock(ParticipantIntermediaryApi.class), chartService);
+        var chartService = mock(ChartService.class);
+        var acElementHandler = new AutomationCompositionElementHandler(
+                mock(ParticipantIntermediaryApi.class), chartService, mock(PodStatusValidator.class));
 
-        assertDoesNotThrow(() -> automationCompositionElementHandler
-                .unlock(commonTestData.createCompositionElementDto(),
+        assertDoesNotThrow(() -> acElementHandler.unlock(commonTestData.createCompositionElementDto(),
                         commonTestData.createInstanceElementDto(Map.of())));
     }
 
     @Test
     void testDelete() {
-        var chartService = Mockito.mock(ChartService.class);
-        var automationCompositionElementHandler =
-                new AutomationCompositionElementHandler(mock(ParticipantIntermediaryApi.class), chartService);
+        var chartService = mock(ChartService.class);
+        var acElementHandler = new AutomationCompositionElementHandler(
+                mock(ParticipantIntermediaryApi.class), chartService, mock(PodStatusValidator.class));
 
-        assertDoesNotThrow(() -> automationCompositionElementHandler
-                .delete(commonTestData.createCompositionElementDto(),
+        assertDoesNotThrow(() -> acElementHandler.delete(commonTestData.createCompositionElementDto(),
                 commonTestData.createInstanceElementDto(Map.of())));
     }
 
     @Test
     void testPrime() {
-        var chartService = Mockito.mock(ChartService.class);
-        var automationCompositionElementHandler =
-                new AutomationCompositionElementHandler(mock(ParticipantIntermediaryApi.class), chartService);
+        var chartService = mock(ChartService.class);
+        var acElementHandler = new AutomationCompositionElementHandler(
+                mock(ParticipantIntermediaryApi.class), chartService, mock(PodStatusValidator.class));
 
-        assertDoesNotThrow(() -> automationCompositionElementHandler.prime(new CompositionDto(UUID.randomUUID(),
-                Map.of(), Map.of())));
+        assertDoesNotThrow(() -> acElementHandler.prime(new CompositionDto(UUID.randomUUID(), Map.of(), Map.of())));
     }
 
     @Test
     void testDeprime() {
-        var chartService = Mockito.mock(ChartService.class);
-        var automationCompositionElementHandler =
-                new AutomationCompositionElementHandler(mock(ParticipantIntermediaryApi.class), chartService);
+        var chartService = mock(ChartService.class);
+        var acElementHandler = new AutomationCompositionElementHandler(
+                mock(ParticipantIntermediaryApi.class), chartService, mock(PodStatusValidator.class));
 
-        assertDoesNotThrow(() -> automationCompositionElementHandler.deprime(new CompositionDto(UUID.randomUUID(),
-                Map.of(), Map.of())));
+        assertDoesNotThrow(() -> acElementHandler.deprime(new CompositionDto(UUID.randomUUID(), Map.of(), Map.of())));
     }
 
     @Test
     void testMigrate() {
-        var chartService = Mockito.mock(ChartService.class);
-        var automationCompositionElementHandler =
-                new AutomationCompositionElementHandler(mock(ParticipantIntermediaryApi.class), chartService);
+        var chartService = mock(ChartService.class);
+        var acElementHandler = new AutomationCompositionElementHandler(
+                mock(ParticipantIntermediaryApi.class), chartService, mock(PodStatusValidator.class));
 
-        assertDoesNotThrow(() -> automationCompositionElementHandler
-                .migrate(commonTestData.createCompositionElementDto(),
+        assertDoesNotThrow(() -> acElementHandler.migrate(commonTestData.createCompositionElementDto(),
                 commonTestData.createCompositionElementDto(), commonTestData.createInstanceElementDto(Map.of()),
                 commonTestData.createInstanceElementDto(Map.of()), 0));
     }
index 77b3ec5..176b65a 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- *  Copyright (C) 2021,2025 OpenInfra Foundation Europe. All rights reserved.
+ *  Copyright (C) 2021,2025-2026 OpenInfra Foundation Europe. All rights reserved.
  *  Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -30,7 +30,8 @@ import static org.junit.jupiter.api.Assertions.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
 
 import java.io.File;
 import java.io.IOException;
@@ -39,10 +40,6 @@ import java.util.List;
 import org.junit.jupiter.api.AfterAll;
 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.acm.participant.kubernetes.exception.ServiceException;
 import org.onap.policy.clamp.acm.participant.kubernetes.models.ChartInfo;
 import org.onap.policy.clamp.acm.participant.kubernetes.models.ChartList;
@@ -51,28 +48,15 @@ import org.onap.policy.clamp.acm.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;
 import org.springframework.util.FileSystemUtils;
 
 
-@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;
-
-    @Mock
-    HelmRepository repo;
-
-
     @BeforeAll
     static void init() throws CoderException {
         charts = CODER.decode(new File(CHART_INFO_YAML), ChartList.class).getCharts();
@@ -85,6 +69,8 @@ class HelmClientTest {
 
     @Test
     void test_installChart() throws ServiceException {
+        var chartStore = mock(ChartStore.class);
+        var helmClient = spy(new HelmClient(chartStore));
         doReturn("success").when(helmClient).executeCommand(any());
         doReturn(new File("/target/tmp/override.yaml")).when(chartStore)
             .getOverrideFile(any());
@@ -96,14 +82,17 @@ class HelmClientTest {
 
         doReturn("").when(helmClient).executeCommand(any());
         assertDoesNotThrow(() -> helmClient.installChart(chartinfo));
-
     }
 
     @Test
     void test_addRepository() throws ServiceException {
+        var chartStore = mock(ChartStore.class);
+        var helmClient = spy(new HelmClient(chartStore));
         doReturn("").when(helmClient).executeCommand(any());
-        when(repo.getRepoName()).thenReturn("RepoName");
-        when(repo.getAddress()).thenReturn("http://localhost:8080");
+
+        var repo = new HelmRepository();
+        repo.setRepoName("RepoName");
+        repo.setAddress("http://localhost:8080");
         assertDoesNotThrow(() -> helmClient.addRepository(repo));
 
         doReturn("failed").when(helmClient).executeCommand(any());
@@ -112,6 +101,8 @@ class HelmClientTest {
 
     @Test
     void test_findChartRepository() throws IOException, ServiceException {
+        var chartStore = mock(ChartStore.class);
+        var helmClient = spy(new HelmClient(chartStore));
         String tmpPath = "target/tmp/dummyChart/1.0/";
         doReturn("nginx-stable/nginx-ingress\t0.9.3\t1.11.3"
                 + " \tNGINX Ingress Controller").when(helmClient).executeCommand(any());
@@ -135,6 +126,8 @@ class HelmClientTest {
 
     @Test
     void test_uninstallChart() throws ServiceException {
+        var chartStore = mock(ChartStore.class);
+        var helmClient = spy(new HelmClient(chartStore));
         doReturn("success").when(helmClient).executeCommand(any());
         helmClient.uninstallChart(charts.get(0));
         doThrow(ServiceException.class).when(helmClient).executeCommand(any());
@@ -145,9 +138,10 @@ class HelmClientTest {
 
     @Test
     void test_verifyConfiguredRepoForInvalidChart() throws ServiceException {
+        var chartStore = mock(ChartStore.class);
+        var helmClient = spy(new HelmClient(chartStore));
         doReturn("").when(helmClient).executeCommand(any());
         String configuredRepo = helmClient.verifyConfiguredRepo(charts.get(1));
         assertNull(configuredRepo);
     }
-
 }
index c925385..7d04d0f 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- *  Copyright (C) 2021-2022,2024 Nordix Foundation.
+ *  Copyright (C) 2021-2022,2024,2026 OpenInfra Foundation Europe. All rights reserved.
  *  Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -25,14 +25,11 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 
 import java.io.File;
-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.onap.policy.clamp.acm.participant.kubernetes.exception.ServiceException;
 import org.onap.policy.clamp.acm.participant.kubernetes.models.ChartInfo;
 import org.onap.policy.clamp.acm.participant.kubernetes.models.ChartList;
@@ -40,54 +37,49 @@ 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.springframework.test.context.junit.jupiter.SpringExtension;
 
-@ExtendWith(SpringExtension.class)
 class PodStatusValidatorTest {
 
     private static final Coder CODER = new StandardCoder();
     private static final String CHART_INFO_YAML = "src/test/resources/ChartList.json";
     private static final int TIMEOUT = 2;
     private static final int STATUS_CHECK_INTERVAL = 1;
-    private static List<ChartInfo> charts;
-
-    @InjectMocks
-    private PodStatusValidator podStatusValidator = new PodStatusValidator(charts.get(0), TIMEOUT,
-            STATUS_CHECK_INTERVAL);
-
-    @InjectMocks
-    private PodStatusValidator podValidatorWithPodName = new PodStatusValidator(charts.get(2), TIMEOUT,
-            STATUS_CHECK_INTERVAL);
-
-
-    @Mock
-    private HelmClient client;
+    private static ChartInfo chart0;
+    private static ChartInfo chart2;
 
     @BeforeAll
     static void init() throws CoderException {
-        charts = CODER.decode(new File(CHART_INFO_YAML), ChartList.class).getCharts();
+        var charts = CODER.decode(new File(CHART_INFO_YAML), ChartList.class).getCharts();
+        chart0 = charts.get(0);
+        chart2 = charts.get(2);
     }
 
-
     @Test
     void test_RunningPodState() throws ServiceException {
+        var client = mock(HelmClient.class);
+        var podStatusValidator = new PodStatusValidator(client);
         String runningPod = "NAME\tREADY\tSTATUS\tRESTARTS\tAGE\r\nHelloWorld-54777df9f8-qpzqr\t1/1\tRunning\t0\t9h";
         doReturn(runningPod).when(client).executeCommand(any());
-        assertDoesNotThrow(() -> podStatusValidator.run());
+        assertDoesNotThrow(() -> podStatusValidator.run(TIMEOUT, STATUS_CHECK_INTERVAL, chart0));
     }
 
     @Test
     void test_InvalidPodState() throws ServiceException {
+        var client = mock(HelmClient.class);
+        var podStatusValidator = new PodStatusValidator(client);
         String invalidPod = "NAME\tREADY\tSTATUS\tRESTARTS\tAGE\nhellofromdocker-54777df9f8-qpzqr\t1/1\tInit\t0\t9h";
         doReturn(invalidPod).when(client).executeCommand(any());
-        assertThrows(PfModelException.class, () -> podStatusValidator.run());
+        assertThrows(PfModelException.class,
+                () -> podStatusValidator.run(TIMEOUT, STATUS_CHECK_INTERVAL, chart0));
     }
 
     // Use case scenario: Hard coded pod name
     @Test
     void test_RunningPodStateWithPodName() throws ServiceException {
+        var client = mock(HelmClient.class);
+        var podStatusValidator = new PodStatusValidator(client);
         String runningPod = "NAME\tREADY\tSTATUS\tRESTARTS\tAGE\r\nhelloallworld-54777df9f8-qpzqr\t1/1\tRunning\t0\t9h";
         doReturn(runningPod).when(client).executeCommand(any());
-        assertDoesNotThrow(() -> podValidatorWithPodName.run());
+        assertDoesNotThrow(() -> podStatusValidator.run(TIMEOUT, STATUS_CHECK_INTERVAL, chart2));
     }
 }
index 669ca3f..7e04653 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- *  Copyright (C) 2021-2022 Nordix Foundation.
+ *  Copyright (C) 2021-2022,2026 OpenInfra Foundation Europe. All rights reserved.
  *  Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,54 +27,35 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 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 static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 
 import java.io.File;
 import java.io.IOException;
-import java.util.ArrayList;
-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.acm.participant.kubernetes.configurations.HelmRepositoryConfig;
 import org.onap.policy.clamp.acm.participant.kubernetes.exception.ServiceException;
 import org.onap.policy.clamp.acm.participant.kubernetes.helm.HelmClient;
 import org.onap.policy.clamp.acm.participant.kubernetes.models.ChartInfo;
 import org.onap.policy.clamp.acm.participant.kubernetes.models.ChartList;
 import org.onap.policy.clamp.acm.participant.kubernetes.models.HelmRepository;
+import org.onap.policy.clamp.acm.participant.kubernetes.parameters.HelmRepositoryConfig;
 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;
-
-    @Mock
-    private HelmRepositoryConfig helmRepositoryConfig;
-
     @BeforeAll
     static void init() throws CoderException {
         charts = CODER.decode(new File(CHART_INFO_YAML), ChartList.class).getCharts();
@@ -82,77 +63,127 @@ class ChartServiceTest {
 
     @Test
     void test_getAllCharts() {
-        assertThat(chartService.getAllCharts()).isEmpty();
+        var chartStore = mock(ChartStore.class);
+        var helmClient = mock(HelmClient.class);
+        var chartService = new ChartService(chartStore, helmClient, new HelmRepositoryConfig());
 
+        assertThat(chartService.getAllCharts()).isEmpty();
         doReturn(charts).when(chartStore).getAllCharts();
-        Collection<ChartInfo> result = chartService.getAllCharts();
+        var result = chartService.getAllCharts();
         assertNotNull(result);
         assertThat(result).containsAll(charts);
     }
 
     @Test
     void test_getChart() {
+        var chartStore = mock(ChartStore.class);
+        var helmClient = mock(HelmClient.class);
+        var chartService = new ChartService(chartStore, helmClient, new HelmRepositoryConfig());
         assertNull(chartService.getChart("dummyName", "dummyversion"));
 
-        doReturn(charts.get(0)).when(chartStore).getChart(any(), any());
-        ChartInfo chart = chartService.getChart(charts.get(0).getChartId().getName(),
-            charts.get(0).getChartId().getVersion());
+        var chart0 = charts.get(0);
+        doReturn(chart0).when(chartStore).getChart(any(), any());
+        var chart = chartService.getChart(chart0.getChartId().getName(), chart0.getChartId().getVersion());
         assertNotNull(chart);
-        assertThat(chart.getNamespace()).isEqualTo(charts.get(0).getNamespace());
+        assertThat(chart.getNamespace()).isEqualTo(chart0.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))
+        var chart0 = charts.get(0);
+        var chartStore = mock(ChartStore.class);
+        doThrow(IOException.class).when(chartStore).saveChart(chart0, null, null);
+        var helmClient = mock(HelmClient.class);
+        var chartService = new ChartService(chartStore, helmClient, new HelmRepositoryConfig());
+        assertThatThrownBy(() -> chartService.saveChart(chart0, null, null))
             .isInstanceOf(IOException.class);
 
-        MockMultipartFile mockChartFile = new MockMultipartFile("chart", "dummy".getBytes());
-        MockMultipartFile mockOverrideFile = new MockMultipartFile("override", "dummy".getBytes());
+        var mockChartFile = new MockMultipartFile("chart", "dummy".getBytes());
+        var mockOverrideFile = new MockMultipartFile("override", "dummy".getBytes());
 
-        doReturn(charts.get(0)).when(chartStore).saveChart(any(), any(), any());
+        doReturn(chart0).when(chartStore).saveChart(any(), any(), any());
 
-        ChartInfo chart = chartService.saveChart(charts.get(0), mockChartFile, mockOverrideFile);
+        var chart = chartService.saveChart(chart0, mockChartFile, mockOverrideFile);
         assertNotNull(chart);
-        assertThat(chart.getChartId().getName()).isEqualTo(charts.get(0).getChartId().getName());
+        assertThat(chart.getChartId().getName()).isEqualTo(chart0.getChartId().getName());
+    }
 
+    @Test
+    void test_FailInstallChart() throws IOException, ServiceException {
+        var helmRepositoryConfig = new HelmRepositoryConfig();
+        helmRepositoryConfig.setRepos("[]");
+        helmRepositoryConfig.setProtocols("http,https");
+        var chartStore = mock(ChartStore.class);
+        var helmClient = mock(HelmClient.class);
+        var chartService = new ChartService(chartStore, helmClient, helmRepositoryConfig);
+        doReturn("dummyRepoName").when(helmClient).findChartRepository(any());
+        var testChart = charts.get(1);
+        var result = chartService.installChart(testChart);
+        assertTrue(result);
+    }
+
+    @Test
+    void test_FailEncodeInstallChart() {
+        var helmRepositoryConfig = new HelmRepositoryConfig();
+        helmRepositoryConfig.setRepos("[");
+        helmRepositoryConfig.setProtocols("http,https");
+        var chartStore = mock(ChartStore.class);
+        var helmClient = mock(HelmClient.class);
+        var chartService = new ChartService(chartStore, helmClient, helmRepositoryConfig);
+        var chart0 = charts.get(0);
+        assertThatThrownBy(() -> chartService.installChart(chart0)).isInstanceOf(ServiceException.class);
     }
 
     @Test
-    void test_installChart() throws IOException, ServiceException {
-        List<HelmRepository> helmRepositoryList = new ArrayList<>();
-        helmRepositoryList.add(HelmRepository.builder().address("https://localhost:8080").build());
-        doReturn(helmRepositoryList).when(helmRepositoryConfig).getRepos();
-        doReturn(List.of("http", "https")).when(helmRepositoryConfig).getProtocols();
-        assertDoesNotThrow(() -> chartService.installChart(charts.get(0)));
+    void test_installChart() throws IOException, ServiceException, CoderException {
+        var helmRepositoryConfig = new HelmRepositoryConfig();
+        var repos = List.of(HelmRepository.builder().address("https://localhost:8080").build());
+        helmRepositoryConfig.setRepos(CODER.encode(repos));
+        helmRepositoryConfig.setProtocols("http,https");
+        var chart0 = charts.get(0);
+        var chartStore = mock(ChartStore.class);
+        var helmClient = mock(HelmClient.class);
+        var chartService = new ChartService(chartStore, helmClient, helmRepositoryConfig);
+        assertDoesNotThrow(() -> chartService.installChart(chart0));
+
         doThrow(ServiceException.class).when(helmClient).installChart(any());
-        assertThatThrownBy(() -> chartService.installChart(charts.get(0))).isInstanceOf(ServiceException.class);
+        assertThatThrownBy(() -> chartService.installChart(chart0)).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().getRepoName());
+        doReturn("dummyRepoName").when(helmClient).findChartRepository(any());
+        var testChart = charts.get(1);
+        chartService.installChart(testChart);
+        assertEquals("dummyRepoName", testChart.getRepository().getRepoName());
 
-        ChartInfo testChart = charts.get(1);
         testChart.setRepository(null);
-        doReturn(null).when(chartService).findChartRepo(any());
-        chartService.installChart(charts.get(1));
+        doReturn(null).when(helmClient).findChartRepository(any());
+        chartService.installChart(testChart);
+
+        chartService.deleteChart(testChart);
+        verify(chartStore).deleteChart(testChart);
     }
 
     @Test
     void test_UninstallChart() throws ServiceException {
-        assertDoesNotThrow(() -> chartService.uninstallChart(charts.get(0)));
+        var chart0 = charts.get(0);
+        var chartStore = mock(ChartStore.class);
+        var helmClient = mock(HelmClient.class);
+        var chartService = new ChartService(chartStore, helmClient, new HelmRepositoryConfig());
+        assertDoesNotThrow(() -> chartService.uninstallChart(chart0));
         doThrow(ServiceException.class).when(helmClient).uninstallChart(any());
-        assertThatThrownBy(() -> chartService.uninstallChart(charts.get(0))).isInstanceOf(ServiceException.class);
+        assertThatThrownBy(() -> chartService.uninstallChart(chart0)).isInstanceOf(ServiceException.class);
     }
 
     @Test
     void test_findChartRepo() throws IOException, ServiceException {
-        assertDoesNotThrow(() -> chartService.findChartRepo(charts.get(0)));
+        var chart0 = charts.get(0);
+        var chartStore = mock(ChartStore.class);
+        var helmClient = mock(HelmClient.class);
+        var chartService = new ChartService(chartStore, helmClient, new HelmRepositoryConfig());
+        assertDoesNotThrow(() -> chartService.findChartRepo(chart0));
         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);
+        assertThatThrownBy(() -> chartService.findChartRepo(chart0)).isInstanceOf(ServiceException.class);
     }
 }