From a7cb7c6917812071cdeb7a6b19926a7c43166d93 Mon Sep 17 00:00:00 2001 From: Shubham Singh Date: Sun, 11 Aug 2024 04:27:02 +0000 Subject: [PATCH] Test coverage for csidriver Signed-off-by: GitHub Test coverage for csidriver Signed-off-by: Shubham Singh --- cloud/pkg/csidriver/controllerserver_test.go | 173 +++++++++++++++++ cloud/pkg/csidriver/identityserver_test.go | 116 ++++++++++++ cloud/pkg/csidriver/uds_test.go | 39 ++++ cloud/pkg/csidriver/utils_test.go | 189 +++++++++++++++++++ 4 files changed, 517 insertions(+) create mode 100644 cloud/pkg/csidriver/controllerserver_test.go create mode 100644 cloud/pkg/csidriver/identityserver_test.go create mode 100644 cloud/pkg/csidriver/uds_test.go create mode 100644 cloud/pkg/csidriver/utils_test.go diff --git a/cloud/pkg/csidriver/controllerserver_test.go b/cloud/pkg/csidriver/controllerserver_test.go new file mode 100644 index 00000000000..618c24f7a6d --- /dev/null +++ b/cloud/pkg/csidriver/controllerserver_test.go @@ -0,0 +1,173 @@ +/* +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 csidriver + +import ( + "testing" + + "github.com/container-storage-interface/spec/lib/go/csi" + "github.com/stretchr/testify/assert" + "golang.org/x/net/context" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func TestNewControllerServer(t *testing.T) { + assert := assert.New(t) + + nodeID := "test-node" + kubeEdgeEndpoint := "http://localhost:8080/test" + + cs := newControllerServer(nodeID, kubeEdgeEndpoint) + assert.NotNil(cs) + + assert.Equal(nodeID, cs.nodeID) + assert.Equal(kubeEdgeEndpoint, cs.kubeEdgeEndpoint) + + expectedCaps := getControllerServiceCapabilities( + []csi.ControllerServiceCapability_RPC_Type{ + csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME, + csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME, + }) + assert.Equal(expectedCaps, cs.caps) + + assert.Equal(csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME, + cs.caps[0].GetRpc().GetType()) + assert.Equal(csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME, + cs.caps[1].GetRpc().GetType()) +} + +func TestValidateVolumeCapabilities(t *testing.T) { + assert := assert.New(t) + + cs := &controllerServer{ + nodeID: "test-node", + kubeEdgeEndpoint: "http://localhost:8080/test", + } + + // Test case 1: Invalid request (missing volume ID) + invalidReq := &csi.ValidateVolumeCapabilitiesRequest{ + VolumeCapabilities: []*csi.VolumeCapability{ + { + AccessType: &csi.VolumeCapability_Mount{ + Mount: &csi.VolumeCapability_MountVolume{}, + }, + AccessMode: &csi.VolumeCapability_AccessMode{ + Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, + }, + }, + }, + } + + result, err := cs.ValidateVolumeCapabilities(context.Background(), invalidReq) + assert.Error(err) + assert.Nil(result) + assert.Equal(codes.InvalidArgument, status.Code(err)) + assert.Contains(err.Error(), "Volume ID cannot be empty") + + // Test case 2: Invalid request (missing volume capabilities) + invalidReq2 := &csi.ValidateVolumeCapabilitiesRequest{ + VolumeId: "test-volume-id", + } + + result, err = cs.ValidateVolumeCapabilities(context.Background(), invalidReq2) + assert.Error(err) + assert.Nil(result) + assert.Equal(codes.InvalidArgument, status.Code(err)) + assert.Contains(err.Error(), "test-volume-id") + + // Test case 3: Valid request + validReq := &csi.ValidateVolumeCapabilitiesRequest{ + VolumeId: "test-volume-id", + VolumeCapabilities: []*csi.VolumeCapability{ + { + AccessType: &csi.VolumeCapability_Mount{ + Mount: &csi.VolumeCapability_MountVolume{}, + }, + AccessMode: &csi.VolumeCapability_AccessMode{ + Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, + }, + }, + }, + } + + result, err = cs.ValidateVolumeCapabilities(context.Background(), validReq) + assert.NoError(err) + assert.NotNil(result) + assert.NotNil(result.Confirmed) + assert.NotEmpty(result.Confirmed.VolumeCapabilities) + assert.Equal(csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, + result.Confirmed.VolumeCapabilities[0].AccessMode.Mode) +} + +func TestControllerGetCapabilities(t *testing.T) { + assert := assert.New(t) + + cs := &controllerServer{ + nodeID: "test-node", + kubeEdgeEndpoint: "http://localhost:8080/test", + caps: getControllerServiceCapabilities( + []csi.ControllerServiceCapability_RPC_Type{ + csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME, + csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME, + }, + ), + } + + req := &csi.ControllerGetCapabilitiesRequest{} + resp, err := cs.ControllerGetCapabilities(context.Background(), req) + assert.NoError(err) + assert.NotNil(resp) + + expectedCaps := []csi.ControllerServiceCapability_RPC_Type{ + csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME, + csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME, + } + + for i, cap := range resp.Capabilities { + assert.Equal(expectedCaps[i], cap.GetRpc().Type, + "Capability %d should be %v", i, expectedCaps[i]) + } +} + +func TestGetControllerServiceCapabilities(t *testing.T) { + assert := assert.New(t) + + // Test case 1: Empty capability list + emptyCaps := getControllerServiceCapabilities([]csi.ControllerServiceCapability_RPC_Type{}) + assert.Empty(emptyCaps) + + // Test case 2: One capability + singleCapType := csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME + singleCap := getControllerServiceCapabilities([]csi.ControllerServiceCapability_RPC_Type{singleCapType}) + + assert.Equal(singleCapType, singleCap[0].GetRpc().Type) + + // Test case 3: Multiple capabilities + multiCapTypes := []csi.ControllerServiceCapability_RPC_Type{ + csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME, + csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME, + csi.ControllerServiceCapability_RPC_LIST_VOLUMES, + } + multiCaps := getControllerServiceCapabilities(multiCapTypes) + + assert.Len(multiCaps, 3) + for i, capType := range multiCapTypes { + assert.Equal(capType, multiCaps[i].GetRpc().Type, + "Capability %d should be %v", i, capType) + } +} diff --git a/cloud/pkg/csidriver/identityserver_test.go b/cloud/pkg/csidriver/identityserver_test.go new file mode 100644 index 00000000000..fd0bb6fa1a2 --- /dev/null +++ b/cloud/pkg/csidriver/identityserver_test.go @@ -0,0 +1,116 @@ +/* +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 csidriver + +import ( + "context" + "testing" + + "github.com/container-storage-interface/spec/lib/go/csi" + "github.com/stretchr/testify/assert" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func TestNewIdentityServer(t *testing.T) { + assert := assert.New(t) + + name := "test-server" + version := "v1.0.0" + + ids := newIdentityServer(name, version) + + assert.NotNil(ids) + assert.Equal(name, ids.name) + assert.Equal(version, ids.version) +} + +func TestGetPluginInfo(t *testing.T) { + assert := assert.New(t) + + testCases := []struct { + name string + version string + expectError bool + errorCode codes.Code + }{ + { + name: "test-driver", + version: "v1.0.0", + expectError: false, + errorCode: codes.OK, + }, + { + name: "", + version: "v1.0.0", + expectError: true, + errorCode: codes.Unavailable, + }, + { + name: "test-driver", + version: "", + expectError: true, + errorCode: codes.Unavailable, + }, + { + name: "", + version: "", + expectError: true, + errorCode: codes.Unavailable, + }, + } + + for _, tc := range testCases { + ids := newIdentityServer(tc.name, tc.version) + result, err := ids.GetPluginInfo(context.Background(), &csi.GetPluginInfoRequest{}) + + if tc.expectError { + assert.Error(err) + assert.Equal(tc.errorCode, status.Code(err)) + } else { + assert.NoError(err) + assert.NotNil(result) + assert.Equal(tc.name, result.Name) + assert.Equal(tc.version, result.VendorVersion) + } + } +} + +func TestProbe(t *testing.T) { + assert := assert.New(t) + + ids := newIdentityServer("test-driver", "v1.0.0") + resp, err := ids.Probe(context.Background(), &csi.ProbeRequest{}) + + assert.NoError(err) + assert.NotNil(resp) +} + +func TestGetPluginCapabilities(t *testing.T) { + assert := assert.New(t) + + ids := newIdentityServer("test-driver", "v1.0.0") + result, err := ids.GetPluginCapabilities(context.Background(), &csi.GetPluginCapabilitiesRequest{}) + + assert.NoError(err) + assert.NotNil(result) + assert.Len(result.Capabilities, 1) + + capabilities := result.Capabilities[0] + assert.NotNil(capabilities.GetService()) + assert.Equal(csi.PluginCapability_Service_CONTROLLER_SERVICE, capabilities.GetService().Type) +} diff --git a/cloud/pkg/csidriver/uds_test.go b/cloud/pkg/csidriver/uds_test.go new file mode 100644 index 00000000000..dafd118beb9 --- /dev/null +++ b/cloud/pkg/csidriver/uds_test.go @@ -0,0 +1,39 @@ +/* +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 csidriver + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNewUnixDomainSocket(t *testing.T) { + assert := assert.New(t) + + // Using default buffer size + us := NewUnixDomainSocket("/tmp/test.sock") + assert.NotNil(us) + assert.Equal("/tmp/test.sock", us.filename) + assert.Equal(DefaultBufferSize, us.buffersize) + + // Using custom buffer size + us = NewUnixDomainSocket("/tmp/test.sock", 2048) + assert.NotNil(us) + assert.Equal("/tmp/test.sock", us.filename) + assert.Equal(2048, us.buffersize) +} diff --git a/cloud/pkg/csidriver/utils_test.go b/cloud/pkg/csidriver/utils_test.go new file mode 100644 index 00000000000..3fe5189edab --- /dev/null +++ b/cloud/pkg/csidriver/utils_test.go @@ -0,0 +1,189 @@ +/* +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 csidriver + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/kubeedge/beehive/pkg/core/model" +) + +func TestParseEndpoint(t *testing.T) { + assert := assert.New(t) + + testCases := []struct { + input string + expectedStrOne string + expectedStrTwo string + expectError bool + }{ + { + input: "unix:///tmp/test.sock", + expectedStrOne: "unix", + expectedStrTwo: "/tmp/test.sock", + expectError: false, + }, + { + input: "unix://tmp/test.sock", + expectedStrOne: "unix", + expectedStrTwo: "tmp/test.sock", + expectError: false, + }, + { + input: "tcp://127.0.0.1:8080", + expectedStrOne: "tcp", + expectedStrTwo: "127.0.0.1:8080", + expectError: false, + }, + { + input: "/tmp/test.sock", + expectedStrOne: "", + expectedStrTwo: "", + expectError: true, + }, + { + input: "unix://", + expectedStrOne: "", + expectedStrTwo: "", + expectError: true, + }, + } + + for _, tc := range testCases { + firstStr, secondStr, err := parseEndpoint(tc.input) + if tc.expectError { + assert.Error(err) + } else { + assert.NoError(err) + assert.Equal(tc.expectedStrOne, firstStr) + assert.Equal(tc.expectedStrTwo, secondStr) + } + } +} + +func TestBuildResource(t *testing.T) { + assert := assert.New(t) + + tests := []struct { + name string + nodeID string + namespace string + resourceType string + resourceID string + want string + wantErr bool + }{ + { + name: "Valid resource without resourceID", + nodeID: "node1", + namespace: "default", + resourceType: "volume", + resourceID: "", + want: "node/node1/default/volume", + wantErr: false, + }, + { + name: "Valid resource with resourceID", + nodeID: "node1", + namespace: "default", + resourceType: "volume", + resourceID: "vol1", + want: "node/node1/default/volume/vol1", + wantErr: false, + }, + { + name: "Resource without nodeID", + nodeID: "", + namespace: "default", + resourceType: "volume", + resourceID: "", + want: "", + wantErr: true, + }, + { + name: "Resource missing namespace", + nodeID: "node1", + namespace: "", + resourceType: "volume", + resourceID: "", + want: "", + wantErr: true, + }, + { + name: "Resource without resourceType", + nodeID: "node1", + namespace: "default", + resourceType: "", + resourceID: "", + want: "", + wantErr: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got, err := buildResource(test.nodeID, test.namespace, test.resourceType, test.resourceID) + if test.wantErr { + assert.Error(err) + } else { + assert.NoError(err) + assert.Equal(test.want, got) + } + }) + } +} + +func TestExtractMessage(t *testing.T) { + assert := assert.New(t) + + tests := []struct { + name string + context string + wantErr bool + }{ + { + name: "Valid JSON", + context: `{"header":{"namespace":"default"},"router":{"resource":"test"},"content":"test"}`, + wantErr: false, + }, + { + name: "Invalid JSON", + context: `{invalid json}`, + wantErr: true, + }, + { + name: "Empty context", + context: "", + wantErr: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + msg, err := extractMessage(test.context) + if test.wantErr { + assert.Error(err) + } else { + assert.NoError(err) + assert.NotNil(msg) + assert.IsType(&model.Message{}, msg) + } + }) + } +}