Skip to content

Commit 8110f11

Browse files
authored
UTs for rclone and utils validator (#214)
* publish dev tag Signed-off-by: Ashima-Ashima1 <[email protected]> * unit tests of controller server Signed-off-by: Ashima-Ashima1 <[email protected]> * uts for rclone.go validator file Signed-off-by: Ashima-Ashima1 <[email protected]> * uts for utils.go validator file Signed-off-by: Ashima-Ashima1 <[email protected]> * uts for utils.go validator file Signed-off-by: Ashima-Ashima1 <[email protected]> * uts for utils.go validator file Signed-off-by: Ashima-Ashima1 <[email protected]> --------- Signed-off-by: Ashima-Ashima1 <[email protected]>
1 parent cd10c49 commit 8110f11

File tree

5 files changed

+250
-15
lines changed

5 files changed

+250
-15
lines changed

cos-csi-mounter/server/rclone_test.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package main
2+
3+
import (
4+
"errors"
5+
"reflect"
6+
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
func TestRClonePopulateArgsSlice_Success(t *testing.T) {
12+
args := RCloneArgs{
13+
AllowOther: "true",
14+
}
15+
16+
resp, err := args.PopulateArgsSlice(testBucket, testTargetPath)
17+
assert.NoError(t, err)
18+
expectedVal := []string{"mount", testBucket, testTargetPath, "--allow-other=true"}
19+
assert.Equal(t, expectedVal, resp)
20+
}
21+
22+
func TestRCloneValidate_Success(t *testing.T) {
23+
FileExists = func(path string) (bool, error) {
24+
return true, nil
25+
}
26+
27+
args := RCloneArgs{}
28+
err := args.Validate(testTargetPath)
29+
assert.NoError(t, err)
30+
}
31+
32+
func TestRCloneValidate_PathValidatorFailed(t *testing.T) {
33+
args := RCloneArgs{}
34+
err := args.Validate("invalid-path")
35+
assert.Error(t, err)
36+
assert.Contains(t, err.Error(), "bad value for target path")
37+
}
38+
39+
func TestRCloneValidate_InvalidS3FSParamValues(t *testing.T) {
40+
fields := []string{
41+
"AllowOther",
42+
"AllowRoot",
43+
"AsyncRead",
44+
"Daemon",
45+
"DirectIO",
46+
"NoModificationTime",
47+
"ReadOnly",
48+
"VfsRefresh",
49+
"WriteBackCache",
50+
}
51+
for _, f := range fields {
52+
args := RCloneArgs{}
53+
54+
val := reflect.ValueOf(&args).Elem().FieldByName(f)
55+
val.SetString("invalid-value")
56+
err := args.Validate(testTargetPath)
57+
assert.Error(t, err)
58+
}
59+
}
60+
61+
func TestRCloneValidate_FailedToCheckPasswordFile(t *testing.T) {
62+
FileExists = func(path string) (bool, error) {
63+
return false, errors.New("error")
64+
}
65+
args := RCloneArgs{}
66+
err := args.Validate(testTargetPath)
67+
assert.Error(t, err)
68+
assert.Contains(t, err.Error(), "error checking rclone config file existence")
69+
}
70+
71+
func TestRCloneValidate_PasswordFileNotFound(t *testing.T) {
72+
FileExists = func(path string) (bool, error) {
73+
return false, nil
74+
}
75+
76+
args := RCloneArgs{}
77+
78+
err := args.Validate(testTargetPath)
79+
assert.Error(t, err)
80+
assert.Contains(t, err.Error(), "rclone config file not found")
81+
}

cos-csi-mounter/server/s3fs_test.go

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package main
33
import (
44
"errors"
55
"reflect"
6+
"slices"
67
"testing"
78

89
"github.com/stretchr/testify/assert"
@@ -13,10 +14,10 @@ var (
1314
testTargetPath = "/var/data/kubelet/pods/"
1415
testEndPoint = "testEndPoint"
1516
testPasswdFilePath = "testPasswdFilePath"
16-
testURL = "https://testRRL"
17+
testURL = "https://testURL"
1718
)
1819

19-
func TestPopulateArgsSlice_Success(t *testing.T) {
20+
func TestS3FSPopulateArgsSlice_Success(t *testing.T) {
2021
args := S3FSArgs{
2122
AllowOther: "true",
2223
EndPoint: "testEndPoint",
@@ -25,10 +26,12 @@ func TestPopulateArgsSlice_Success(t *testing.T) {
2526
resp, err := args.PopulateArgsSlice(testBucket, testTargetPath)
2627
assert.NoError(t, err)
2728
expectedVal := []string{testBucket, testTargetPath, "-o", "allow_other", "-o", "endpoint=" + testEndPoint}
29+
slices.Sort(expectedVal)
30+
slices.Sort(resp)
2831
assert.Equal(t, expectedVal, resp)
2932
}
3033

31-
func TestValidate_Success(t *testing.T) {
34+
func TestS3FSValidate_Success(t *testing.T) {
3235
FileExists = func(path string) (bool, error) {
3336
return true, nil
3437
}
@@ -41,14 +44,14 @@ func TestValidate_Success(t *testing.T) {
4144
assert.NoError(t, err)
4245
}
4346

44-
func TestValidate_PathValidatorFailed(t *testing.T) {
47+
func TestS3FSValidate_PathValidatorFailed(t *testing.T) {
4548
args := S3FSArgs{}
4649
err := args.Validate("invalid-path")
4750
assert.Error(t, err)
4851
assert.Contains(t, err.Error(), "bad value for target path")
4952
}
5053

51-
func TestValidate_InvalidS3FSParamValues(t *testing.T) {
54+
func TestS3FSValidate_InvalidS3FSParamValues(t *testing.T) {
5255
fields := []string{
5356
"AllowOther",
5457
"AutoCache",
@@ -85,7 +88,7 @@ func TestValidate_InvalidS3FSParamValues(t *testing.T) {
8588
}
8689
}
8790

88-
func TestValidate_FailedToCheckPasswordFile(t *testing.T) {
91+
func TestS3FSValidate_FailedToCheckPasswordFile(t *testing.T) {
8992
FileExists = func(path string) (bool, error) {
9093
return false, errors.New("error")
9194
}
@@ -97,7 +100,7 @@ func TestValidate_FailedToCheckPasswordFile(t *testing.T) {
97100
assert.Contains(t, err.Error(), "error checking credential file existence")
98101
}
99102

100-
func TestValidate_PasswordFileNotFound(t *testing.T) {
103+
func TestS3FSValidate_PasswordFileNotFound(t *testing.T) {
101104
FileExists = func(path string) (bool, error) {
102105
return false, nil
103106
}
@@ -111,7 +114,7 @@ func TestValidate_PasswordFileNotFound(t *testing.T) {
111114
assert.Contains(t, err.Error(), "credential file not found")
112115
}
113116

114-
func TestValidate_RetryCountLessThanOne(t *testing.T) {
117+
func TestS3FSValidate_RetryCountLessThanOne(t *testing.T) {
115118
FileExists = func(path string) (bool, error) {
116119
return true, nil
117120
}
@@ -126,7 +129,7 @@ func TestValidate_RetryCountLessThanOne(t *testing.T) {
126129
assert.Contains(t, err.Error(), "value of retires should be >= 1")
127130
}
128131

129-
func TestValidate_StatCacheExpireSecondsThanZero(t *testing.T) {
132+
func TestS3FSValidate_StatCacheExpireSecondsThanZero(t *testing.T) {
130133
FileExists = func(path string) (bool, error) {
131134
return true, nil
132135
}

cos-csi-mounter/server/utils.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ var (
2424
safeMountDirs = []string{"/var/data/kubelet/pods", "/var/lib/kubelet/pods"}
2525
// Directories where s3fs/rclone configuration files need to be present
2626
safeMounterConfigDir = "/var/lib/coscsi-config"
27+
28+
FileExists = fileExists
29+
absPathResolver = filepath.Abs
2730
)
2831

2932
// MounterArgs ...
@@ -43,7 +46,7 @@ func strictDecodeForUnknownFields(data json.RawMessage, v interface{}) error {
4346
}
4447

4548
func pathValidator(targetPath string) error {
46-
absPath, err := filepath.Abs(targetPath)
49+
absPath, err := absPathResolver(targetPath)
4750
if err != nil {
4851
return fmt.Errorf("failed to resolve absolute mount path: %v", err)
4952
}
@@ -94,11 +97,9 @@ func isBoolString(s string) bool {
9497
return s == "true" || s == "false"
9598
}
9699

97-
var FileExists = fileExists
98-
99100
// fileExists checks whether the given file path exists and is not a directory.
100101
func fileExists(path string) (bool, error) {
101-
absPath, err := filepath.Abs(path)
102+
absPath, err := absPathResolver(path)
102103
if err != nil {
103104
return false, fmt.Errorf("failed to resolve absolute path: %v", err)
104105
}

cos-csi-mounter/server/utils_test.go

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"errors"
6+
"path/filepath"
7+
"testing"
8+
9+
"github.com/IBM/ibm-object-csi-driver/pkg/constants"
10+
"github.com/stretchr/testify/assert"
11+
)
12+
13+
func TestParse_UnknownMounter(t *testing.T) {
14+
req := MountRequest{
15+
Mounter: "unknown",
16+
}
17+
18+
mounter := DefaultMounterArgsParser{}
19+
args, err := mounter.Parse(req)
20+
assert.Nil(t, args)
21+
assert.Error(t, err)
22+
assert.Contains(t, err.Error(), "unknown mounter")
23+
}
24+
25+
func TestParseMounterArgs_S3FS_Valid(t *testing.T) {
26+
FileExists = func(path string) (bool, error) {
27+
return true, nil
28+
}
29+
30+
req := MountRequest{
31+
Path: testTargetPath,
32+
Bucket: testBucket,
33+
Mounter: constants.S3FS,
34+
}
35+
argsStruct := S3FSArgs{
36+
URL: testURL,
37+
}
38+
b, _ := json.Marshal(argsStruct)
39+
req.Args = b
40+
41+
args, err := req.ParseMounterArgs()
42+
assert.NoError(t, err)
43+
assert.NotNil(t, args)
44+
}
45+
46+
func TestParseMounterArgs_RClone_Valid(t *testing.T) {
47+
FileExists = func(path string) (bool, error) {
48+
return true, nil
49+
}
50+
51+
req := MountRequest{
52+
Path: testTargetPath,
53+
Bucket: testBucket,
54+
Mounter: constants.RClone,
55+
}
56+
argsStruct := RCloneArgs{}
57+
b, _ := json.Marshal(argsStruct)
58+
req.Args = b
59+
60+
args, err := req.ParseMounterArgs()
61+
assert.NoError(t, err)
62+
assert.NotNil(t, args)
63+
}
64+
65+
func TestParseMounterArgs_S3FS_InvalidJSON(t *testing.T) {
66+
req := MountRequest{
67+
Path: testTargetPath,
68+
Bucket: testBucket,
69+
Mounter: constants.S3FS,
70+
Args: json.RawMessage(`{"invalid-json"}`),
71+
}
72+
73+
args, err := req.ParseMounterArgs()
74+
assert.Error(t, err)
75+
assert.Nil(t, args)
76+
}
77+
78+
func TestParseMounterArgs_RClone_InvalidJSON(t *testing.T) {
79+
req := MountRequest{
80+
Path: testTargetPath,
81+
Bucket: testBucket,
82+
Mounter: constants.RClone,
83+
Args: json.RawMessage(`{"invalid-json"}`),
84+
}
85+
86+
args, err := req.ParseMounterArgs()
87+
assert.Error(t, err)
88+
assert.Nil(t, args)
89+
}
90+
91+
func TestParseMounterArgs_S3FS_ValidationFails(t *testing.T) {
92+
req := MountRequest{
93+
Path: "invalid-path",
94+
Bucket: testBucket,
95+
Mounter: constants.S3FS,
96+
}
97+
argsStruct := S3FSArgs{}
98+
b, _ := json.Marshal(argsStruct)
99+
req.Args = b
100+
101+
args, err := req.ParseMounterArgs()
102+
assert.Nil(t, args)
103+
assert.Error(t, err)
104+
assert.Contains(t, err.Error(), "s3fs args validation failed")
105+
}
106+
107+
func TestParseMounterArgs_RClone_ValidationFails(t *testing.T) {
108+
req := MountRequest{
109+
Path: "invalid-path",
110+
Bucket: testBucket,
111+
Mounter: constants.RClone,
112+
}
113+
argsStruct := RCloneArgs{}
114+
b, _ := json.Marshal(argsStruct)
115+
req.Args = b
116+
117+
args, err := req.ParseMounterArgs()
118+
assert.Nil(t, args)
119+
assert.Error(t, err)
120+
assert.Contains(t, err.Error(), "rclone args validation failed")
121+
}
122+
123+
func TestFileExists_FileDoesNotExist(t *testing.T) {
124+
path := filepath.Join(safeMounterConfigDir, "nonexistent-file")
125+
exists, err := fileExists(path)
126+
assert.False(t, exists)
127+
assert.NoError(t, err)
128+
}
129+
130+
func TestFileExists_OutsideSafeDirectory(t *testing.T) {
131+
path := "/tmp/unsafe-file"
132+
exists, err := fileExists(path)
133+
assert.False(t, exists)
134+
assert.Error(t, err)
135+
assert.Contains(t, err.Error(), "outside the safe directory")
136+
}
137+
138+
func TestFileExists_AbsFails(t *testing.T) {
139+
originalAbs := absPathResolver
140+
defer func() { absPathResolver = originalAbs }()
141+
142+
absPathResolver = func(path string) (string, error) {
143+
return "", errors.New("Failed to resolve absolute path")
144+
}
145+
146+
exists, err := fileExists("invalid-path")
147+
assert.False(t, exists)
148+
assert.Error(t, err)
149+
assert.Contains(t, err.Error(), "failed to resolve absolute path")
150+
}

pkg/constants/constants.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ const (
3737

3838
PVCNameKey = "csi.storage.k8s.io/pvc/name"
3939
PVCNamespaceKey = "csi.storage.k8s.io/pvc/namespace"
40-
SecretNameKey = "cos.csi.driver/secret" // #nosec G101 -- false positive, this is not a credential
41-
SecretNamespaceKey = "cos.csi.driver/secret-namespace" // #nosec G101 -- false positive, this is not a credential
40+
SecretNameKey = "cos.csi.driver/secret" // #nosec G101 -- false positive, this is not a credential
41+
SecretNamespaceKey = "cos.csi.driver/secret-namespace" // #nosec G101 -- false positive, this is not a credential
4242

4343
BucketVersioning = "bucketVersioning"
4444

0 commit comments

Comments
 (0)