Repository: kubernetes-csi/csi-driver-smb
Pull request #55 (2020-07-09 04:36:25)

test: Add unit test for nodeserver
This commit adds unit test for nodeserver.
A fake mounter is also added to support the
unit tests.

Redundant checks for target path in Publish
Volume has been removed. A typo is also
fixed in nodeserver.

Signed-off-by: Animesh Kumar <animuz111@gmail.com>

<!--  Thanks for sending a pull request!  Here are some tips for you:

1. If this is your first time, please read our contributor guidelines: https://git.k8s.io/community/contributors/guide#your-first-contribution and developer guide https://git.k8s.io/community/contributors/devel/development.md#development-guide
2. Please label this pull request according to what type of issue you are addressing, especially if this is a release targeted pull request. For reference on required PR/issue labels, read here:
https://git.k8s.io/community/contributors/devel/release.md#issue-kind-label
3. Ensure you have added or ran the appropriate tests for your PR: https://git.k8s.io/community/contributors/devel/testing.md
4. If you want *faster* PR reviews, read how: https://git.k8s.io/community/contributors/guide/pull-requests.md#best-practices-for-faster-reviews
5. Follow the instructions for writing a release note: https://git.k8s.io/community/contributors/guide/release-notes.md
6. If the PR is unfinished, see how to mark it: https://git.k8s.io/community/contributors/guide/pull-requests.md#marking-unfinished-pull-requests
-->

**What type of PR is this?**
> Uncomment only one ` /kind <>` line, hit enter to put that in a new line, and remove leading whitespaces from that line:
>
> /kind api-change
> /kind bug
> /kind cleanup
> /kind design
> /kind documentation

> /kind failing-test
> /kind feature
> /kind flake

/kind test

**What this PR does / why we need it**:
Adds unit tests for nodeserver
**Which issue(s) this PR fixes**:
<!-- 
*Automatically closes linked issue when PR is merged.
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
_If PR is about `failing-tests or flakes`, please post the related issues/tests in a comment and do not use `Fixes`_*
-->
Fixes #

