From aaebea0c5d563e8f0b4ec6999bc59eb69613fc9c Mon Sep 17 00:00:00 2001 From: Arvind Choudhary Date: Thu, 19 Dec 2024 09:31:11 +0000 Subject: [PATCH] feat: [AH-687]: Added PR check script for code coverage (#3135) * Merge branch 'main' of https://git0.harness.io/l7B_kbSEQD2wjrM7PShm5w/PROD/Harness_Commons/gitness into AH-687-PR-checks * [AH-687]: Updated format * [AH-687]: Updated copyright * [AH-687]: updated script * [AH-687]: updated script * [AH-687]: updated script * [AH-687]: Cleanup * [AH-687]: updated script * []: updated * []: updated script * []: updated script * []: updated script * []: added test * []: Added utils * []: updated script * []: updated script * []: updated script * []: updated script * []: updated script * []: updated script * []: added temp utils files * []: Updated dummy_arvind * []: Updated dummy_arvind * []: Updated dummy_arvind --- .../app/api/controller/metadata/utils_test.go | 161 ++++++++++++++++++ scripts/coverage/test_script.sh | 122 +++++++++++++ 2 files changed, 283 insertions(+) create mode 100644 registry/app/api/controller/metadata/utils_test.go create mode 100755 scripts/coverage/test_script.sh diff --git a/registry/app/api/controller/metadata/utils_test.go b/registry/app/api/controller/metadata/utils_test.go new file mode 100644 index 000000000..3744313aa --- /dev/null +++ b/registry/app/api/controller/metadata/utils_test.go @@ -0,0 +1,161 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package metadata + +import ( + "testing" + "time" + + "github.com/harness/gitness/registry/app/api/openapi/contracts/artifact" + + "github.com/stretchr/testify/assert" +) + +func TestValidatePackageTypes_ValidTypes(t *testing.T) { + err := ValidatePackageTypes([]string{"DOCKER", "HELM"}) + assert.NoError(t, err) +} + +func TestValidatePackageTypes_InvalidTypes(t *testing.T) { + err := ValidatePackageTypes([]string{"INVALID"}) + assert.Error(t, err) + assert.Equal(t, "invalid package type", err.Error()) +} + +func TestValidatePackageType_ValidType(t *testing.T) { + err := ValidatePackageType("DOCKER") + assert.NoError(t, err) +} + +func TestValidatePackageType_InvalidType(t *testing.T) { + err := ValidatePackageType("INVALID") + assert.Error(t, err) + assert.Equal(t, "invalid package type", err.Error()) +} + +func TestValidateIdentifier_ValidIdentifier(t *testing.T) { + err := ValidateIdentifier("valid-identifier") + assert.NoError(t, err) +} + +func TestValidateIdentifier_InvalidIdentifier(t *testing.T) { + err := ValidateIdentifier("Invalid Identifier") + assert.Error(t, err) + assert.Equal(t, RegistryIdentifierErrorMsg, err.Error()) +} + +func TestCleanURLPath_ValidURL(t *testing.T) { + input := "https://example.com/path/" + expected := "https://example.com/path" + CleanURLPath(&input) + assert.Equal(t, expected, input) +} + +func TestCleanURLPath_InvalidURL(t *testing.T) { + input := "://invalid-url" + expected := "://invalid-url" + CleanURLPath(&input) + assert.Equal(t, expected, input) +} + +func TestGetTimeInMs_ValidTime(t *testing.T) { + tm := time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC) + expected := "1672531200000" + result := GetTimeInMs(tm) + assert.Equal(t, expected, result) +} + +func TestGetErrorResponse_ValidError(t *testing.T) { + code := 404 + message := "Not Found" + expected := &artifact.Error{ + Code: "404", + Message: message, + } + result := GetErrorResponse(code, message) + assert.Equal(t, expected, result) +} + +func TestGetSortByOrder_ValidOrder(t *testing.T) { + assert.Equal(t, "ASC", GetSortByOrder("")) + assert.Equal(t, "DESC", GetSortByOrder("DESC")) + assert.Equal(t, "ASC", GetSortByOrder("INVALID")) +} + +func TestGetSortByField_ValidField(t *testing.T) { + assert.Equal(t, "name", GetSortByField("identifier", RepositoryResource)) + assert.Equal(t, "created_at", GetSortByField("invalid", RepositoryResource)) +} + +func TestGetPageLimit_ValidPageSize(t *testing.T) { + pageSize := artifact.PageSize(20) + assert.Equal(t, 20, GetPageLimit(&pageSize)) + assert.Equal(t, 10, GetPageLimit(nil)) +} + +func TestGetOffset_ValidOffset(t *testing.T) { + pageSize := artifact.PageSize(20) + pageNumber := artifact.PageNumber(2) + assert.Equal(t, 40, GetOffset(&pageSize, &pageNumber)) + assert.Equal(t, 0, GetOffset(nil, nil)) +} + +func TestGetPageNumber_ValidPageNumber(t *testing.T) { + pageNumber := artifact.PageNumber(2) + assert.Equal(t, int64(2), GetPageNumber(&pageNumber)) + assert.Equal(t, int64(1), GetPageNumber(nil)) +} + +func TestGetSuccessResponse_ValidResponse(t *testing.T) { + expected := &artifact.Success{ + Status: artifact.StatusSUCCESS, + } + result := GetSuccessResponse() + assert.Equal(t, expected, result) +} + +func TestGetPageCount_ValidCount(t *testing.T) { + assert.Equal(t, int64(5), GetPageCount(50, 10)) + assert.Equal(t, int64(0), GetPageCount(0, 10)) +} + +func TestGetRegistryRef_ValidRef(t *testing.T) { + assert.Equal(t, "root/registry", GetRegistryRef("root", "registry")) +} + +func TestGetRepoURLWithoutProtocol_ValidURL(t *testing.T) { + assert.Equal(t, "example.com/path", GetRepoURLWithoutProtocol("https://example.com/path")) + assert.Equal(t, "", GetRepoURLWithoutProtocol("://invalid-url")) +} + +func TestGetTagURL_ValidURL(t *testing.T) { + assert.Equal(t, "https://example.com/artifact/version", GetTagURL("artifact", "version", "https://example.com")) +} + +func TestGetPullCommand_ValidCommand(t *testing.T) { + assert.Equal(t, "docker pull example.com/image:tag", + GetPullCommand("image", "tag", "DOCKER", "https://example.com")) + assert.Equal(t, "helm pull oci://example.com/image:tag", + GetPullCommand("image", "tag", "HELM", "https://example.com")) + assert.Equal(t, "", GetPullCommand("image", "tag", "INVALID", "https://example.com")) +} + +func TestGetDockerPullCommand_ValidCommand(t *testing.T) { + assert.Equal(t, "docker pull example.com/image:tag", GetDockerPullCommand("image", "tag", "https://example.com")) +} + +func TestGetHelmPullCommand_ValidCommand(t *testing.T) { + assert.Equal(t, "helm pull oci://example.com/image:tag", GetHelmPullCommand("image", "tag", "https://example.com")) +} diff --git a/scripts/coverage/test_script.sh b/scripts/coverage/test_script.sh new file mode 100755 index 000000000..bb4d4aa07 --- /dev/null +++ b/scripts/coverage/test_script.sh @@ -0,0 +1,122 @@ +#!/usr/bin/env bash + +# Requirements: +# Run tests on the current and target branches. The directories are provided on the top. +# Find the total coverage of each of them. +# Success: +# current_branch_total_coverage >= target_branch_total_coverage +# for the new files in the provided list of directories, the coverage should be atleast 75% + + +set -euo pipefail + +# This function needs some work! +calculate_file_coverage() { + local file=$1 + local total_statements=0 + local covered_statements=0 + + # Parse coverage report for the given file + while read -r line; do + echo $line + # Each line format: "::\t" + # Extract statements and coverage percentage + statements=$(echo "$line" | awk -F: '{print $4}' | awk '{print $2}') + coverage=$(echo "$line" | awk -F: '{print $4}' | awk '{print $NF}' | sed 's/%//') + + # Accumulate total statements and covered statements + total_statements=$((total_statements + statements)) + covered_statements=$(echo "$covered_statements + ($statements * $coverage / 100)" | bc) + done < <(grep "$file" coverage_report.txt) + + # Calculate and print total coverage for the file + if (( total_statements == 0 )); then + echo "0.0" + else + echo "scale=2; ($covered_statements / $total_statements) * 100" | bc + fi +} + +# ============================ +# Configuration Section +# ============================ +# Provide the directories for which coverage should be checked. +# Directories can be nested. We'll add "/..." to each to ensure all sub-packages are included. +# Example: DIRECTORIES="./registry ./another/path" +DIRECTORIES="./registry" + +if [ $# -lt 1 ]; then + echo "Usage: $0 " + exit 1 +fi +TARGET_BRANCH=$1 + +# Convert directories into their recursive form (./dir => ./dir/...) +# to ensure nested packages are included +TEST_DIRS="" +for d in $DIRECTORIES; do + # If it doesn't already end with "..." then append it + if [[ "$d" != *"..." ]]; then + d="${d%/}/..." + fi + TEST_DIRS="$TEST_DIRS $d" +done + +# ============================ +# Setup +# ============================ +git fetch origin "${TARGET_BRANCH}:${TARGET_BRANCH}" +git checkout "${TARGET_BRANCH}" + +# Run coverage on the target branch +go test -coverprofile=coverage_target.out $TEST_DIRS +TARGET_COV=$(go tool cover -func=coverage_target.out | grep total | awk '{print $3}' | sed 's/%//') + +# Go back to the current (feature) branch +git checkout - + +# Run coverage on the current (feature) branch +go test -coverprofile=coverage_current.out $TEST_DIRS +CURRENT_COV=$(go tool cover -func=coverage_current.out | grep total | awk '{print $3}' | sed 's/%//') + +# Ensure the current branch coverage is not lower than the target branch coverage. +if (( $(echo "$CURRENT_COV < $TARGET_COV" | bc -l) )); then + echo "Coverage decreased from ${TARGET_COV}% to ${CURRENT_COV}%." + exit 1 +fi +echo "Coverage checks passed! Current coverage: ${CURRENT_COV}%, Target coverage: ${TARGET_COV}%" + +# Identify newly added files in the current branch compared to the target branch. +# NEW_FILES=$(git diff --name-status "${TARGET_BRANCH}...HEAD" | grep '^A' | awk '{print $2}' | grep '\.go$' | grep -v '_test\.go$') +# echo "new files: ""$NEW_FILES" + +#go tool cover -func=coverage_current.out > coverage_report.txt +# +#for file in $NEW_FILES; do +# # Check if the file is within one of the specified directories. +# echo "new file is: "$file +# IN_DIRS=false +# for d in $DIRECTORIES; do +# # Remove the trailing "/..." from the directory for a direct prefix check +# base_dir="${d%/...}" +# echo "base_dir value: "$d +# normalized_base_dir="${base_dir#./}" +# # If file starts with base_dir +# if [[ $file == $normalized_base_dir* ]]; then +# IN_DIRS=true +# break +# fi +# done +# +# if [ "$IN_DIRS" = true ]; then +# file_coverage=$(calculate_file_coverage "$file") +# if (( $(echo "$file_coverage < 75.0" | bc -l) )); then +# echo "New file $file has coverage of $file_coverage%, which is less than 75%." +# else +# echo "New file $file has coverage of $fcov%, which is better than 75%." +# fi +# fi +#done + +echo "All checks passed! Current coverage: ${CURRENT_COV}%, Target coverage: ${TARGET_COV}%" +rm -rf coverage_*