From b7b5ec6c23b968e33eb151ca61a126568dda73cb Mon Sep 17 00:00:00 2001 From: Shubham Singh Date: Fri, 11 Oct 2024 07:32:20 +0000 Subject: [PATCH] added tests for metamanager/client Signed-off-by: GitHub --- edge/pkg/metamanager/client/pod_test.go | 240 ++++++++++++++++++ edge/pkg/metamanager/client/podstatus_test.go | 114 +++++++++ edge/pkg/metamanager/client/secret_test.go | 139 ++++++++++ 3 files changed, 493 insertions(+) create mode 100644 edge/pkg/metamanager/client/pod_test.go create mode 100644 edge/pkg/metamanager/client/podstatus_test.go create mode 100644 edge/pkg/metamanager/client/secret_test.go diff --git a/edge/pkg/metamanager/client/pod_test.go b/edge/pkg/metamanager/client/pod_test.go new file mode 100644 index 00000000000..43adb5c0cd9 --- /dev/null +++ b/edge/pkg/metamanager/client/pod_test.go @@ -0,0 +1,240 @@ +/* +Copyright 2024 The KubeEdge Authors. + +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 client + +import ( + "encoding/json" + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/kubeedge/beehive/pkg/core/model" + "github.com/kubeedge/kubeedge/edge/pkg/common/modules" +) + +const ( + testNamespace = "test-namespace" +) + +func TestNewPods(t *testing.T) { + assert := assert.New(t) + + send := newSend() + + pods := newPods(testNamespace, send) + assert.NotNil(pods) + assert.Equal(namespace, pods.namespace) + assert.Equal(send, pods.send) +} + +func TestPods_Delete(t *testing.T) { + assert := assert.New(t) + + podName := "test-pod" + deleteOptions := metav1.DeleteOptions{} + + testCases := []struct { + name string + respFunc func(*model.Message) (*model.Message, error) + expectErr bool + }{ + { + name: "Delete Pod Success", + respFunc: func(message *model.Message) (*model.Message, error) { + resp := model.NewMessage(message.GetID()) + resp.Content = "OK" + return resp, nil + }, + expectErr: false, + }, + { + name: "Delete Pod Error", + respFunc: func(message *model.Message) (*model.Message, error) { + return nil, fmt.Errorf("test error") + }, + expectErr: true, + }, + } + + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + mockSend := &mockSendInterface{} + mockSend.sendSyncFunc = func(message *model.Message) (*model.Message, error) { + assert.Equal(modules.MetaGroup, message.GetGroup()) + assert.Equal(modules.EdgedModuleName, message.GetSource()) + assert.NotEmpty(message.GetID()) + assert.Equal(fmt.Sprintf("%s/%s/%s", testNamespace, model.ResourceTypePod, podName), message.GetResource()) + assert.Equal(model.DeleteOperation, message.GetOperation()) + + return test.respFunc(message) + } + + podsClient := newPods(testNamespace, mockSend) + + err := podsClient.Delete(podName, deleteOptions) + + if test.expectErr { + assert.Error(err) + } else { + assert.NoError(err) + } + }) + } +} + +func TestPods_Get(t *testing.T) { + assert := assert.New(t) + + podName := "test-pod" + expectedPod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: podName, + Namespace: testNamespace, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "test-container", + Image: "test-image", + }, + }, + }, + } + + testCases := []struct { + name string + respFunc func(*model.Message) (*model.Message, error) + stdResult *corev1.Pod + expectErr bool + }{ + { + name: "Get Pod Success", + respFunc: func(message *model.Message) (*model.Message, error) { + resp := model.NewMessage(message.GetID()) + podJSON, _ := json.Marshal(expectedPod) + resp.Content = []string{string(podJSON)} + return resp, nil + }, + stdResult: expectedPod, + expectErr: false, + }, + { + name: "Get Pod Error", + respFunc: func(message *model.Message) (*model.Message, error) { + return nil, fmt.Errorf("test error") + }, + stdResult: nil, + expectErr: true, + }, + } + + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + mockSend := &mockSendInterface{} + mockSend.sendSyncFunc = func(message *model.Message) (*model.Message, error) { + assert.Equal(modules.MetaGroup, message.GetGroup()) + assert.Equal(modules.EdgedModuleName, message.GetSource()) + assert.NotEmpty(message.GetID()) + assert.Equal(fmt.Sprintf("%s/%s/%s", testNamespace, model.ResourceTypePod, podName), message.GetResource()) + assert.Equal(model.QueryOperation, message.GetOperation()) + + return test.respFunc(message) + } + + podsClient := newPods(testNamespace, mockSend) + + pod, err := podsClient.Get(podName) + + if test.expectErr { + assert.Error(err) + assert.Nil(pod) + } else { + assert.NoError(err) + assert.Equal(test.stdResult, pod) + } + }) + } +} + +func TestHandlePodFromMetaDB(t *testing.T) { + assert := assert.New(t) + + testCases := []struct { + name string + content []byte + expectedPod *corev1.Pod + expectedErr bool + }{ + { + name: "Valid Pod", + content: []byte(`["{\"metadata\":{\"name\":\"test-pod\",\"namespace\":\"default\"},\"spec\":{\"containers\":[{\"name\":\"test-container\",\"image\":\"test-image\"}]}}"]`), + expectedPod: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-pod", + Namespace: "default", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "test-container", + Image: "test-image", + }, + }, + }, + }, + expectedErr: false, + }, + { + name: "Invalid JSON", + content: []byte(`["invalid json"]`), + expectedPod: nil, + expectedErr: true, + }, + { + name: "Empty list", + content: []byte(`[]`), + expectedPod: nil, + expectedErr: true, + }, + { + name: "Multiple Pods", + content: []byte(`["{}", "{}"]`), + expectedPod: nil, + expectedErr: true, + }, + } + + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + pod, err := handlePodFromMetaDB("test-pod", test.content) + + if test.expectedErr { + assert.Error(err) + assert.Nil(pod) + } else { + assert.NoError(err) + assert.Equal(test.expectedPod.ObjectMeta.Name, pod.ObjectMeta.Name) + assert.Equal(test.expectedPod.ObjectMeta.Namespace, pod.ObjectMeta.Namespace) + assert.Equal(test.expectedPod.Spec.Containers[0].Name, pod.Spec.Containers[0].Name) + assert.Equal(test.expectedPod.Spec.Containers[0].Image, pod.Spec.Containers[0].Image) + } + }) + } +} diff --git a/edge/pkg/metamanager/client/podstatus_test.go b/edge/pkg/metamanager/client/podstatus_test.go new file mode 100644 index 00000000000..ff61eec113d --- /dev/null +++ b/edge/pkg/metamanager/client/podstatus_test.go @@ -0,0 +1,114 @@ +/* +Copyright 2024 The KubeEdge Authors. + +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 client + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/kubeedge/beehive/pkg/core/model" + "github.com/kubeedge/kubeedge/common/constants" + edgeapi "github.com/kubeedge/kubeedge/common/types" + "github.com/kubeedge/kubeedge/edge/pkg/common/modules" +) + +func TestNewPodStatus(t *testing.T) { + assert := assert.New(t) + + namespace := "test-namespace" + s := newSend() + + ps := newPodStatus(namespace, s) + + assert.NotNil(ps) + assert.Equal(namespace, ps.namespace) + assert.IsType(&send{}, ps.send) +} + +func TestPodStatus_Update(t *testing.T) { + assert := assert.New(t) + + testCases := []struct { + name string + rsName string + podStatusReq edgeapi.PodStatusRequest + sendSyncResult *model.Message + sendSyncError error + expectedError error + }{ + { + name: "Successful Update", + rsName: "test-rs", + podStatusReq: edgeapi.PodStatusRequest{ + UID: "test-uid", + }, + sendSyncResult: &model.Message{ + Content: constants.MessageSuccessfulContent, + }, + sendSyncError: nil, + expectedError: nil, + }, + { + name: "SendSync Error", + rsName: "test-rs", + podStatusReq: edgeapi.PodStatusRequest{ + UID: "test-uid", + }, + sendSyncResult: nil, + sendSyncError: errors.New("send sync error"), + expectedError: errors.New("update podstatus failed, err: send sync error"), + }, + { + name: "Unsuccessful Update", + rsName: "test-rs", + podStatusReq: edgeapi.PodStatusRequest{ + UID: "test-uid", + }, + sendSyncResult: &model.Message{ + Content: errors.New("update failed"), + }, + sendSyncError: nil, + expectedError: errors.New("update failed"), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + mockSend := &mockSendInterface{ + sendSyncFunc: func(message *model.Message) (*model.Message, error) { + assert.Equal(modules.MetaGroup, message.GetGroup()) + assert.Equal(modules.EdgedModuleName, message.GetSource()) + assert.Equal(testNamespace+"/"+model.ResourceTypePodStatus+"/"+tc.rsName, message.GetResource()) + assert.Equal(model.UpdateOperation, message.GetOperation()) + + return tc.sendSyncResult, tc.sendSyncError + }, + } + + ps := newPodStatus(testNamespace, mockSend) + err := ps.Update(tc.rsName, tc.podStatusReq) + + if tc.expectedError != nil { + assert.EqualError(err, tc.expectedError.Error()) + } else { + assert.NoError(err) + } + }) + } +} diff --git a/edge/pkg/metamanager/client/secret_test.go b/edge/pkg/metamanager/client/secret_test.go new file mode 100644 index 00000000000..5dd0030d588 --- /dev/null +++ b/edge/pkg/metamanager/client/secret_test.go @@ -0,0 +1,139 @@ +/* +Copyright 2024 The KubeEdge Authors. + +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 client + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" + api "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestNewSecrets(t *testing.T) { + assert := assert.New(t) + + namespace := "test-namespace" + s := newSend() + + secret := newSecrets(namespace, s) + + assert.NotNil(secret) + assert.Equal(namespace, secret.namespace) + assert.IsType(&send{}, secret.send) +} + +func TestHandleSecretFromMetaDB(t *testing.T) { + assert := assert.New(t) + + // Test case 1: Valid Secret JSON in a single-element list + secret := &api.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-secret", + Namespace: "default", + }, + Data: map[string][]byte{ + "username": []byte("admin"), + "password": []byte("password123"), + }, + Type: api.SecretTypeOpaque, + } + secretJSON, _ := json.Marshal(secret) + content, _ := json.Marshal([]string{string(secretJSON)}) + + result, err := handleSecretFromMetaDB(content) + assert.NoError(err) + assert.Equal(secret, result) + + // Test case 2: Empty list + emptyList, _ := json.Marshal([]string{}) + + result, err = handleSecretFromMetaDB(emptyList) + assert.Error(err) + assert.Nil(result) + assert.Contains(err.Error(), "secret length from meta db is 0") + + // Test case 3: List with multiple elements + multipleSecrets, _ := json.Marshal([]string{string(secretJSON), string(secretJSON)}) + + result, err = handleSecretFromMetaDB(multipleSecrets) + assert.Error(err) + assert.Nil(result) + assert.Contains(err.Error(), "secret length from meta db is 2") + + // Test case 4: Invalid JSON in the list + invalidJSON := []byte(`["{invalid json}"]`) + + result, err = handleSecretFromMetaDB(invalidJSON) + assert.Error(err) + assert.Nil(result) + assert.Contains(err.Error(), "unmarshal message to secret from db failed") + + // Test case 5: Invalid outer JSON (not a list) + invalidOuterJSON := []byte(`{"not": "a list"}`) + + result, err = handleSecretFromMetaDB(invalidOuterJSON) + assert.Error(err) + assert.Nil(result) + assert.Contains(err.Error(), "unmarshal message to secret list from db failed") +} + +func TestHandleSecretFromMetaManager(t *testing.T) { + assert := assert.New(t) + + // Test case 1: Valid Secret JSON + secret := &api.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-secret", + Namespace: "default", + }, + Data: map[string][]byte{ + "username": []byte("admin"), + "password": []byte("password123"), + }, + Type: api.SecretTypeOpaque, + } + content, _ := json.Marshal(secret) + + result, err := handleSecretFromMetaManager(content) + assert.NoError(err) + assert.Equal(secret, result) + + // Test case 2: Empty JSON + emptyContent := []byte("{}") + + result, err = handleSecretFromMetaManager(emptyContent) + assert.NoError(err) + assert.Equal(&api.Secret{}, result) + + // Test case 3: Invalid JSON + invalidContent := []byte(`{"invalid": json}`) + + result, err = handleSecretFromMetaManager(invalidContent) + assert.Error(err) + assert.Nil(result) + assert.Contains(err.Error(), "unmarshal message to secret failed") + + // Test case 4: Partial Secret JSON + partialSecret := []byte(`{"metadata": {"name": "partial-secret"}}`) + + result, err = handleSecretFromMetaManager(partialSecret) + assert.NoError(err) + assert.Equal("partial-secret", result.Name) + assert.Nil(result.Data) +}