From d89539198ab1e931426411049cf7bfe544471f41 Mon Sep 17 00:00:00 2001 From: Aisuko Date: Mon, 15 May 2023 05:03:28 +0000 Subject: [PATCH] feat: add suit test cases for controller Signed-off-by: Aisuko --- .github/workflows/test.yaml | 2 +- controllers/k8sgpt_controller_test.go | 121 ++++++++++++++++++++++++++ controllers/suite_test.go | 89 ++++++++++++++++--- 3 files changed, 198 insertions(+), 14 deletions(-) create mode 100644 controllers/k8sgpt_controller_test.go diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 28aef71c..9c65e1eb 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -21,7 +21,7 @@ jobs: go-version: ${{ env.GO_VERSION }} - name: Test - run: go test -v ./... + run: make test test-chart: name: Test Helm Chart diff --git a/controllers/k8sgpt_controller_test.go b/controllers/k8sgpt_controller_test.go new file mode 100644 index 00000000..215833ea --- /dev/null +++ b/controllers/k8sgpt_controller_test.go @@ -0,0 +1,121 @@ +/* +Copyright 2023 K8sGPT Contributors. + +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 controllers + +import ( + "context" + + corev1alpha1 "github.com/k8sgpt-ai/k8sgpt-operator/api/v1alpha1" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" +) + +var _ = Describe("K8sGPT controller suit test", func() { + + var ( + ctx context.Context + ) + + BeforeEach(func() { + ctx = context.TODO() + }) + + Context("Getting k8sgpt CRDs", func() { + It("Should get error when getting k8sgpt CRDs", func() { + By("Getting k8sgpt CRDs") + k8sgpt := &corev1alpha1.K8sGPT{} + namespace := "default" + Expect(k8sClient.Get(ctx, types.NamespacedName{Name: "default", Namespace: namespace}, k8sgpt)).Should(HaveOccurred()) + }) + }) + + Context("Creating k8sgpt CRDs", func() { + It("Should not create k8sgpt CRDs by invalid backend", func() { + k8sgpt := &corev1alpha1.K8sGPT{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default", + Namespace: "default", + }, + Spec: corev1alpha1.K8sGPTSpec{ + EnableAI: true, + NoCache: true, + Backend: "gpt2", + Filters: []string{"gpt2"}, + }, + } + Expect(k8sClient.Create(ctx, k8sgpt)).Should(HaveOccurred()) + }) + + It("Should create k8sgpt CRDs by valid backend", func() { + k8sgpt := &corev1alpha1.K8sGPT{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default", + Namespace: "default", + }, + Spec: corev1alpha1.K8sGPTSpec{ + EnableAI: true, + NoCache: true, + Backend: "openai", + Filters: []string{"openai"}, + }, + } + Expect(k8sClient.Create(ctx, k8sgpt)).Should(Succeed()) + }) + }) + + Context("Getting k8sgpt CRDs", func() { + It("Should get k8sgpt CRDs", func() { + By("Getting k8sgpt CRDs") + k8sgpt := &corev1alpha1.K8sGPT{} + namespace := "default" + Expect(k8sClient.Get(ctx, types.NamespacedName{Name: "default", Namespace: namespace}, k8sgpt)).Should(Succeed()) + }) + }) + + Context("Updating k8sgpt CRDs", func() { + It("Should update k8sgpt CRDs", func() { + By("Updating k8sgpt CRDs") + k8sgpt := &corev1alpha1.K8sGPT{} + namespace := "default" + Expect(k8sClient.Get(ctx, types.NamespacedName{Name: "default", Namespace: namespace}, k8sgpt)).Should(Succeed()) + k8sgpt.Spec.EnableAI = false + Expect(k8sClient.Update(ctx, k8sgpt)).Should(Succeed()) + }) + }) + + Context("Getting k8sgpt CRDs", func() { + It("Should get k8sgpt CRDs with the latest value", func() { + By("Getting k8sgpt CRDs") + k8sgpt := &corev1alpha1.K8sGPT{} + namespace := "default" + Expect(k8sClient.Get(ctx, types.NamespacedName{Name: "default", Namespace: namespace}, k8sgpt)).Should(Succeed()) + Expect(k8sgpt.Spec.EnableAI).Should(BeFalse()) + }) + }) + + Context("Deleting k8sgpt CRDs", func() { + It("Should delete k8sgpt CRDs", func() { + By("Deleting k8sgpt CRDs") + k8sgpt := &corev1alpha1.K8sGPT{} + namespace := "default" + Expect(k8sClient.Get(ctx, types.NamespacedName{Name: "default", Namespace: namespace}, k8sgpt)).Should(Succeed()) + Expect(k8sClient.Delete(ctx, k8sgpt)).Should(Succeed()) + }) + }) +}) diff --git a/controllers/suite_test.go b/controllers/suite_test.go index 619a9b39..c5a3120a 100644 --- a/controllers/suite_test.go +++ b/controllers/suite_test.go @@ -14,14 +14,21 @@ limitations under the License. package controllers import ( + "os" "path/filepath" "testing" + "time" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "k8s.io/client-go/kubernetes/scheme" + kclient "github.com/k8sgpt-ai/k8sgpt-operator/pkg/client" + apiv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + k8sscheme "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" + ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/envtest" logf "sigs.k8s.io/controller-runtime/pkg/log" @@ -34,9 +41,11 @@ import ( // These tests use Ginkgo (BDD-style Go testing framework). Refer to // http://onsi.github.io/ginkgo/ to learn more about Ginkgo. -var cfg *rest.Config -var k8sClient client.Client -var testEnv *envtest.Environment +var ( + k8sClient client.Client + testEnv *envtest.Environment + mgr ctrl.Manager +) func TestAPIs(t *testing.T) { RegisterFailHandler(Fail) @@ -44,34 +53,88 @@ func TestAPIs(t *testing.T) { RunSpecs(t, "Controller Suite") } -var _ = BeforeSuite(func() { +var _ = BeforeSuite(func(ctx SpecContext) { logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) By("bootstrapping test environment") + timeout := 3 * time.Minute testEnv = &envtest.Environment{ - CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")}, - ErrorIfCRDPathMissing: true, + CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")}, + ErrorIfCRDPathMissing: true, + ControlPlaneStartTimeout: timeout, + ControlPlaneStopTimeout: timeout, + AttachControlPlaneOutput: false, } + var cfg *rest.Config var err error - // cfg is defined in this file globally. - cfg, err = testEnv.Start() + // this is a channel to signal when the test environment is ready + done := make(chan interface{}) + go func() { + // this will block until the test environment is ready + defer GinkgoRecover() + cfg, err = testEnv.Start() + close(done) + }() + // wait for the test environment to be ready + Eventually(done).WithContext(ctx).WithTimeout(timeout).Should(BeClosed()) Expect(err).NotTo(HaveOccurred()) Expect(cfg).NotTo(BeNil()) - err = corev1alpha1.AddToScheme(scheme.Scheme) - Expect(err).NotTo(HaveOccurred()) + scheme := runtime.NewScheme() + Expect(corev1alpha1.AddToScheme(scheme)).To(Succeed()) + Expect(k8sscheme.AddToScheme(scheme)).To(Succeed()) + Expect(apiv1.AddToScheme(scheme)).To(Succeed()) //+kubebuilder:scaffold:scheme - k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme}) Expect(err).NotTo(HaveOccurred()) Expect(k8sClient).NotTo(BeNil()) + By("Creating controller manager") + mgr, err = ctrl.NewManager(cfg, ctrl.Options{ + Scheme: scheme, + MetricsBindAddress: "0", + LeaderElection: false, + Port: 8443, + }) + Expect(err).ToNot(HaveOccurred()) + Expect(mgr).ToNot(BeNil()) + + kc, err := kclient.NewClient("localhost:50051") + Expect(err).ToNot(HaveOccurred()) + + kcs := map[string]*kclient.Client{ + "localhost:50051": kc, + } + + By("Creatng the controllers") + k8sGPTController := &K8sGPTReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + K8sGPTClient: kc, + k8sGPTClients: kcs, + } + Expect(k8sGPTController.SetupWithManager(mgr)).To(Succeed()) + + go func() { + defer GinkgoRecover() + ctrl.Log.Info("Starting the manager") + Expect(mgr.Start(ctrl.SetupSignalHandler())).To(Succeed()) + }() + + crd := &apiv1.CustomResourceDefinition{} + Expect(k8sClient.Get(ctx, types.NamespacedName{Name: "k8sgpts.core.k8sgpt.ai"}, crd)).To(Succeed()) + Expect(k8sClient.Get(ctx, types.NamespacedName{Name: "results.core.k8sgpt.ai"}, crd)).To(Succeed()) + }) var _ = AfterSuite(func() { By("tearing down the test environment") err := testEnv.Stop() - Expect(err).NotTo(HaveOccurred()) + // Here we need to exit directly because the testEnv.Stop() may hang forever in some cases. + if err != nil { + os.Exit(1) + } })