From 0f1824d8e298f1581ebaba4de75b80467ef81552 Mon Sep 17 00:00:00 2001 From: Prudhvi Godithi Date: Tue, 29 Oct 2024 10:00:19 -0700 Subject: [PATCH] Onboard repo code coverage metrics Signed-off-by: Prudhvi Godithi --- .github/workflows/cdk-ci-test.yml | 6 + build.gradle | 2 + .../dagger/MetricsModule.java | 16 +- .../lambda/MetricsLambda.java | 10 + .../metrics/MetricsCalculation.java | 50 ++++ .../metrics/release/CodeCoverage.java | 91 +++++++ .../metrics/release/ReleaseMetrics.java | 22 +- .../ReleaseVersionIncrementChecker.java | 10 + .../model/codecov/CodeCovResponse.java | 41 +++ .../model/codecov/CodeCovResult.java | 89 +++++++ .../model/release/ReleaseMetricsData.java | 11 +- .../lambda/MetricsLambdaTest.java | 19 +- .../metrics/MetricsCalculationTest.java | 46 ++++ .../metrics/release/CodeCoverageTest.java | 148 +++++++++++ .../metrics/release/ReleaseMetricsTest.java | 93 ++++--- .../ReleaseVersionIncrementCheckerTest.java | 30 +-- .../codecov/CodeCovDoubleSerializerTest.java | 58 +++++ .../model/codecov/CodeCovResponseTest.java | 75 ++++++ .../model/codecov/CodeCovResultTest.java | 233 ++++++++++++++++++ 19 files changed, 988 insertions(+), 62 deletions(-) create mode 100644 src/main/java/org/opensearchmetrics/metrics/release/CodeCoverage.java create mode 100644 src/main/java/org/opensearchmetrics/model/codecov/CodeCovResponse.java create mode 100644 src/main/java/org/opensearchmetrics/model/codecov/CodeCovResult.java create mode 100644 src/test/java/org/opensearchmetrics/metrics/release/CodeCoverageTest.java create mode 100644 src/test/java/org/opensearchmetrics/model/codecov/CodeCovDoubleSerializerTest.java create mode 100644 src/test/java/org/opensearchmetrics/model/codecov/CodeCovResponseTest.java create mode 100644 src/test/java/org/opensearchmetrics/model/codecov/CodeCovResultTest.java diff --git a/.github/workflows/cdk-ci-test.yml b/.github/workflows/cdk-ci-test.yml index 5bc2c62..969a8ee 100644 --- a/.github/workflows/cdk-ci-test.yml +++ b/.github/workflows/cdk-ci-test.yml @@ -14,6 +14,12 @@ jobs: with: node-version: '20.8.0' + - name: Setup Java 17 + uses: actions/setup-java@v4 + with: + distribution: 'corretto' + java-version: '17' + - name: Run CDK Test run: | ./gradlew clean build diff --git a/build.gradle b/build.gradle index 32da440..107b63b 100644 --- a/build.gradle +++ b/build.gradle @@ -36,6 +36,8 @@ dependencies { implementation 'software.amazon.awssdk:s3:2.28.17' + implementation 'org.json:json:20240303' + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' diff --git a/src/main/java/org/opensearchmetrics/dagger/MetricsModule.java b/src/main/java/org/opensearchmetrics/dagger/MetricsModule.java index 4078cc6..7e20fd7 100644 --- a/src/main/java/org/opensearchmetrics/dagger/MetricsModule.java +++ b/src/main/java/org/opensearchmetrics/dagger/MetricsModule.java @@ -1,3 +1,12 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + package org.opensearchmetrics.dagger; import com.fasterxml.jackson.databind.ObjectMapper; @@ -5,6 +14,7 @@ import dagger.Provides; import org.opensearchmetrics.metrics.general.*; import org.opensearchmetrics.metrics.label.LabelMetrics; +import org.opensearchmetrics.metrics.release.CodeCoverage; import org.opensearchmetrics.metrics.release.ReleaseBranchChecker; import org.opensearchmetrics.metrics.release.ReleaseIssueChecker; import org.opensearchmetrics.metrics.release.ReleaseLabelIssuesFetcher; @@ -144,7 +154,9 @@ public LabelMetrics getLabelMetrics() { public ReleaseMetrics getReleaseMetrics(OpenSearchUtil openSearchUtil, ObjectMapper objectMapper, ReleaseRepoFetcher releaseRepoFetcher, ReleaseLabelIssuesFetcher releaseLabelIssuesFetcher, ReleaseLabelPullsFetcher releaseLabelPullsFetcher, ReleaseVersionIncrementChecker releaseVersionIncrementChecker, - ReleaseBranchChecker releaseBranchChecker, ReleaseNotesChecker releaseNotesChecker, ReleaseIssueChecker releaseIssueChecker) { - return new ReleaseMetrics(openSearchUtil, objectMapper, releaseRepoFetcher, releaseLabelIssuesFetcher, releaseLabelPullsFetcher, releaseVersionIncrementChecker, releaseBranchChecker, releaseNotesChecker, releaseIssueChecker); + ReleaseBranchChecker releaseBranchChecker, ReleaseNotesChecker releaseNotesChecker, ReleaseIssueChecker releaseIssueChecker, CodeCoverage codeCoverage) { + return new ReleaseMetrics(openSearchUtil, objectMapper, releaseRepoFetcher, + releaseLabelIssuesFetcher, releaseLabelPullsFetcher, releaseVersionIncrementChecker, + releaseBranchChecker, releaseNotesChecker, releaseIssueChecker, codeCoverage); } } diff --git a/src/main/java/org/opensearchmetrics/lambda/MetricsLambda.java b/src/main/java/org/opensearchmetrics/lambda/MetricsLambda.java index 7a551ab..ad9e1a6 100644 --- a/src/main/java/org/opensearchmetrics/lambda/MetricsLambda.java +++ b/src/main/java/org/opensearchmetrics/lambda/MetricsLambda.java @@ -1,3 +1,12 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + package org.opensearchmetrics.lambda; import com.amazonaws.services.lambda.runtime.Context; @@ -56,6 +65,7 @@ public Void handleRequest(Void input, Context context) { metricsCalculation.generateGeneralMetrics(keys); metricsCalculation.generateLabelMetrics(keys); metricsCalculation.generateReleaseMetrics(); + metricsCalculation.generateCodeCovMetrics(); } catch (Exception e) { throw new RuntimeException("Error running Metrics Calculation", e); } diff --git a/src/main/java/org/opensearchmetrics/metrics/MetricsCalculation.java b/src/main/java/org/opensearchmetrics/metrics/MetricsCalculation.java index d0942ae..887a0bb 100644 --- a/src/main/java/org/opensearchmetrics/metrics/MetricsCalculation.java +++ b/src/main/java/org/opensearchmetrics/metrics/MetricsCalculation.java @@ -1,3 +1,12 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + package org.opensearchmetrics.metrics; import com.fasterxml.jackson.databind.ObjectMapper; @@ -6,8 +15,11 @@ import org.opensearch.index.query.BoolQueryBuilder; import org.opensearchmetrics.metrics.general.*; import org.opensearchmetrics.metrics.label.LabelMetrics; +import org.opensearchmetrics.metrics.release.CodeCoverage; import org.opensearchmetrics.metrics.release.ReleaseInputs; import org.opensearchmetrics.metrics.release.ReleaseMetrics; +import org.opensearchmetrics.model.codecov.CodeCovResponse; +import org.opensearchmetrics.model.codecov.CodeCovResult; import org.opensearchmetrics.model.label.LabelData; import org.opensearchmetrics.model.general.MetricsData; import org.opensearchmetrics.model.release.ReleaseMetricsData; @@ -199,4 +211,42 @@ public void generateReleaseMetrics() { openSearchUtil.bulkIndex("opensearch_release_metrics", metricFinalData); } + public void generateCodeCovMetrics() { + ReleaseInputs[] releaseInputs = ReleaseInputs.getAllReleaseInputs(); + Map metricFinalData = + Arrays.stream(releaseInputs) + .filter(ReleaseInputs::getTrack) + .flatMap(releaseInput -> releaseMetrics.getReleaseRepos(releaseInput.getVersion()).entrySet().stream() + .flatMap(entry -> { + String repoName = entry.getKey(); + String componentName = entry.getValue(); + CodeCovResult codeCovResult = new CodeCovResult(); + codeCovResult.setRepository(repoName); + codeCovResult.setComponent(componentName); + codeCovResult.setCurrentDate(currentDate.toString()); + try { + codeCovResult.setId(String.valueOf(UUID.nameUUIDFromBytes(MessageDigest.getInstance("SHA-1") + .digest(("codecov-metrics-" + releaseInput.getBranch() + releaseInput.getVersion() + "-" + currentDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) + "-" + repoName) + .getBytes())))); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + codeCovResult.setReleaseVersion(releaseInput.getVersion()); + codeCovResult.setVersion(releaseInput.getVersion()); + codeCovResult.setReleaseState(releaseInput.getState()); + codeCovResult.setBranch(releaseInput.getBranch()); + CodeCovResponse codeCovResponse = releaseMetrics.getCodeCoverage(releaseInput.getBranch(), repoName); + codeCovResult.setCommitid(codeCovResponse.getCommitid()); + codeCovResult.setState(codeCovResponse.getState()); + codeCovResult.setCoverage(codeCovResponse.getCoverage()); + codeCovResult.setUrl(codeCovResponse.getUrl()); + return Stream.of(codeCovResult); + })) + .collect(Collectors.toMap(CodeCovResult::getId, + codeCovResult -> codeCovResult.getJson(codeCovResult, objectMapper))); + String codeCovIndexName = "opensearch-codecov-metrics-" + currentDate.format(DateTimeFormatter.ofPattern("MM-yyyy")); + openSearchUtil.createIndexIfNotExists(codeCovIndexName); + openSearchUtil.bulkIndex(codeCovIndexName, metricFinalData); + } + } diff --git a/src/main/java/org/opensearchmetrics/metrics/release/CodeCoverage.java b/src/main/java/org/opensearchmetrics/metrics/release/CodeCoverage.java new file mode 100644 index 0000000..cfb8876 --- /dev/null +++ b/src/main/java/org/opensearchmetrics/metrics/release/CodeCoverage.java @@ -0,0 +1,91 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearchmetrics.metrics.release; + +import com.google.common.annotations.VisibleForTesting; +import org.apache.http.util.EntityUtils; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.json.JSONArray; +import org.json.JSONObject; +import org.opensearchmetrics.model.codecov.CodeCovResponse; + +import javax.inject.Inject; +import java.io.IOException; +import java.util.Optional; + +public class CodeCoverage { + private final CloseableHttpClient httpClient; + + @Inject + public CodeCoverage() { + this(HttpClients.createDefault()); + } + + @VisibleForTesting + CodeCoverage(CloseableHttpClient httpClient) { + this.httpClient = httpClient; + } + + public CodeCovResponse coverage(String branch, String repo) { + String codeCovRepoURL = String.format("https://api.codecov.io/api/v2/github/opensearch-project/repos/%s/commits?branch=%s", repo, branch); + System.out.println("The codeCovRepoURL is " + codeCovRepoURL); + CodeCovResponse codeCovResponse = new CodeCovResponse(); + codeCovResponse.setUrl(codeCovRepoURL); + HttpGet request = new HttpGet(codeCovRepoURL); + CloseableHttpResponse response; + try { + response = httpClient.execute(request); + } catch (IOException e) { + throw new RuntimeException(e); + } + try { + if (response.getStatusLine().getStatusCode() == 200) { + String codeCovApiResult; + try { + codeCovApiResult = EntityUtils.toString(response.getEntity()); + } catch (IOException e) { + throw new RuntimeException(e); + } + JSONObject jsonObject = new JSONObject(codeCovApiResult); + JSONArray resultsCoverage = jsonObject.getJSONArray("results"); + Optional firstResult = Optional.ofNullable(resultsCoverage) + .filter(results -> results.length() > 0) + .map(results -> results.getJSONObject(0)); + firstResult.ifPresentOrElse( + result -> { + codeCovResponse.setState(Optional.ofNullable(result.optString("state")) + .orElse("no-coverage")); + codeCovResponse.setCommitid(result.optString("commitid", "none")); + codeCovResponse.setCoverage( + Optional.ofNullable(result.optJSONObject("totals")) + .map(totals -> totals.optDouble("coverage", 0.0)) + .orElse(0.0) + ); + }, + () -> { + codeCovResponse.setState("no-coverage"); + codeCovResponse.setCommitid("none"); + codeCovResponse.setCoverage(0.0); + } + ); + } + } finally { + try { + response.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + return codeCovResponse; + } +} diff --git a/src/main/java/org/opensearchmetrics/metrics/release/ReleaseMetrics.java b/src/main/java/org/opensearchmetrics/metrics/release/ReleaseMetrics.java index 16d16c0..762821b 100644 --- a/src/main/java/org/opensearchmetrics/metrics/release/ReleaseMetrics.java +++ b/src/main/java/org/opensearchmetrics/metrics/release/ReleaseMetrics.java @@ -1,9 +1,22 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + package org.opensearchmetrics.metrics.release; import com.fasterxml.jackson.databind.ObjectMapper; +import org.opensearchmetrics.model.codecov.CodeCovResponse; +import org.opensearchmetrics.model.codecov.CodeCovResult; import org.opensearchmetrics.util.OpenSearchUtil; import javax.inject.Inject; +import java.io.IOException; +import java.nio.charset.CoderResult; import java.util.List; import java.util.Map; @@ -24,11 +37,13 @@ public class ReleaseMetrics { private final ReleaseIssueChecker releaseIssueChecker; + private final CodeCoverage codeCoverage; + @Inject public ReleaseMetrics(OpenSearchUtil openSearchUtil, ObjectMapper objectMapper, ReleaseRepoFetcher releaseRepoFetcher, ReleaseLabelIssuesFetcher releaseLabelIssuesFetcher, ReleaseLabelPullsFetcher releaseLabelPullsFetcher, ReleaseVersionIncrementChecker releaseVersionIncrementChecker, ReleaseBranchChecker releaseBranchChecker, - ReleaseNotesChecker releaseNotesChecker, ReleaseIssueChecker releaseIssueChecker) { + ReleaseNotesChecker releaseNotesChecker, ReleaseIssueChecker releaseIssueChecker, CodeCoverage codeCoverage) { this.openSearchUtil = openSearchUtil; this.objectMapper = objectMapper; this.releaseRepoFetcher = releaseRepoFetcher; @@ -38,6 +53,7 @@ public ReleaseMetrics(OpenSearchUtil openSearchUtil, ObjectMapper objectMapper, this.releaseBranchChecker = releaseBranchChecker; this.releaseNotesChecker = releaseNotesChecker; this.releaseIssueChecker = releaseIssueChecker; + this.codeCoverage = codeCoverage; } public Map getReleaseRepos(String releaseVersion) { @@ -73,5 +89,9 @@ public String getReleaseIssue (String releaseVersion, String repo) { return releaseIssueChecker.releaseIssue(releaseVersion, repo, openSearchUtil); } + public CodeCovResponse getCodeCoverage (String branch, String repo) { + return codeCoverage.coverage(branch, repo); + } + } diff --git a/src/main/java/org/opensearchmetrics/metrics/release/ReleaseVersionIncrementChecker.java b/src/main/java/org/opensearchmetrics/metrics/release/ReleaseVersionIncrementChecker.java index a6fc9c1..dddb9e5 100644 --- a/src/main/java/org/opensearchmetrics/metrics/release/ReleaseVersionIncrementChecker.java +++ b/src/main/java/org/opensearchmetrics/metrics/release/ReleaseVersionIncrementChecker.java @@ -1,3 +1,12 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + package org.opensearchmetrics.metrics.release; import com.fasterxml.jackson.databind.ObjectMapper; @@ -36,6 +45,7 @@ public boolean releaseVersionIncrement(String releaseVersion, String repo, Strin } } + public boolean checkOpenSearchVersion(String releaseVersion, String branch) { String url = String.format("https://raw.githubusercontent.com/opensearch-project/OpenSearch/%s/buildSrc/version.properties", branch); try { diff --git a/src/main/java/org/opensearchmetrics/model/codecov/CodeCovResponse.java b/src/main/java/org/opensearchmetrics/model/codecov/CodeCovResponse.java new file mode 100644 index 0000000..16fb382 --- /dev/null +++ b/src/main/java/org/opensearchmetrics/model/codecov/CodeCovResponse.java @@ -0,0 +1,41 @@ +package org.opensearchmetrics.model.codecov; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import lombok.Data; + +import java.io.IOException; + +@Data +public class CodeCovResponse { + + @JsonProperty("commitid") + private String commitid; + + @JsonProperty("url") + private String url; + + @JsonProperty("state") + private String state; + + + @JsonSerialize(using = CodeCovDoubleSerializer.class) + @JsonProperty("coverage") + private Double coverage; +} + +class CodeCovDoubleSerializer extends JsonSerializer { + + @Override + public void serialize(Double value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) + throws IOException { + if (value == null) { + jsonGenerator.writeNumber(0.0); + } else { + jsonGenerator.writeNumber(value); + } + } +} diff --git a/src/main/java/org/opensearchmetrics/model/codecov/CodeCovResult.java b/src/main/java/org/opensearchmetrics/model/codecov/CodeCovResult.java new file mode 100644 index 0000000..49fdd25 --- /dev/null +++ b/src/main/java/org/opensearchmetrics/model/codecov/CodeCovResult.java @@ -0,0 +1,89 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearchmetrics.model.codecov; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.Data; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +@Data +public class CodeCovResult { + + @JsonProperty("id") + private String id; + + @JsonProperty("current_date") + private String currentDate; + + @JsonProperty("repository") + private String repository; + + @JsonProperty("component") + private String component; + + @JsonProperty("release_version") + private String releaseVersion; + + @JsonProperty("release_state") + private String releaseState; + + @JsonProperty("version") + private String version; + + @JsonProperty("timestamp") + private String timestamp; + + @JsonProperty("commitid") + private String commitid; + + @JsonProperty("state") + private String state; + + @JsonProperty("coverage") + private Double coverage; + + @JsonProperty("branch") + private String branch; + + @JsonProperty("url") + private String url; + + + public String toJson(ObjectMapper mapper) throws JsonProcessingException { + Map data = new HashMap<>(); + data.put("id", id); + data.put("current_date", currentDate); + data.put("repository", repository); + data.put("component", component); + data.put("release_version", releaseVersion); + data.put("version", version); + data.put("release_state", releaseState); + data.put("commitid", commitid); + data.put("state", state); + data.put("coverage", coverage); + data.put("branch", branch); + data.put("url", url); + return mapper.writeValueAsString(data); + } + + public String getJson(CodeCovResult codeCovResult, ObjectMapper objectMapper) { + try { + return codeCovResult.toJson(objectMapper); + } catch (JsonProcessingException e) { + System.out.println("Error while serializing ReportDataRow to JSON " + e); + throw new RuntimeException(e); + } + } +} diff --git a/src/main/java/org/opensearchmetrics/model/release/ReleaseMetricsData.java b/src/main/java/org/opensearchmetrics/model/release/ReleaseMetricsData.java index 6113173..8c25782 100644 --- a/src/main/java/org/opensearchmetrics/model/release/ReleaseMetricsData.java +++ b/src/main/java/org/opensearchmetrics/model/release/ReleaseMetricsData.java @@ -1,3 +1,12 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + package org.opensearchmetrics.model.release; import com.fasterxml.jackson.annotation.JsonProperty; @@ -6,7 +15,6 @@ import lombok.Data; import java.util.HashMap; -import java.util.List; import java.util.Map; @Data @@ -38,6 +46,7 @@ public class ReleaseMetricsData { @JsonProperty("autocut_issues_open") private Long autocutIssuesOpen; + @JsonProperty("issues_closed") private Long issuesClosed; diff --git a/src/test/java/org/opensearchmetrics/lambda/MetricsLambdaTest.java b/src/test/java/org/opensearchmetrics/lambda/MetricsLambdaTest.java index c8df699..2e2ea3f 100644 --- a/src/test/java/org/opensearchmetrics/lambda/MetricsLambdaTest.java +++ b/src/test/java/org/opensearchmetrics/lambda/MetricsLambdaTest.java @@ -1,3 +1,12 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + package org.opensearchmetrics.lambda; import com.amazonaws.services.lambda.runtime.Context; @@ -46,12 +55,11 @@ public void testHandleRequest(){ when(aggregations.get("repos")).thenReturn(termsAggregation); when(termsAggregation.getBuckets()).thenReturn(Collections.emptyList()); metricsLambda.handleRequest(null, context); - - // Assert verify(openSearchUtil, times(1)).search(any(SearchRequest.class)); verify(metricsCalculation, times(1)).generateGeneralMetrics(anyList()); verify(metricsCalculation, times(1)).generateLabelMetrics(anyList()); verify(metricsCalculation, times(1)).generateReleaseMetrics(); + verify(metricsCalculation, times(1)).generateCodeCovMetrics(); } @@ -60,8 +68,6 @@ public void testHandleRequest(){ public void testHandleRequestWithMetricsCalculationException() { MetricsLambda metricsLambda = new MetricsLambda(openSearchUtil, metricsCalculation); Context context = mock(Context.class); - - // Mocking openSearchUtil.search() to return a SearchResponse with non-null aggregations SearchResponse searchResponse = mock(SearchResponse.class); Aggregations aggregations = mock(Aggregations.class); ParsedStringTerms termsAggregation = mock(ParsedStringTerms.class); // Mock the ParsedStringTerms object @@ -69,16 +75,11 @@ public void testHandleRequestWithMetricsCalculationException() { when(aggregations.get("repos")).thenReturn(termsAggregation); // Return non-null termsAggregation when(searchResponse.getAggregations()).thenReturn(aggregations); // Return non-null aggregations when(openSearchUtil.search(any(SearchRequest.class))).thenReturn(searchResponse); - - // Mocking MetricsCalculation methods to throw an exception doThrow(new RuntimeException("Error running Metrics Calculation")).when(metricsCalculation).generateGeneralMetrics(anyList()); - - // Act & Assert try { metricsLambda.handleRequest(null, context); fail("Expected a RuntimeException to be thrown"); } catch (RuntimeException e) { - // Exception caught as expected System.out.println("Caught exception message: " + e.getMessage()); assertTrue(e.getMessage().contains("Error running Metrics Calculation")); } diff --git a/src/test/java/org/opensearchmetrics/metrics/MetricsCalculationTest.java b/src/test/java/org/opensearchmetrics/metrics/MetricsCalculationTest.java index edaf286..689af40 100644 --- a/src/test/java/org/opensearchmetrics/metrics/MetricsCalculationTest.java +++ b/src/test/java/org/opensearchmetrics/metrics/MetricsCalculationTest.java @@ -1,5 +1,15 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + package org.opensearchmetrics.metrics; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -7,6 +17,7 @@ import org.mockito.ArgumentMatchers; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.opensearch.action.search.SearchRequest; @@ -15,6 +26,7 @@ import org.opensearchmetrics.metrics.label.LabelMetrics; import org.opensearchmetrics.metrics.release.ReleaseInputs; import org.opensearchmetrics.metrics.release.ReleaseMetrics; +import org.opensearchmetrics.model.codecov.CodeCovResponse; import org.opensearchmetrics.model.label.LabelData; import org.opensearchmetrics.model.general.MetricsData; import org.opensearchmetrics.model.release.ReleaseMetricsData; @@ -22,6 +34,7 @@ import java.io.IOException; import java.security.NoSuchAlgorithmException; +import java.time.LocalDate; import java.time.LocalDateTime; import java.time.ZoneId; import java.util.*; @@ -141,4 +154,37 @@ void testGenerateReleaseMetrics() { verify(openSearchUtil).bulkIndex(eq("opensearch_release_metrics"), ArgumentMatchers.anyMap()); verify(openSearchUtil, times(1)).createIndexIfNotExists("opensearch_release_metrics"); } + + + @Test + void testGenerateCodeCovMetrics() { + try (MockedStatic mockedReleaseInputs = Mockito.mockStatic(ReleaseInputs.class)) { + ReleaseInputs releaseInput = mock(ReleaseInputs.class); + when(releaseInput.getVersion()).thenReturn("2.18.0"); + when(releaseInput.getBranch()).thenReturn("main"); + when(releaseInput.getTrack()).thenReturn(true); + when(releaseInput.getState()).thenReturn("active"); + ReleaseInputs[] releaseInputsArray = {releaseInput}; + mockedReleaseInputs.when(ReleaseInputs::getAllReleaseInputs).thenReturn(releaseInputsArray); + Map releaseRepos = new HashMap<>(); + releaseRepos.put("repo1", "component1"); + when(releaseMetrics.getReleaseRepos("2.18.0")).thenReturn(releaseRepos); + CodeCovResponse codeCovResponse = new CodeCovResponse(); + codeCovResponse.setCommitid("abc123"); + codeCovResponse.setUrl("https://sample-url.com"); + codeCovResponse.setState("success"); + codeCovResponse.setCoverage(85.5); + when(releaseMetrics.getCodeCoverage("main", "repo1")).thenReturn(codeCovResponse); + try { + when(objectMapper.writeValueAsString(any())).thenReturn("{}"); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + metricsCalculation.generateCodeCovMetrics(); + verify(openSearchUtil).createIndexIfNotExists(matches("opensearch-codecov-metrics-\\d{2}-\\d{4}")); + verify(openSearchUtil).bulkIndex(matches("opensearch-codecov-metrics-\\d{2}-\\d{4}"), argThat(map -> !map.isEmpty())); + verify(releaseMetrics).getCodeCoverage("main", "repo1"); + verify(releaseMetrics).getReleaseRepos("2.18.0"); + } + } } diff --git a/src/test/java/org/opensearchmetrics/metrics/release/CodeCoverageTest.java b/src/test/java/org/opensearchmetrics/metrics/release/CodeCoverageTest.java new file mode 100644 index 0000000..47a9e65 --- /dev/null +++ b/src/test/java/org/opensearchmetrics/metrics/release/CodeCoverageTest.java @@ -0,0 +1,148 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearchmetrics.metrics.release; + +import org.apache.http.HttpEntity; +import org.apache.http.StatusLine; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.impl.client.CloseableHttpClient; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opensearchmetrics.model.codecov.CodeCovResponse; + +import java.io.ByteArrayInputStream; +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class CodeCoverageTest { + + @Mock + private CloseableHttpClient httpClient; + + private CodeCoverage codeCoverage; + + private final String validJsonResponse = """ + { + "results": [{ + "state": "complete", + "commitid": "abc123", + "totals": { + "coverage": 85.5 + } + }] + } + """; + + private final String emptyResultsResponse = """ + { + "results": [] + } + """; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + codeCoverage = new CodeCoverage(httpClient); + } + + @Test + void testDefaultConstructor() { + CodeCoverage defaultCodeCoverage = new CodeCoverage(); + assertNotNull(defaultCodeCoverage); + } + + @Test + void testSuccessfulCoverageRequest() throws IOException { + CloseableHttpResponse httpResponse = mock(CloseableHttpResponse.class); + StatusLine statusLine = mock(StatusLine.class); + HttpEntity httpEntity = mock(HttpEntity.class); + when(statusLine.getStatusCode()).thenReturn(200); + when(httpResponse.getStatusLine()).thenReturn(statusLine); + when(httpResponse.getEntity()).thenReturn(httpEntity); + when(httpEntity.getContent()).thenReturn(new ByteArrayInputStream(validJsonResponse.getBytes())); + when(httpClient.execute(any())).thenReturn(httpResponse); + CodeCovResponse response = codeCoverage.coverage("main", "test-repo"); + assertNotNull(response); + assertEquals("complete", response.getState()); + assertEquals("abc123", response.getCommitid()); + assertEquals(85.5, response.getCoverage()); + assertEquals("https://api.codecov.io/api/v2/github/opensearch-project/repos/test-repo/commits?branch=main", + response.getUrl()); + } + + @Test + void testEmptyResultsResponse() throws IOException { + CloseableHttpResponse httpResponse = mock(CloseableHttpResponse.class); + StatusLine statusLine = mock(StatusLine.class); + HttpEntity httpEntity = mock(HttpEntity.class); + + when(statusLine.getStatusCode()).thenReturn(200); + when(httpResponse.getStatusLine()).thenReturn(statusLine); + when(httpResponse.getEntity()).thenReturn(httpEntity); + when(httpEntity.getContent()).thenReturn(new ByteArrayInputStream(emptyResultsResponse.getBytes())); + when(httpClient.execute(any())).thenReturn(httpResponse); + CodeCovResponse response = codeCoverage.coverage("main", "test-repo"); + assertNotNull(response); + assertEquals("no-coverage", response.getState()); + assertEquals("none", response.getCommitid()); + assertEquals(0.0, response.getCoverage()); + } + + @Test + void testWithout200Response() throws IOException { + CloseableHttpResponse httpResponse = mock(CloseableHttpResponse.class); + StatusLine statusLine = mock(StatusLine.class); + when(statusLine.getStatusCode()).thenReturn(404); + when(httpResponse.getStatusLine()).thenReturn(statusLine); + when(httpClient.execute(any())).thenReturn(httpResponse); + CodeCovResponse response = codeCoverage.coverage("main", "test-repo"); + assertNotNull(response); + assertNull(response.getState()); + assertNull(response.getCommitid()); + assertEquals(null, response.getCoverage()); + } + + @Test + void testEntityUtilsThrowsException() throws IOException { + CloseableHttpResponse httpResponse = mock(CloseableHttpResponse.class); + StatusLine statusLine = mock(StatusLine.class); + HttpEntity httpEntity = mock(HttpEntity.class); + when(statusLine.getStatusCode()).thenReturn(200); + when(httpResponse.getStatusLine()).thenReturn(statusLine); + when(httpResponse.getEntity()).thenReturn(httpEntity); + when(httpEntity.getContent()).thenThrow(new IOException("Error reading content")); + when(httpClient.execute(any())).thenReturn(httpResponse); + assertThrows(RuntimeException.class, () -> codeCoverage.coverage("main", "test-repo")); + } + + @Test + void testHttpClientExecuteThrowsException() throws IOException { + when(httpClient.execute(any())).thenThrow(new IOException("Failed to execute request")); + assertThrows(RuntimeException.class, () -> codeCoverage.coverage("main", "test-repo")); + } + + @Test + void testResponseCloseThrowsException() throws IOException { + CloseableHttpResponse httpResponse = mock(CloseableHttpResponse.class); + StatusLine statusLine = mock(StatusLine.class); + when(statusLine.getStatusCode()).thenReturn(404); + when(httpResponse.getStatusLine()).thenReturn(statusLine); + when(httpClient.execute(any())).thenReturn(httpResponse); + doThrow(new IOException("Failed to close response")).when(httpResponse).close(); + assertThrows(RuntimeException.class, () -> codeCoverage.coverage("main", "test-repo")); + } +} diff --git a/src/test/java/org/opensearchmetrics/metrics/release/ReleaseMetricsTest.java b/src/test/java/org/opensearchmetrics/metrics/release/ReleaseMetricsTest.java index b82d646..e128e5b 100644 --- a/src/test/java/org/opensearchmetrics/metrics/release/ReleaseMetricsTest.java +++ b/src/test/java/org/opensearchmetrics/metrics/release/ReleaseMetricsTest.java @@ -1,17 +1,28 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + package org.opensearchmetrics.metrics.release; import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.opensearchmetrics.model.codecov.CodeCovResponse; import org.opensearchmetrics.util.OpenSearchUtil; import java.util.Collections; -import java.util.List; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.when; @@ -41,90 +52,114 @@ public class ReleaseMetricsTest { @Mock private ReleaseNotesChecker releaseNotesChecker; - @Mock private ReleaseIssueChecker releaseIssueChecker; + @Mock + private CodeCoverage codeCoverage; + + @InjectMocks + private ReleaseMetrics releaseMetrics; + + private CodeCovResponse codeCovResponse; + + @BeforeEach + public void setUp() { + MockitoAnnotations.openMocks(this); + codeCovResponse = new CodeCovResponse(); + codeCovResponse.setCommitid("abc123"); + codeCovResponse.setUrl("https://sample-release-issue/100"); + codeCovResponse.setState("success"); + codeCovResponse.setCoverage(85.5); + } + @Test public void testGetReleaseRepos() { - MockitoAnnotations.openMocks(this); Map repos = Collections.singletonMap("testRepo", "testComponent"); when(releaseRepoFetcher.getReleaseRepos(anyString())).thenReturn(repos); - Map result = releaseRepoFetcher.getReleaseRepos("1.0.0"); + + Map result = releaseMetrics.getReleaseRepos("1.0.0"); assertEquals(repos, result); } @Test public void testGetReleaseNotes() { - MockitoAnnotations.openMocks(this); - boolean expectedNotes = true; when(releaseNotesChecker.releaseNotes(anyString(), anyString(), anyString())) .thenReturn(expectedNotes); - boolean result = releaseNotesChecker.releaseNotes("1.0.0", "testRepo", "1.0"); + + boolean result = releaseMetrics.getReleaseNotes("1.0.0", "testRepo", "1.0"); assertEquals(expectedNotes, result); } @Test public void testGetReleaseLabelIssues() { - MockitoAnnotations.openMocks(this); - long expectedIssuesCount = 10; when(releaseLabelIssuesFetcher.releaseLabelIssues(anyString(), anyString(), anyString(), anyBoolean(), any())) .thenReturn(expectedIssuesCount); - long result = releaseLabelIssuesFetcher.releaseLabelIssues("1.0.0", "testRepo", "open", true, openSearchUtil); - + long result = releaseMetrics.getReleaseLabelIssues("1.0.0", "testRepo", "open", true); assertEquals(expectedIssuesCount, result); } @Test public void testGetReleaseLabelPulls() { - MockitoAnnotations.openMocks(this); long expectedPullsCount = 5; when(releaseLabelPullsFetcher.releaseLabelPulls(anyString(), anyString(), anyString(), any())) .thenReturn(expectedPullsCount); - long result = releaseLabelPullsFetcher.releaseLabelPulls("1.0.0", "testRepo", "open", openSearchUtil); + + long result = releaseMetrics.getReleaseLabelPulls("1.0.0", "testRepo", "open"); assertEquals(expectedPullsCount, result); } @Test public void testGetReleaseVersionIncrement() { - MockitoAnnotations.openMocks(this); boolean expectedIncrement = true; when(releaseVersionIncrementChecker.releaseVersionIncrement(anyString(), anyString(), anyString(), any(), any())) .thenReturn(expectedIncrement); - boolean result = releaseVersionIncrementChecker.releaseVersionIncrement("1.0.0", "testRepo", "main", objectMapper, openSearchUtil); + + boolean result = releaseMetrics.getReleaseVersionIncrement("1.0.0", "testRepo", "main"); assertEquals(expectedIncrement, result); } @Test public void testGetReleaseBranch() { - MockitoAnnotations.openMocks(this); boolean expectedBranch = true; when(releaseBranchChecker.releaseBranch(anyString(), anyString())) .thenReturn(expectedBranch); - boolean result = releaseBranchChecker.releaseBranch("1.0.0", "testRepo"); + + boolean result = releaseMetrics.getReleaseBranch("1.0.0", "testRepo"); assertEquals(expectedBranch, result); } @Test - public void testGetReleaseOwner() { - MockitoAnnotations.openMocks(this); - String[] releaseOwners = new String[]{"sample_user_1"}; + public void testGetReleaseOwners() { + String[] expectedOwners = new String[]{"sample_user_1"}; when(releaseIssueChecker.releaseOwners(anyString(), anyString(), any())) - .thenReturn(releaseOwners); - String[] result = releaseIssueChecker.releaseOwners("1.0.0", "testRepo", openSearchUtil); - assertEquals(releaseOwners, result); + .thenReturn(expectedOwners); + + String[] result = releaseMetrics.getReleaseOwners("1.0.0", "testRepo"); + assertArrayEquals(expectedOwners, result); } @Test public void testGetReleaseIssue() { - MockitoAnnotations.openMocks(this); - String releaseIssue = "https://sample-release-issue/100"; + String expectedIssue = "https://sample-release-issue/100"; when(releaseIssueChecker.releaseIssue(anyString(), anyString(), any())) - .thenReturn(releaseIssue); - String result = releaseIssueChecker.releaseIssue("1.0.0", "testRepo", openSearchUtil); - assertEquals(releaseIssue, result); + .thenReturn(expectedIssue); + + String result = releaseMetrics.getReleaseIssue("1.0.0", "testRepo"); + assertEquals(expectedIssue, result); + } + + @Test + public void testGetCodeCoverage() { + when(codeCoverage.coverage(anyString(), anyString())).thenReturn(codeCovResponse); + + CodeCovResponse result = releaseMetrics.getCodeCoverage("2.18", "testRepo"); + assertEquals("abc123", result.getCommitid()); + assertEquals("https://sample-release-issue/100", result.getUrl()); + assertEquals("success", result.getState()); + assertEquals(85.5, result.getCoverage()); } } diff --git a/src/test/java/org/opensearchmetrics/metrics/release/ReleaseVersionIncrementCheckerTest.java b/src/test/java/org/opensearchmetrics/metrics/release/ReleaseVersionIncrementCheckerTest.java index 0b3e5f3..ffc0d34 100644 --- a/src/test/java/org/opensearchmetrics/metrics/release/ReleaseVersionIncrementCheckerTest.java +++ b/src/test/java/org/opensearchmetrics/metrics/release/ReleaseVersionIncrementCheckerTest.java @@ -1,3 +1,12 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + package org.opensearchmetrics.metrics.release; import com.fasterxml.jackson.databind.ObjectMapper; @@ -18,39 +27,25 @@ public class ReleaseVersionIncrementCheckerTest { @Test void testReleaseVersionIncrement_OpenSearch() { - // Given String releaseVersion = "1.0.0"; String branch = "main"; ReleaseVersionIncrementChecker checker = new ReleaseVersionIncrementChecker(); - - // When boolean result = checker.releaseVersionIncrement(releaseVersion, "OpenSearch", branch, null, null); - - // Then assertFalse(result); } @Test void testReleaseVersionIncrement_OpenSearchDashboards() throws IOException { - // Given String releaseVersion = "1.0.0"; String branch = "main"; String repo = "OpenSearch-Dashboards"; ObjectMapper objectMapper = Mockito.mock(ObjectMapper.class); - - // Mocking the behavior of objectMapper.readTree() JsonNodeFactory nodeFactory = JsonNodeFactory.instance; ObjectNode objectNode = nodeFactory.objectNode(); objectNode.put("version", releaseVersion); - Mockito.when(objectMapper.readTree(Mockito.anyString())).thenReturn(objectNode); - ReleaseVersionIncrementChecker checker = new ReleaseVersionIncrementChecker(); - - // When boolean result = checker.releaseVersionIncrement(releaseVersion, repo, branch, objectMapper, null); - - // Then assertTrue(result); } @@ -58,22 +53,17 @@ void testReleaseVersionIncrement_OpenSearchDashboards() throws IOException { @Test void testReleaseVersionIncrement_GithubPulls() { - // Given String releaseVersion = "1.0.0"; String repo = "some-repo"; ObjectMapper objectMapper = Mockito.mock(ObjectMapper.class); OpenSearchUtil openSearchUtil = Mockito.mock(OpenSearchUtil.class); SearchResponse searchResponse = Mockito.mock(SearchResponse.class); SearchHits searchHits = Mockito.mock(SearchHits.class); - Mockito.when(searchHits.getHits()).thenReturn(new SearchHit[0]); // Empty array to avoid NPE + Mockito.when(searchHits.getHits()).thenReturn(new SearchHit[0]); Mockito.when(searchResponse.getHits()).thenReturn(searchHits); Mockito.when(openSearchUtil.search(Mockito.any())).thenReturn(searchResponse); ReleaseVersionIncrementChecker checker = new ReleaseVersionIncrementChecker(); - - // When boolean result = checker.releaseVersionIncrement(releaseVersion, repo, "main", objectMapper, openSearchUtil); - - // Then assertFalse(result); } diff --git a/src/test/java/org/opensearchmetrics/model/codecov/CodeCovDoubleSerializerTest.java b/src/test/java/org/opensearchmetrics/model/codecov/CodeCovDoubleSerializerTest.java new file mode 100644 index 0000000..98703d6 --- /dev/null +++ b/src/test/java/org/opensearchmetrics/model/codecov/CodeCovDoubleSerializerTest.java @@ -0,0 +1,58 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearchmetrics.model.codecov; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.io.IOException; + +import static org.mockito.Mockito.verify; + +public class CodeCovDoubleSerializerTest { + + @Mock + private JsonGenerator jsonGenerator; + + @Mock + private SerializerProvider serializerProvider; + + private CodeCovDoubleSerializer serializer; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + serializer = new CodeCovDoubleSerializer(); + } + + @Test + public void testSerializeNullValue() throws IOException { + serializer.serialize(null, jsonGenerator, serializerProvider); + verify(jsonGenerator).writeNumber(0.0); + } + + @Test + public void testSerializeNonNullValue() throws IOException { + Double value = 85.5; + serializer.serialize(value, jsonGenerator, serializerProvider); + verify(jsonGenerator).writeNumber(85.5); + } + + @Test + public void testSerializeZeroValue() throws IOException { + Double value = 0.0; + serializer.serialize(value, jsonGenerator, serializerProvider); + verify(jsonGenerator).writeNumber(0.0); + } +} diff --git a/src/test/java/org/opensearchmetrics/model/codecov/CodeCovResponseTest.java b/src/test/java/org/opensearchmetrics/model/codecov/CodeCovResponseTest.java new file mode 100644 index 0000000..88c905c --- /dev/null +++ b/src/test/java/org/opensearchmetrics/model/codecov/CodeCovResponseTest.java @@ -0,0 +1,75 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearchmetrics.model.codecov; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class CodeCovResponseTest { + + private ObjectMapper objectMapper; + + @BeforeEach + void setUp() { + objectMapper = new ObjectMapper(); + SimpleModule module = new SimpleModule(); + module.addSerializer(Double.class, new CodeCovDoubleSerializer()); + objectMapper.registerModule(module); + } + + @Test + public void testSerializeNonNullCoverage() throws JsonProcessingException { + CodeCovResponse response = new CodeCovResponse(); + response.setCommitid("abc123"); + response.setUrl("https://sample.url"); + response.setState("success"); + response.setCoverage(85.5); + String json = objectMapper.writeValueAsString(response); + String expectedJson = "{\"commitid\":\"abc123\",\"url\":\"https://sample.url\",\"state\":\"success\",\"coverage\":85.5}"; + assertEquals(expectedJson, json); + } + + @Test + public void testSerializeZeroCoverage() throws JsonProcessingException { + CodeCovResponse response = new CodeCovResponse(); + response.setCommitid("abc123"); + response.setUrl("https://sample.url"); + response.setState("success"); + response.setCoverage(0.0); + String json = objectMapper.writeValueAsString(response); + String expectedJson = "{\"commitid\":\"abc123\",\"url\":\"https://sample.url\",\"state\":\"success\",\"coverage\":0.0}"; + assertEquals(expectedJson, json); + } + + @Test + public void testDeserializeResponse() throws JsonProcessingException { + String json = "{\"commitid\":\"abc123\",\"url\":\"https://sample.url\",\"state\":\"success\",\"coverage\":85.5}"; + CodeCovResponse response = objectMapper.readValue(json, CodeCovResponse.class); + assertEquals("abc123", response.getCommitid()); + assertEquals("https://sample.url", response.getUrl()); + assertEquals("success", response.getState()); + assertEquals(85.5, response.getCoverage()); + } + + @Test + public void testDeserializeNullCoverage() throws JsonProcessingException { + String json = "{\"commitid\":\"abc123\",\"url\":\"https://sample.url\",\"state\":\"success\",\"coverage\":null}"; + CodeCovResponse response = objectMapper.readValue(json, CodeCovResponse.class); + assertEquals("abc123", response.getCommitid()); + assertEquals("https://sample.url", response.getUrl()); + assertEquals("success", response.getState()); + assertEquals(null, response.getCoverage()); + } +} diff --git a/src/test/java/org/opensearchmetrics/model/codecov/CodeCovResultTest.java b/src/test/java/org/opensearchmetrics/model/codecov/CodeCovResultTest.java new file mode 100644 index 0000000..f43abd7 --- /dev/null +++ b/src/test/java/org/opensearchmetrics/model/codecov/CodeCovResultTest.java @@ -0,0 +1,233 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearchmetrics.model.codecov; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockitoAnnotations; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class CodeCovResultTest { + + private ObjectMapper objectMapper; + + private CodeCovResult codeCovResult; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + codeCovResult = new CodeCovResult(); + objectMapper = new ObjectMapper(); + SimpleModule module = new SimpleModule(); + module.addSerializer(Double.class, new CodeCovDoubleSerializer()); + objectMapper.registerModule(module); + } + + @Test + public void testId() { + codeCovResult.setId("123"); + assertEquals("123", codeCovResult.getId()); + } + + @Test + public void testCurrentDate() { + codeCovResult.setCurrentDate("2024-01-01"); + assertEquals("2024-01-01", codeCovResult.getCurrentDate()); + } + + @Test + public void testRepository() { + codeCovResult.setRepository("test-repo"); + assertEquals("test-repo", codeCovResult.getRepository()); + } + + @Test + public void testComponent() { + codeCovResult.setComponent("test-component"); + assertEquals("test-component", codeCovResult.getComponent()); + } + + @Test + public void testReleaseVersion() { + codeCovResult.setReleaseVersion("2.0.0"); + assertEquals("2.0.0", codeCovResult.getReleaseVersion()); + } + + @Test + public void testReleaseState() { + codeCovResult.setReleaseState("released"); + assertEquals("released", codeCovResult.getReleaseState()); + } + + @Test + public void testVersion() { + codeCovResult.setVersion("1.0"); + assertEquals("1.0", codeCovResult.getVersion()); + } + + @Test + public void testTimestamp() { + codeCovResult.setTimestamp("2024-01-01T12:00:00Z"); + assertEquals("2024-01-01T12:00:00Z", codeCovResult.getTimestamp()); + } + + @Test + public void testCommitid() { + codeCovResult.setCommitid("abc123"); + assertEquals("abc123", codeCovResult.getCommitid()); + } + + @Test + public void testState() { + codeCovResult.setState("success"); + assertEquals("success", codeCovResult.getState()); + } + + @Test + public void testCoverage() { + codeCovResult.setCoverage(85.5); + assertEquals(85.5, codeCovResult.getCoverage()); + } + + @Test + public void testBranch() { + codeCovResult.setBranch("main"); + assertEquals("main", codeCovResult.getBranch()); + } + + @Test + public void testUrl() { + codeCovResult.setUrl("https://example.com"); + assertEquals("https://example.com", codeCovResult.getUrl()); + } + + @Test + public void testSerializeNonNullCoverage() throws JsonProcessingException { + CodeCovResult result = new CodeCovResult(); + result.setId("1"); + result.setCommitid("abc123"); + result.setUrl("https://sample.url"); + result.setState("success"); + result.setCoverage(85.5); + result.setRepository("sample-repo"); + String json = result.toJson(objectMapper); + ObjectMapper mapper = new ObjectMapper(); + Map actualMap = mapper.readValue(json, new TypeReference<>() {}); + Map expectedMap = mapper.readValue( + "{\"id\":\"1\"," + + "\"current_date\":null," + + "\"repository\":\"sample-repo\"," + + "\"component\":null," + + "\"release_version\":null," + + "\"version\":null," + + "\"release_state\":null," + + "\"commitid\":\"abc123\"," + + "\"state\":\"success\"," + + "\"coverage\":85.5," + + "\"branch\":null," + + "\"url\":\"https://sample.url\"}", + new TypeReference<>() { + } + ); + assertEquals(expectedMap, actualMap); + } + + @Test + public void testSerializeZeroCoverage() throws JsonProcessingException { + CodeCovResult result = new CodeCovResult(); + result.setId("2"); + result.setCommitid("def456"); + result.setUrl("https://sample2.url"); + result.setState("failure"); + result.setCoverage(0.0); + String json = result.toJson(objectMapper); + ObjectMapper mapper = new ObjectMapper(); + Map actualMap = mapper.readValue(json, new TypeReference>() {}); + Map expectedMap = mapper.readValue( + "{\"id\":\"2\"," + + "\"current_date\":null," + + "\"repository\":null," + + "\"component\":null," + + "\"release_version\":null," + + "\"version\":null," + + "\"release_state\":null," + + "\"commitid\":\"def456\"," + + "\"state\":\"failure\"," + + "\"coverage\":0.0," + + "\"branch\":null," + + "\"url\":\"https://sample2.url\"}", + new TypeReference<>() { + } + ); + assertEquals(expectedMap, actualMap); + } + + @Test + public void testDeserializeResult() throws JsonProcessingException { + String json = "{\"id\":\"3\",\"commitid\":\"ghi789\",\"url\":\"https://sample3.url\"," + + "\"state\":\"partial\",\"coverage\":90.0,\"repository\":\"test-repo\"}"; + CodeCovResult result = objectMapper.readValue(json, CodeCovResult.class); + assertEquals("3", result.getId()); + assertEquals("ghi789", result.getCommitid()); + assertEquals("https://sample3.url", result.getUrl()); + assertEquals("partial", result.getState()); + assertEquals(90.0, result.getCoverage()); + assertEquals("test-repo", result.getRepository()); + } + + @Test + public void testDeserializeNullCoverage() throws JsonProcessingException { + String json = "{\"id\":\"4\",\"commitid\":\"jkl012\",\"url\":\"https://sample4.url\"," + + "\"state\":\"error\",\"coverage\":null}"; + CodeCovResult result = objectMapper.readValue(json, CodeCovResult.class); + assertEquals("4", result.getId()); + assertEquals("jkl012", result.getCommitid()); + assertEquals("https://sample4.url", result.getUrl()); + assertEquals("error", result.getState()); + assertNull(result.getCoverage()); + } + + @Test + public void testGetJsonWithValidData() throws JsonProcessingException { + CodeCovResult result = new CodeCovResult(); + result.setId("5"); + result.setCommitid("mno345"); + result.setUrl("https://sample5.url"); + result.setState("completed"); + result.setCoverage(75.0); + String json = result.getJson(result, objectMapper); + ObjectMapper mapper = new ObjectMapper(); + Map actualMap = mapper.readValue(json, new TypeReference>() {}); + Map expectedMap = mapper.readValue( + "{\"id\":\"5\"," + + "\"current_date\":null," + + "\"repository\":null," + + "\"component\":null," + + "\"release_version\":null," + + "\"version\":null," + + "\"release_state\":null," + + "\"commitid\":\"mno345\"," + + "\"state\":\"completed\"," + + "\"coverage\":75.0," + + "\"branch\":null," + + "\"url\":\"https://sample5.url\"}", + new TypeReference<>() { + } + ); + assertEquals(expectedMap, actualMap); + } +}