*
* Copyright (c) 2025 Bocloud Technologies Co., Ltd.
* installer is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*
*/
package main
import (
"context"
"errors"
"io"
"strings"
"testing"
"github.com/agiledragon/gomonkey/v2"
"github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/restmapper"
ctrl "sigs.k8s.io/controller-runtime"
crdembed "gopkg.openfuyao.cn/cluster-api-provider-bke/config"
)
func TestEnableCrdHasInstalledClientSetError(t *testing.T) {
patches := gomonkey.ApplyFuncReturn(ctrl.GetConfigOrDie, nil)
defer patches.Reset()
patches.ApplyFuncReturn(kubernetes.NewForConfig, nil, errors.New("clientset error"))
err := enableCrdHasInstalled()
assert.Error(t, err)
}
func TestInstallCrdOpenFileError(t *testing.T) {
patches := gomonkey.ApplyMethod(crdembed.CRDs, "Open", func(_ interface{}, name string) (io.ReadCloser, error) {
return nil, errors.New("open error")
})
defer patches.Reset()
err := installCrd(&kubernetes.Clientset{}, nil)
assert.Error(t, err)
}
func TestInstallCrdGetAPIGroupError(t *testing.T) {
patches := gomonkey.ApplyMethod(crdembed.CRDs, "Open", func(_ interface{}, name string) (io.ReadCloser, error) {
return io.NopCloser(strings.NewReader("")), nil
})
defer patches.Reset()
patches.ApplyFunc(restmapper.GetAPIGroupResources, func(d interface{}) ([]*restmapper.APIGroupResources, error) {
return nil, errors.New("api error")
})
err := installCrd(&kubernetes.Clientset{}, nil)
assert.Error(t, err)
}
func TestInstallCrdDecodeEOF(t *testing.T) {
patches := gomonkey.ApplyMethod(crdembed.CRDs, "Open", func(_ interface{}, name string) (io.ReadCloser, error) {
return io.NopCloser(strings.NewReader("")), nil
})
defer patches.Reset()
patches.ApplyFunc(restmapper.GetAPIGroupResources, func(d interface{}) ([]*restmapper.APIGroupResources, error) {
return []*restmapper.APIGroupResources{}, nil
})
err := installCrd(&kubernetes.Clientset{}, nil)
assert.NoError(t, err)
}
func TestInstallCrdDecodeError(t *testing.T) {
patches := gomonkey.ApplyMethod(crdembed.CRDs, "Open", func(_ interface{}, name string) (io.ReadCloser, error) {
return io.NopCloser(strings.NewReader("invalid yaml content")), nil
})
defer patches.Reset()
patches.ApplyFunc(restmapper.GetAPIGroupResources, func(d interface{}) ([]*restmapper.APIGroupResources, error) {
return []*restmapper.APIGroupResources{}, nil
})
err := installCrd(&kubernetes.Clientset{}, nil)
assert.Error(t, err)
}
func TestInstallCrdUnstructuredDecodeError(t *testing.T) {
yamlContent := "apiVersion: v1\nkind: Pod"
patches := gomonkey.ApplyMethod(crdembed.CRDs, "Open", func(_ interface{}, name string) (io.ReadCloser, error) {
return io.NopCloser(strings.NewReader(yamlContent)), nil
})
defer patches.Reset()
patches.ApplyFunc(restmapper.GetAPIGroupResources, func(d interface{}) ([]*restmapper.APIGroupResources, error) {
return []*restmapper.APIGroupResources{}, nil
})
patches.ApplyFunc(unstructured.UnstructuredJSONScheme.Decode, func(data []byte, defaults *schema.GroupVersionKind, into runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
return nil, nil, errors.New("decode error")
})
err := installCrd(&kubernetes.Clientset{}, nil)
assert.Error(t, err)
}
func TestInstallCrdToUnstructuredError(t *testing.T) {
yamlContent := "apiVersion: v1\nkind: Pod"
patches := gomonkey.ApplyMethod(crdembed.CRDs, "Open", func(_ interface{}, name string) (io.ReadCloser, error) {
return io.NopCloser(strings.NewReader(yamlContent)), nil
})
defer patches.Reset()
patches.ApplyFunc(restmapper.GetAPIGroupResources, func(d interface{}) ([]*restmapper.APIGroupResources, error) {
return []*restmapper.APIGroupResources{}, nil
})
patches.ApplyFunc(runtime.DefaultUnstructuredConverter.ToUnstructured, func(obj interface{}) (map[string]interface{}, error) {
return nil, errors.New("convert error")
})
err := installCrd(&kubernetes.Clientset{}, &mockDynamicClient{})
assert.Error(t, err)
}
func TestInstallCrdCreateError(t *testing.T) {
yamlContent := "apiVersion: v1\nkind: Pod\nmetadata:\n name: test"
patches := gomonkey.ApplyMethod(crdembed.CRDs, "Open", func(_ interface{}, name string) (io.ReadCloser, error) {
return io.NopCloser(strings.NewReader(yamlContent)), nil
})
defer patches.Reset()
patches.ApplyFunc(restmapper.GetAPIGroupResources, func(d interface{}) ([]*restmapper.APIGroupResources, error) {
return []*restmapper.APIGroupResources{}, nil
})
mockDynamic := &mockDynamicClient{createErr: errors.New("create error")}
err := installCrd(&kubernetes.Clientset{}, mockDynamic)
assert.Error(t, err)
}
type mockDynamicClient struct {
createErr error
}
func (m *mockDynamicClient) Resource(resource schema.GroupVersionResource) dynamic.NamespaceableResourceInterface {
return &mockResourceInterface{createErr: m.createErr}
}
type mockResourceInterface struct {
createErr error
}
func (m *mockResourceInterface) Namespace(string) dynamic.ResourceInterface { return m }
func (m *mockResourceInterface) Create(ctx context.Context, obj *unstructured.Unstructured, opts metav1.CreateOptions, subresources ...string) (*unstructured.Unstructured, error) {
if m.createErr != nil {
return nil, m.createErr
}
return &unstructured.Unstructured{}, nil
}
func (m *mockResourceInterface) Update(context.Context, *unstructured.Unstructured, metav1.UpdateOptions, ...string) (*unstructured.Unstructured, error) {
return nil, nil
}
func (m *mockResourceInterface) UpdateStatus(context.Context, *unstructured.Unstructured, metav1.UpdateOptions) (*unstructured.Unstructured, error) {
return nil, nil
}
func (m *mockResourceInterface) Delete(context.Context, string, metav1.DeleteOptions, ...string) error {
return nil
}
func (m *mockResourceInterface) DeleteCollection(context.Context, metav1.DeleteOptions, metav1.ListOptions) error {
return nil
}
func (m *mockResourceInterface) Get(context.Context, string, metav1.GetOptions, ...string) (*unstructured.Unstructured, error) {
return nil, nil
}
func (m *mockResourceInterface) List(context.Context, metav1.ListOptions) (*unstructured.UnstructuredList, error) {
return nil, nil
}
func (m *mockResourceInterface) Watch(context.Context, metav1.ListOptions) (watch.Interface, error) {
return nil, nil
}
func (m *mockResourceInterface) Patch(context.Context, string, types.PatchType, []byte, metav1.PatchOptions, ...string) (*unstructured.Unstructured, error) {
return nil, nil
}
func (m *mockResourceInterface) Apply(context.Context, string, *unstructured.Unstructured, metav1.ApplyOptions, ...string) (*unstructured.Unstructured, error) {
return nil, nil
}
func (m *mockResourceInterface) ApplyStatus(context.Context, string, *unstructured.Unstructured, metav1.ApplyOptions) (*unstructured.Unstructured, error) {
return nil, nil
}