**Requirements**:
- [x] uses [conventional commit messages](https://www.conventionalcommits.org/)
  <!-- Common commit types:
        build: Build 🏭
        chore: Maintenance 🔧
        ci: Continuous Integration 💜
        docs: Documentation 📘
        feat: Features 🌈
        fix: Bug Fixes 🐞
        perf: Performance Improvements 🚀
        refactor: Code Refactoring 💎
        revert: Revert Change ◀️
        style: Code Style 🎶
        security: Security Fix 🛡️
        test: Testing 💚 -->
- [ ] includes documentation
- [x] adds unit tests
- [ ] tested upgrade from previous version

**Special notes for your reviewer**:
Multiple checks for target path has been removed from NodePublishVolume has been removed.
Similar to: https://github.com/kubernetes-sigs/azurefile-csi-driver/pull/287

**Release note**:
```
test: Add unit test for nodeserver
```



Patch:
diff --git a/pkg/smb/fake_mounter.go b/pkg/smb/fake_mounter.go
new file mode 100644
index 000000000..50dcb3b94
--- /dev/null
+++ b/pkg/smb/fake_mounter.go
@@ -0,0 +1,61 @@
+/*
+Copyright 2020 The Kubernetes 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 smb
+
+import (
+	"fmt"
+	"strings"
+
+	"k8s.io/utils/mount"
+)
+
+type fakeMounter struct {
+	mount.FakeMounter
+}
+
+// Mount overrides mount.FakeMounter.Mount.
+func (f *fakeMounter) Mount(source string, target string, fstype string, options []string) error {
+	if strings.Contains(source, "error_mount") {
+		return fmt.Errorf("fake Mount: source error")
+	} else if strings.Contains(target, "error_mount") {
+		return fmt.Errorf("fake Mount: target error")
+	}
+
+	return nil
+}
+
+// MountSensitive overrides mount.FakeMounter.MountSensitive.
+func (f *fakeMounter) MountSensitive(source string, target string, fstype string, options []string, sensitiveOptions []string) error {
+	if strings.Contains(source, "error_mount_sens") {
+		return fmt.Errorf("fake MountSensitive: source error")
+	} else if strings.Contains(target, "error_mount_sens") {
+		return fmt.Errorf("fake MountSensitive: target error")
+	}
+
+	return nil
+}
+
+//IsLikelyNotMountPoint overrides mount.FakeMounter.IsLikelyNotMountPoint.
+func (f *fakeMounter) IsLikelyNotMountPoint(file string) (bool, error) {
+	if strings.Contains(file, "error_is_likely") {
+		return false, fmt.Errorf("fake IsLikelyNotMountPoint: fake error")
+	}
+	if strings.Contains(file, "false_is_likely") {
+		return false, nil
+	}
+	return true, nil
+}
diff --git a/pkg/smb/nodeserver.go b/pkg/smb/nodeserver.go
index f11655b76..415393297 100644
--- a/pkg/smb/nodeserver.go
+++ b/pkg/smb/nodeserver.go
@@ -53,8 +53,10 @@ func (d *Driver) NodePublishVolume(ctx context.Context, req *csi.NodePublishVolu
 	if len(req.GetVolumeId()) == 0 {
 		return nil, status.Error(codes.InvalidArgument, "Volume ID missing in request")
 	}
-	if len(req.GetTargetPath()) == 0 {
-		return nil, status.Error(codes.InvalidArgument, "Target path missing in request")
+
+	target := req.GetTargetPath()
+	if len(target) == 0 {
+		return nil, status.Error(codes.InvalidArgument, "Target path not provided")
 	}
 
 	source := req.GetStagingTargetPath()
@@ -62,11 +64,6 @@ func (d *Driver) NodePublishVolume(ctx context.Context, req *csi.NodePublishVolu
 		return nil, status.Error(codes.InvalidArgument, "Staging target not provided")
 	}
 
-	target := req.GetTargetPath()
-	if len(target) == 0 {
-		return nil, status.Error(codes.InvalidArgument, "Target path not provided")
-	}
-
 	mountOptions := []string{"bind"}
 	if req.GetReadonly() {
 		mountOptions = append(mountOptions, "ro")
@@ -227,7 +224,7 @@ func (d *Driver) NodeUnstageVolume(ctx context.Context, req *csi.NodeUnstageVolu
 
 	klog.V(2).Infof("NodeUnstageVolume: CleanupMountPoint %s", stagingTargetPath)
 	if err := CleanupSMBMountPoint(d.mounter, stagingTargetPath, false); err != nil {
-		return nil, status.Errorf(codes.Internal, "failed to unmount staing target %q: %v", stagingTargetPath, err)
+		return nil, status.Errorf(codes.Internal, "failed to unmount staging target %q: %v", stagingTargetPath, err)
 	}
 
 	klog.V(2).Infof("NodeUnstageVolume: unmount %s successfully", stagingTargetPath)


Test patch:
diff --git a/pkg/smb/fake_mounter_test.go b/pkg/smb/fake_mounter_test.go
new file mode 100644
index 000000000..1156c57d8
--- /dev/null
+++ b/pkg/smb/fake_mounter_test.go
@@ -0,0 +1,140 @@
+/*
+Copyright 2020 The Kubernetes 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 smb
+
+import (
+	"fmt"
+	"reflect"
+	"testing"
+
+	"k8s.io/utils/mount"
+)
+
+func TestMount(t *testing.T) {
+	tests := []struct {
+		desc        string
+		source      string
+		target      string
+		expectedErr error
+	}{
+		{
+			desc:        "[Error] Mocked source error",
+			source:      "./error_mount_source",
+			target:      targetTest,
+			expectedErr: fmt.Errorf("fake Mount: source error"),
+		},
+		{
+			desc:        "[Error] Mocked target error",
+			source:      sourceTest,
+			target:      "./error_mount_target",
+			expectedErr: fmt.Errorf("fake Mount: target error"),
+		},
+		{
+			desc:        "[Success] Successful run",
+			source:      sourceTest,
+			target:      targetTest,
+			expectedErr: nil,
+		},
+	}
+
+	d := NewFakeDriver()
+	fakeMounter := &fakeMounter{}
+	d.mounter = &mount.SafeFormatAndMount{
+		Interface: fakeMounter,
+	}
+	for _, test := range tests {
+		err := d.mounter.Mount(test.source, test.target, "", nil)
+		if !reflect.DeepEqual(err, test.expectedErr) {
+			t.Errorf("Unexpected error: %v", err)
+		}
+	}
+}
+
+func TestMountSensitive(t *testing.T) {
+	tests := []struct {
+		desc        string
+		source      string
+		target      string
+		expectedErr error
+	}{
+		{
+			desc:        "[Error] Mocked source error",
+			source:      "./error_mount_sens_source",
+			target:      targetTest,
+			expectedErr: fmt.Errorf("fake MountSensitive: source error"),
+		},
+		{
+			desc:        "[Error] Mocked target error",
+			source:      sourceTest,
+			target:      "./error_mount_sens_target",
+			expectedErr: fmt.Errorf("fake MountSensitive: target error"),
+		},
+		{
+			desc:        "[Success] Successful run",
+			source:      sourceTest,
+			target:      targetTest,
+			expectedErr: nil,
+		},
+	}
+
+	d := NewFakeDriver()
+	fakeMounter := &fakeMounter{}
+	d.mounter = &mount.SafeFormatAndMount{
+		Interface: fakeMounter,
+	}
+	for _, test := range tests {
+		err := d.mounter.MountSensitive(test.source, test.target, "", nil, nil)
+		if !reflect.DeepEqual(err, test.expectedErr) {
+			t.Errorf("Unexpected error: %v", err)
+		}
+	}
+}
+
+func TestIsLikelyNotMountPoint(t *testing.T) {
+	tests := []struct {
+		desc        string
+		file        string
+		expectedErr error
+	}{
+		{
+			desc:        "[Error] Mocked file error",
+			file:        "./error_is_likely_target",
+			expectedErr: fmt.Errorf("fake IsLikelyNotMountPoint: fake error"),
+		},
+		{desc: "[Success] Successful run",
+			file:        targetTest,
+			expectedErr: nil,
+		},
+		{
+			desc:        "[Success] Successful run not a mount",
+			file:        "./false_is_likely_target",
+			expectedErr: nil,
+		},
+	}
+
+	d := NewFakeDriver()
+	fakeMounter := &fakeMounter{}
+	d.mounter = &mount.SafeFormatAndMount{
+		Interface: fakeMounter,
+	}
+	for _, test := range tests {
+		_, err := d.mounter.IsLikelyNotMountPoint(test.file)
+		if !reflect.DeepEqual(err, test.expectedErr) {
+			t.Errorf("Unexpected error: %v", err)
+		}
+	}
+}
diff --git a/pkg/smb/nodeserver_test.go b/pkg/smb/nodeserver_test.go
index a04cba2f6..0f6d077a4 100644
--- a/pkg/smb/nodeserver_test.go
+++ b/pkg/smb/nodeserver_test.go
@@ -17,71 +17,117 @@ limitations under the License.
 package smb
 
 import (
-	"bytes"
 	"context"
-	"flag"
+	"errors"
+	"fmt"
+	"os"
 	"reflect"
+	"syscall"
 	"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"
-	"k8s.io/klog"
+	"k8s.io/utils/mount"
+)
+
+const (
+	sourceTest = "./source_test"
+	targetTest = "./target_test"
 )
 
 func TestNodeStageVolume(t *testing.T) {
-	klog.InitFlags(nil)
-	if e := flag.Set("logtostderr", "false"); e != nil {
-		t.Error(e)
-	}
-	if e := flag.Set("alsologtostderr", "false"); e != nil {
-		t.Error(e)
-	}
-	if e := flag.Set("v", "100"); e != nil {
-		t.Error(e)
+	stdVolCap := csi.VolumeCapability{
+		AccessType: &csi.VolumeCapability_Mount{
+			Mount: &csi.VolumeCapability_MountVolume{},
+		},
 	}
-	flag.Parse()
 
-	buf := new(bytes.Buffer)
-	klog.SetOutput(buf)
+	errorMountSensSource := "./error_mount_sens_source"
+	smbFile := "./smb.go"
 
-	d := NewFakeDriver()
+	volContext := map[string]string{
+		sourceField: "test_source",
+	}
+	secrets := map[string]string{
+		usernameField: "test_username",
+		passwordField: "test_password",
+		domainField:   "test_doamin",
+	}
 
 	tests := []struct {
-		name   string
-		req    *csi.NodeStageVolumeRequest
-		expStr string
+		desc        string
+		req         csi.NodeStageVolumeRequest
+		expectedErr error
 	}{
 		{
-			"with secrets",
-			&csi.NodeStageVolumeRequest{
-				VolumeId: "vol_1",
-				Secrets: map[string]string{
-					"password": "testpassword",
-					"username": "testuser",
-				},
-				VolumeCapability: &csi.VolumeCapability{},
-				XXX_sizecache:    100,
-			},
-			`NodeStageVolume called with request {vol_1 map[]   map[password:**** username:testuser] map[] {} [] 100}`,
+			desc:        "[Error] Volume ID missing",
+			req:         csi.NodeStageVolumeRequest{},
+			expectedErr: status.Error(codes.InvalidArgument, "Volume ID missing in request"),
+		},
+		{
+			desc:        "[Error] Volume capabilities missing",
+			req:         csi.NodeStageVolumeRequest{VolumeId: "vol_1"},
+			expectedErr: status.Error(codes.InvalidArgument, "Volume capability not provided"),
+		},
+		{
+			desc:        "[Error] Stage target path missing",
+			req:         csi.NodeStageVolumeRequest{VolumeId: "vol_1", VolumeCapability: &stdVolCap},
+			expectedErr: status.Error(codes.InvalidArgument, "Staging target not provided"),
+		},
+		{
+			desc: "[Error] Source field is missing in context",
+			req: csi.NodeStageVolumeRequest{VolumeId: "vol_1", StagingTargetPath: sourceTest,
+				VolumeCapability: &stdVolCap},
+			expectedErr: status.Error(codes.InvalidArgument, "source field is missing, current context: map[]"),
+		},
+		{
+			desc: "[Error] Not a Directory",
+			req: csi.NodeStageVolumeRequest{VolumeId: "vol_1##", StagingTargetPath: smbFile,
+				VolumeCapability: &stdVolCap,
+				VolumeContext:    volContext,
+				Secrets:          secrets},
+			expectedErr: status.Error(codes.Internal, "MkdirAll ./smb.go failed with error: mkdir ./smb.go: not a directory"),
+		},
+		{
+			desc: "[Error] Failed SMB mount mocked by MountSensitive",
+			req: csi.NodeStageVolumeRequest{VolumeId: "vol_1##", StagingTargetPath: errorMountSensSource,
+				VolumeCapability: &stdVolCap,
+				VolumeContext:    volContext,
+				Secrets:          secrets},
+			expectedErr: status.Errorf(codes.Internal, "volume(vol_1##) mount \"test_source\" on \"./error_mount_sens_source\" failed with fake MountSensitive: target error"),
+		},
+		{
+			desc: "[Success] Valid request",
+			req: csi.NodeStageVolumeRequest{VolumeId: "vol_1##", StagingTargetPath: sourceTest,
+				VolumeCapability: &stdVolCap,
+				VolumeContext:    volContext,
+				Secrets:          secrets},
+			expectedErr: nil,
 		},
 	}
 
-	for _, test := range tests {
-		t.Run(test.name, func(t *testing.T) {
-			// EXECUTE
-			_, _ = d.NodeStageVolume(context.Background(), test.req)
-			klog.Flush()
+	// Setup
+	d := NewFakeDriver()
 
-			//ASSERT
-			assert.Contains(t, buf.String(), test.expStr)
+	for _, test := range tests {
+		fakeMounter := &fakeMounter{}
+		d.mounter = &mount.SafeFormatAndMount{
+			Interface: fakeMounter,
+		}
 
-			// CLEANUP
-			buf.Reset()
-		})
+		_, err := d.NodeStageVolume(context.Background(), &test.req)
+		if !reflect.DeepEqual(err, test.expectedErr) {
+			t.Errorf("test case: %s, Unexpected error: %v", test.desc, err)
+		}
 	}
 
+	// Clean up
+	err := os.RemoveAll(sourceTest)
+	assert.NoError(t, err)
+	err = os.RemoveAll(errorMountSensSource)
+	assert.NoError(t, err)
 }
 
 func TestNodeGetInfo(t *testing.T) {
@@ -132,3 +178,285 @@ func TestNodeExpandVolume(t *testing.T) {
 		t.Errorf("Unexpected error: %v", err)
 	}
 }
+
+func TestNodePublishVolume(t *testing.T) {
+	volumeCap := csi.VolumeCapability_AccessMode{Mode: csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER}
+	errorMountSource := "./error_mount_source"
+	alreadyMountedTarget := "./false_is_likely_exist_target"
+	smbFile := "./smb.go"
+
+	tests := []struct {
+		desc        string
+		req         csi.NodePublishVolumeRequest
+		expectedErr error
+	}{
+		{
+			desc:        "[Error] Volume capabilities missing",
+			req:         csi.NodePublishVolumeRequest{},
+			expectedErr: status.Error(codes.InvalidArgument, "Volume capability missing in request"),
+		},
+		{
+			desc:        "[Error] Volume ID missing",
+			req:         csi.NodePublishVolumeRequest{VolumeCapability: &csi.VolumeCapability{AccessMode: &volumeCap}},
+			expectedErr: status.Error(codes.InvalidArgument, "Volume ID missing in request"),
+		},
+		{
+			desc: "[Error] Target path missing",
+			req: csi.NodePublishVolumeRequest{VolumeCapability: &csi.VolumeCapability{AccessMode: &volumeCap},
+				VolumeId: "vol_1"},
+			expectedErr: status.Error(codes.InvalidArgument, "Target path not provided"),
+		},
+		{
+			desc: "[Error] Stage target path missing",
+			req: csi.NodePublishVolumeRequest{VolumeCapability: &csi.VolumeCapability{AccessMode: &volumeCap},
+				VolumeId:   "vol_1",
+				TargetPath: targetTest},
+			expectedErr: status.Error(codes.InvalidArgument, "Staging target not provided"),
+		},
+		{
+			desc: "[Error] Not a directory",
+			req: csi.NodePublishVolumeRequest{VolumeCapability: &csi.VolumeCapability{AccessMode: &volumeCap},
+				VolumeId:          "vol_1",
+				TargetPath:        smbFile,
+				StagingTargetPath: sourceTest,
+				Readonly:          true},
+			expectedErr: status.Errorf(codes.Internal, "Could not mount target \"./smb.go\": mkdir ./smb.go: not a directory"),
+		},
+		{
+			desc: "[Error] Mount error mocked by Mount",
+			req: csi.NodePublishVolumeRequest{VolumeCapability: &csi.VolumeCapability{AccessMode: &volumeCap},
+				VolumeId:          "vol_1",
+				TargetPath:        targetTest,
+				StagingTargetPath: errorMountSource,
+				Readonly:          true},
+			expectedErr: status.Errorf(codes.Internal, "Could not mount \"./error_mount_source\" at \"./target_test\": fake Mount: source error"),
+		},
+		{
+			desc: "[Success] Valid request read only",
+			req: csi.NodePublishVolumeRequest{VolumeCapability: &csi.VolumeCapability{AccessMode: &volumeCap},
+				VolumeId:          "vol_1",
+				TargetPath:        targetTest,
+				StagingTargetPath: sourceTest,
+				Readonly:          true},
+			expectedErr: nil,
+		},
+		{
+			desc: "[Success] Valid request already mounted",
+			req: csi.NodePublishVolumeRequest{VolumeCapability: &csi.VolumeCapability{AccessMode: &volumeCap},
+				VolumeId:          "vol_1",
+				TargetPath:        alreadyMountedTarget,
+				StagingTargetPath: sourceTest,
+				Readonly:          true},
+			expectedErr: nil,
+		},
+		{
+			desc: "[Success] Valid request",
+			req: csi.NodePublishVolumeRequest{VolumeCapability: &csi.VolumeCapability{AccessMode: &volumeCap},
+				VolumeId:          "vol_1",
+				TargetPath:        targetTest,
+				StagingTargetPath: sourceTest,
+				Readonly:          true},
+			expectedErr: nil,
+		},
+	}
+
+	// Setup
+	_ = makeDir(alreadyMountedTarget)
+	d := NewFakeDriver()
+	fakeMounter := &fakeMounter{}
+	d.mounter = &mount.SafeFormatAndMount{
+		Interface: fakeMounter,
+	}
+
+	for _, test := range tests {
+		_, err := d.NodePublishVolume(context.Background(), &test.req)
+		if !reflect.DeepEqual(err, test.expectedErr) {
+			t.Errorf("test case: %s, Unexpected error: %v", test.desc, err)
+		}
+	}
+
+	// Clean up
+	err := os.RemoveAll(targetTest)
+	assert.NoError(t, err)
+	err = os.RemoveAll(alreadyMountedTarget)
+	assert.NoError(t, err)
+}
+
+func TestNodeUnpublishVolume(t *testing.T) {
+	errorTarget := "./error_is_likely_target"
+	targetFile := "./abc.go"
+
+	tests := []struct {
+		desc        string
+		req         csi.NodeUnpublishVolumeRequest
+		expectedErr error
+	}{
+		{
+			desc:        "[Error] Volume ID missing",
+			req:         csi.NodeUnpublishVolumeRequest{TargetPath: targetTest},
+			expectedErr: status.Error(codes.InvalidArgument, "Volume ID missing in request"),
+		},
+		{
+			desc:        "[Error] Target missing",
+			req:         csi.NodeUnpublishVolumeRequest{VolumeId: "vol_1"},
+			expectedErr: status.Error(codes.InvalidArgument, "Target path missing in request"),
+		},
+		{
+			desc:        "[Error] Unmount error mocked by IsLikelyNotMountPoint",
+			req:         csi.NodeUnpublishVolumeRequest{TargetPath: errorTarget, VolumeId: "vol_1"},
+			expectedErr: status.Error(codes.Internal, "failed to unmount target \"./error_is_likely_target\": fake IsLikelyNotMountPoint: fake error"),
+		},
+		{
+			desc:        "[Success] Valid request",
+			req:         csi.NodeUnpublishVolumeRequest{TargetPath: targetFile, VolumeId: "vol_1"},
+			expectedErr: nil,
+		},
+	}
+
+	// Setup
+	_ = makeDir(errorTarget)
+	d := NewFakeDriver()
+	fakeMounter := &fakeMounter{}
+	d.mounter = &mount.SafeFormatAndMount{
+		Interface: fakeMounter,
+	}
+
+	for _, test := range tests {
+		_, err := d.NodeUnpublishVolume(context.Background(), &test.req)
+		if !reflect.DeepEqual(err, test.expectedErr) {
+			t.Errorf("test case: %s, Unexpected error: %v", test.desc, err)
+		}
+	}
+
+	// Clean up
+	err := os.RemoveAll(errorTarget)
+	assert.NoError(t, err)
+}
+
+func TestNodeUnstageVolume(t *testing.T) {
+	errorTarget := "./error_is_likely_target"
+	targetFile := "./abc.go"
+
+	tests := []struct {
+		desc        string
+		req         csi.NodeUnstageVolumeRequest
+		expectedErr error
+	}{
+		{
+			desc:        "[Error] Volume ID missing",
+			req:         csi.NodeUnstageVolumeRequest{StagingTargetPath: targetTest},
+			expectedErr: status.Error(codes.InvalidArgument, "Volume ID missing in request"),
+		},
+		{
+			desc:        "[Error] Target missing",
+			req:         csi.NodeUnstageVolumeRequest{VolumeId: "vol_1"},
+			expectedErr: status.Error(codes.InvalidArgument, "Staging target not provided"),
+		},
+		{
+			desc:        "[Error] CleanupMountPoint error mocked by IsLikelyNotMountPoint",
+			req:         csi.NodeUnstageVolumeRequest{StagingTargetPath: errorTarget, VolumeId: "vol_1"},
+			expectedErr: status.Error(codes.Internal, "failed to unmount staging target \"./error_is_likely_target\": fake IsLikelyNotMountPoint: fake error"),
+		},
+		{
+			desc:        "[Success] Valid request",
+			req:         csi.NodeUnstageVolumeRequest{StagingTargetPath: targetFile, VolumeId: "vol_1"},
+			expectedErr: nil,
+		},
+	}
+
+	// Setup
+	_ = makeDir(errorTarget)
+	d := NewFakeDriver()
+	fakeMounter := &fakeMounter{}
+	d.mounter = &mount.SafeFormatAndMount{
+		Interface: fakeMounter,
+	}
+
+	for _, test := range tests {
+		_, err := d.NodeUnstageVolume(context.Background(), &test.req)
+		if !reflect.DeepEqual(err, test.expectedErr) {
+			t.Errorf("test case: %s, Unexpected error: %v", test.desc, err)
+		}
+	}
+
+	// Clean up
+	err := os.RemoveAll(errorTarget)
+	assert.NoError(t, err)
+}
+
+func TestEnsureMountPoint(t *testing.T) {
+	errorTarget := "./error_is_likely_target"
+	alreadyExistTarget := "./false_is_likely_exist_target"
+	falseTarget := "./false_is_likely_target"
+	smbFile := "./smb.go"
+
+	tests := []struct {
+		desc        string
+		target      string
+		expectedErr error
+	}{
+		{
+			desc:        "[Error] Mocked by IsLikelyNotMountPoint",
+			target:      errorTarget,
+			expectedErr: fmt.Errorf("fake IsLikelyNotMountPoint: fake error"),
+		},
+		{
+			desc:        "[Error] Error opening file",
+			target:      falseTarget,
+			expectedErr: &os.PathError{Op: "open", Path: "./false_is_likely_target", Err: syscall.ENOENT},
+		},
+		{
+			desc:        "[Error] Not a directory",
+			target:      smbFile,
+			expectedErr: &os.PathError{Op: "mkdir", Path: "./smb.go", Err: syscall.ENOTDIR},
+		},
+		{
+			desc:        "[Success] Successful run",
+			target:      targetTest,
+			expectedErr: nil,
+		},
+		{
+			desc:        "[Success] Already existing mount",
+			target:      alreadyExistTarget,
+			expectedErr: nil,
+		},
+	}
+
+	// Setup
+	_ = makeDir(alreadyExistTarget)
+	d := NewFakeDriver()
+	fakeMounter := &fakeMounter{}
+	d.mounter = &mount.SafeFormatAndMount{
+		Interface: fakeMounter,
+	}
+
+	for _, test := range tests {
+		_, err := d.ensureMountPoint(test.target)
+		if !reflect.DeepEqual(err, test.expectedErr) {
+			t.Errorf("test case: %s, Unexpected error: %v", test.desc, err)
+		}
+	}
+
+	// Clean up
+	err := os.RemoveAll(alreadyExistTarget)
+	assert.NoError(t, err)
+	err = os.RemoveAll(targetTest)
+	assert.NoError(t, err)
+}
+
+func TestMakeDir(t *testing.T) {
+	//Successfully create directory
+	err := makeDir(targetTest)
+	assert.NoError(t, err)
+
+	//Failed case
+	err = makeDir("./smb.go")
+	var e *os.PathError
+	if !errors.As(err, &e) {
+		t.Errorf("Unexpected Error: %v", err)
+	}
+
+	// Remove the directory created
+	err = os.RemoveAll(targetTest)
+	assert.NoError(t, err)
+}