diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 9331e887b..0102ab93d 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -36,6 +36,8 @@ jobs: - name: Build if: steps.cache-build.outputs.cache-hit != 'true' run: make + - name: Run Unit Tests + run: make go-unit-test - name: Run Integration Tests run: make test-parallel - name: Generate code coverage artifacts @@ -43,12 +45,12 @@ jobs: uses: actions/upload-artifact@v4 with: name: code-coverage - path: cover.out + path: cover-*.out - name: Upload code coverage information to codecov.io if: ${{ !cancelled() }} uses: codecov/codecov-action@v5.3.1 with: - files: cover.out + files: cover.out,cover-go-unit.out fail_ci_if_error: false env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/Makefile b/Makefile index 5498285e7..e12b927e6 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,4 @@ +CURRENT_DIR=$(shell pwd) # Image URL to use all building/pushing image targets IMG ?= quay.io/argoprojlabs/gitops-promoter:latest # ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. @@ -79,8 +80,12 @@ vet: ## Run go vet against code. go vet ./... .PHONY: test -test: manifests generate fmt vet envtest ## Run tests. - KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out +test: test-deps ## Run Ginkgo tests using go test, we prefer to use test-parallel but this target is nice because it dose not require ginkgo cli. + KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test -run TestControllersGinkgo ./... -coverprofile cover.out + +.PHONY: go-unit-test +go-unit-test: ## Run go unit tests + NO_GINKGO="true" KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test ./... -coverprofile cover-go-unit.out # Utilize Kind or modify the e2e tests to load the image locally, enabling compatibility with other vendors. .PHONY: test-e2e # Run the e2e tests against a Kind k8s instance that is spun up. @@ -211,7 +216,7 @@ GORELEASER ?= $(LOCALBIN)/goreleaser-$(GORELEASER_VERSION) KUSTOMIZE_VERSION ?= v5.3.0 CONTROLLER_TOOLS_VERSION ?= v0.16.3 ENVTEST_VERSION ?= release-0.19 -GOLANGCI_LINT_VERSION ?= v1.62.0 +GOLANGCI_LINT_VERSION ?= v1.62.2 MOCKERY_VERSION ?= v2.42.2 NILAWAY_VERSION ?= latest GINKGO_VERSION=$(shell go list -m all | grep github.com/onsi/ginkgo/v2 | awk '{print $$2}') diff --git a/internal/controller/suite_test.go b/internal/controller/suite_test.go index d6d5049c1..a03e41b27 100644 --- a/internal/controller/suite_test.go +++ b/internal/controller/suite_test.go @@ -76,7 +76,10 @@ const ( WebhookReceiverPort = 3333 ) -func TestControllers(t *testing.T) { +func TestControllersGinkgo(t *testing.T) { + if os.Getenv("NO_GINKGO") == "true" { + t.Skip("Skipping testing in CI environment") + } t.Parallel() RegisterFailHandler(Fail) diff --git a/internal/utils/utils_test.go b/internal/utils/utils_test.go new file mode 100644 index 000000000..0cd804464 --- /dev/null +++ b/internal/utils/utils_test.go @@ -0,0 +1,117 @@ +package utils + +import ( + "testing" + + promoterv1alpha1 "github.com/argoproj-labs/gitops-promoter/api/v1alpha1" + "github.com/stretchr/testify/assert" +) + +func TestTruncateString(t *testing.T) { + t.Parallel() // Enable parallel execution for the top-level test + tests := []struct { + name string + input string + length int + expected string + }{ + {"Empty string", "", 5, ""}, + {"Short string", "abc", 5, "abc"}, + {"Exact length", "abcde", 5, "abcde"}, + {"Truncated string", "abcdef", 5, "abcde"}, + {"Negative length", "abcdef", -1, ""}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + result := TruncateString(test.input, test.length) + assert.Equal(t, test.expected, result) + }) + } +} + +func TestUpsertEnvironmentStatus(t *testing.T) { + t.Parallel() // Enable parallel execution for the top-level test + tests := []struct { + name string + initial []promoterv1alpha1.EnvironmentStatus + insert promoterv1alpha1.EnvironmentStatus + expected []promoterv1alpha1.EnvironmentStatus + }{ + { + name: "Upsert on empty slice", + initial: []promoterv1alpha1.EnvironmentStatus{}, + insert: promoterv1alpha1.EnvironmentStatus{Branch: "main"}, + expected: []promoterv1alpha1.EnvironmentStatus{{Branch: "main"}}, + }, + { + name: "Append new element", + initial: []promoterv1alpha1.EnvironmentStatus{{Branch: "main"}}, + insert: promoterv1alpha1.EnvironmentStatus{Branch: "dev"}, + expected: []promoterv1alpha1.EnvironmentStatus{{Branch: "main"}, {Branch: "dev"}}, + }, + { + name: "Edge case with one element", + initial: []promoterv1alpha1.EnvironmentStatus{{Branch: "main"}}, + insert: promoterv1alpha1.EnvironmentStatus{Branch: "dev"}, + expected: []promoterv1alpha1.EnvironmentStatus{{Branch: "main"}, {Branch: "dev"}}, + }, + { + name: "Update existing element", + initial: []promoterv1alpha1.EnvironmentStatus{{ + Branch: "main", + Active: promoterv1alpha1.PromotionStrategyBranchStateStatus{ + Dry: promoterv1alpha1.CommitShaState{ + Sha: "old", + }, + Hydrated: promoterv1alpha1.CommitShaState{ + Sha: "old", + }, + CommitStatus: promoterv1alpha1.PromotionStrategyCommitStatus{ + Sha: "old", + Phase: "pending", + }, + }, + }}, + insert: promoterv1alpha1.EnvironmentStatus{ + Branch: "main", + Active: promoterv1alpha1.PromotionStrategyBranchStateStatus{ + Dry: promoterv1alpha1.CommitShaState{ + Sha: "new", + }, + Hydrated: promoterv1alpha1.CommitShaState{ + Sha: "new", + }, + CommitStatus: promoterv1alpha1.PromotionStrategyCommitStatus{ + Sha: "new", + Phase: "success", + }, + }, + }, + expected: []promoterv1alpha1.EnvironmentStatus{{ + Branch: "main", + Active: promoterv1alpha1.PromotionStrategyBranchStateStatus{ + Dry: promoterv1alpha1.CommitShaState{ + Sha: "new", + }, + Hydrated: promoterv1alpha1.CommitShaState{ + Sha: "new", + }, + CommitStatus: promoterv1alpha1.PromotionStrategyCommitStatus{ + Sha: "new", + Phase: "success", + }, + }, + }}, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + result := UpsertEnvironmentStatus(test.initial, test.insert) + assert.Equal(t, test.expected, result) + }) + } +} diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index fa9b70f6c..56d7d652a 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -18,6 +18,7 @@ package e2e import ( "fmt" + "os" "testing" . "github.com/onsi/ginkgo/v2" @@ -26,6 +27,9 @@ import ( // Run e2e tests using the Ginkgo runner. func TestE2E(t *testing.T) { + if os.Getenv("NO_GINKGO") == "true" { + t.Skip("Skipping testing in CI environment") + } t.Parallel() RegisterFailHandler(Fail)