diff --git a/api/pkg/metadata/service.go b/api/pkg/metadata/service.go index 0e2c7211a..a4ed5a3e8 100755 --- a/api/pkg/metadata/service.go +++ b/api/pkg/metadata/service.go @@ -61,7 +61,7 @@ func (s *APIService) ListMetadata(request *restful.Request, response *restful.Re listMetadataRequest, err := GetListMetaDataRequest(request) if err != nil { - log.Errorf("Failed to construct list metadata request err: \n", err) + log.Errorf("Failed to construct list metadata request err: %v\n", err) response.WriteEntity("Invalid type for sizeOfObject or sizeOfBucket request params. It should be integer type.") return } @@ -69,7 +69,7 @@ func (s *APIService) ListMetadata(request *restful.Request, response *restful.Re //* calling the ListMetaData method from metadata manager m8s res, err := s.metaClient.ListMetadata(ctx, &listMetadataRequest) if err != nil { - log.Errorf("Failed to get metadata details err: \n", err) + log.Errorf("Failed to get metadata details err: %v\n", err) response.WriteEntity(err) return } diff --git a/api/pkg/metadata/service_test.go b/api/pkg/metadata/service_test.go new file mode 100644 index 000000000..1b5a544c0 --- /dev/null +++ b/api/pkg/metadata/service_test.go @@ -0,0 +1,80 @@ +/* + * // Copyright 2023 The SODA 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 metadata + +import ( + "github.com/emicklei/go-restful" + mt "github.com/opensds/multi-cloud/metadata/proto" + "github.com/stretchr/testify/assert" + "net/http" + "net/url" + "strconv" + "testing" +) + +func GetRequest(uri string, param url.Values, t *testing.T) *restful.Request { + httpReq, err := http.NewRequest("GET", uri+param.Encode(), nil) + if err != nil { + t.Fail() + } + req := restful.NewRequest(httpReq) + return req +} + +func Test_ListMetaData_Api_Param_Fetching(t *testing.T) { + + validParams := make(url.Values) + validParams["limit"] = []string{"100"} + validParams["offset"] = []string{"1"} + + invalidParams := make(url.Values) + invalidParams["limit"] = []string{"invalid"} + invalidParams["offset"] = []string{"1"} + + tests := []struct { + title string + input interface{} + expectedOutput interface{} + expectedErr error + }{ + { + title: "UT to test whether the request params are read correctly for valid values", + input: validParams, + expectedOutput: mt.ListMetadataRequest{Limit: 100, Offset: 1}, + expectedErr: nil, + }, + { + title: "UT to test whether the request params is giving error for invalid values", + input: invalidParams, + expectedOutput: nil, + expectedErr: &strconv.NumError{}, + }, + } + + for _, test := range tests { + uri := "http://localhost:41651/v1/metadata/metadata/?" + values := test.input.(url.Values) + req := GetRequest(uri, values, t) + res, err := GetListMetaDataRequest(req) + if err != nil { + assert.NotNil(t, test.expectedErr, test.title) + } else { + assert.Equal(t, test.expectedOutput, res, test.title) + } + } +} diff --git a/metadata/pkg/query-manager/paginator_test.go b/metadata/pkg/query-manager/paginator_test.go new file mode 100644 index 000000000..33cec9079 --- /dev/null +++ b/metadata/pkg/query-manager/paginator_test.go @@ -0,0 +1,92 @@ +/* + * // Copyright 2023 The SODA 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 query_manager + +import ( + "github.com/opensds/multi-cloud/metadata/pkg/model" + "testing" +) + +func TestPaginate(t *testing.T) { + tests := []struct { + unPaginatedResult []*model.MetaBackend + limit int32 + offset int32 + expectedResult []*model.MetaBackend + }{ + { + unPaginatedResult: []*model.MetaBackend{ + {Id: "1"}, + {Id: "2"}, + {Id: "3"}, + {Id: "4"}, + {Id: "5"}, + }, + limit: 2, + offset: 0, + expectedResult: []*model.MetaBackend{ + {Id: "1"}, + {Id: "2"}, + }, + }, + { + unPaginatedResult: []*model.MetaBackend{ + {Id: "1"}, + {Id: "2"}, + {Id: "3"}, + {Id: "4"}, + {Id: "5"}, + }, + limit: 2, + offset: 2, + expectedResult: []*model.MetaBackend{ + {Id: "3"}, + {Id: "4"}, + }, + }, + { + unPaginatedResult: []*model.MetaBackend{ + {Id: "1"}, + {Id: "2"}, + {Id: "3"}, + {Id: "4"}, + {Id: "5"}, + }, + limit: 2, + offset: 5, + expectedResult: []*model.MetaBackend{}, + }, + { + unPaginatedResult: []*model.MetaBackend{}, + limit: 2, + offset: 0, + expectedResult: []*model.MetaBackend{}, + }, + } + for i, test := range tests { + result := Paginate(test.unPaginatedResult, test.limit, test.offset) + if len(result) != len(test.expectedResult) { + t.Errorf("Test case %d: expected length of result to be %d but got %d", i, len(test.expectedResult), len(result)) + } + for j, r := range result { + if r.Id != test.expectedResult[j].Id { + t.Errorf("Test case %d: expected result at index %d to be %s but got %s", i, j, test.expectedResult[j].Id, r.Id) + } + } + } +} diff --git a/metadata/pkg/query-manager/translator_test.go b/metadata/pkg/query-manager/translator_test.go new file mode 100644 index 000000000..7313330bc --- /dev/null +++ b/metadata/pkg/query-manager/translator_test.go @@ -0,0 +1,175 @@ +/* + * // Copyright 2023 The SODA 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 query_manager + +import ( + pb "github.com/opensds/multi-cloud/metadata/proto" + "github.com/stretchr/testify/assert" + "go.mongodb.org/mongo-driver/bson" + "testing" +) + +func Test_Translator(t *testing.T) { + sortAscBasedOnBackName := bson.D{{"$sort", bson.D{{"backendName", 1}}}} + + backendNameMatch := bson.D{ + bson.E{Key: "$match", Value: bson.D{ + bson.E{Key: "backendName", Value: "backendName"}}}} + + bucketNameMatch := bson.D{ + bson.E{Key: "$match", Value: bson.D{ + bson.E{Key: "buckets", Value: bson.D{ + bson.E{Key: "$elemMatch", Value: bson.D{ + bson.E{Key: "name", Value: bson.D{ + bson.E{Key: "$eq", Value: "bucketName"}}}}}}}}}} + + bucketNameFilter := bson.D{ + bson.E{Key: "$project", Value: bson.D{ + bson.E{Key: "_id", Value: 1}, + bson.E{Key: "backendName", Value: 1}, + bson.E{Key: "region", Value: 1}, + bson.E{Key: "type", Value: 1}, + bson.E{Key: "buckets", Value: bson.D{ + bson.E{Key: "$filter", Value: bson.D{ + bson.E{Key: "input", Value: "$buckets"}, + bson.E{Key: "as", Value: "bucket"}, + bson.E{Key: "cond", Value: bson.D{ + bson.E{Key: "$and", Value: bson.A{bson.D{ + bson.E{Key: "$eq", Value: bson.A{"$$bucket.name", "bucketName"}}}}}}}}}}}, + bson.E{Key: "numberOfBuckets", Value: 1}, + bson.E{Key: "numberOFilteredBuckets", Value: bson.D{ + bson.E{Key: "$size", Value: bson.D{ + bson.E{Key: "$filter", Value: bson.D{ + bson.E{Key: "input", Value: "$buckets"}, + bson.E{Key: "as", Value: "bucket"}, + bson.E{Key: "cond", + Value: bson.D{ + bson.E{Key: "$and", + Value: bson.A{bson.D{ + bson.E{Key: "$eq", Value: bson.A{"$$bucket.name", "bucketName"}}}}}}}}}}}}}}}} + + objectNameMatch := bson.D{ + bson.E{Key: "$match", Value: bson.D{ + bson.E{Key: "buckets", Value: bson.D{ + bson.E{Key: "$elemMatch", Value: bson.D{ + bson.E{Key: "objects.name", Value: bson.D{ + bson.E{Key: "$eq", Value: "objectName"}}}}}}}}}} + + objectNameFilter := bson.D{ + bson.E{Key: "$project", Value: bson.D{ + bson.E{Key: "_id", Value: 1}, + bson.E{Key: "backendName", Value: 1}, + bson.E{Key: "region", Value: 1}, + bson.E{Key: "type", Value: 1}, + bson.E{Key: "numberOfBuckets", Value: 1}, + bson.E{Key: "buckets", + Value: bson.D{ + bson.E{Key: "$map", Value: bson.D{ + bson.E{Key: "input", Value: "$buckets"}, + bson.E{Key: "as", Value: "bucket"}, + bson.E{Key: "in", Value: bson.D{ + bson.E{Key: "creationDate", Value: "$$bucket.creationDate"}, + bson.E{Key: "name", Value: "$$bucket.name"}, + bson.E{Key: "region", Value: "$$bucket.region"}, + bson.E{Key: "type", Value: "$$bucket.type"}, + bson.E{Key: "access", Value: "$$bucket.access"}, + bson.E{Key: "numberOfObjects", Value: "$$bucket.numberOfObjects"}, + bson.E{Key: "numberOfFilteredObjects", Value: bson.D{ + bson.E{Key: "$size", Value: bson.D{ + bson.E{Key: "$filter", Value: bson.D{ + bson.E{Key: "input", Value: "$$bucket.objects"}, + bson.E{Key: "as", Value: "object"}, + bson.E{Key: "cond", Value: bson.D{ + bson.E{Key: "$and", Value: bson.A{bson.D{ + bson.E{Key: "$eq", Value: bson.A{"$$object.name", "objectName"}}}}}}}}}}}}}, + bson.E{Key: "totalSize", Value: "$$bucket.totalSize"}, + bson.E{Key: "filteredBucketSize", Value: bson.D{ + bson.E{Key: "$sum", Value: bson.D{ + bson.E{Key: "$map", Value: bson.D{ + bson.E{Key: "input", Value: bson.D{ + bson.E{Key: "$filter", Value: bson.D{ + bson.E{Key: "input", Value: "$$bucket.objects"}, + bson.E{Key: "as", Value: "object"}, + bson.E{Key: "cond", Value: bson.D{ + bson.E{Key: "$and", Value: bson.A{bson.D{ + bson.E{Key: "$eq", Value: bson.A{"$$object.name", "objectName"}}}}}}}}}}}, + bson.E{Key: "as", Value: "object"}, + bson.E{Key: "in", Value: "$$object.size"}}}}}}}, + bson.E{Key: "tags", Value: "$$bucket.tags"}, + bson.E{Key: "objects", Value: bson.D{ + bson.E{Key: "$filter", Value: bson.D{ + bson.E{Key: "input", Value: "$$bucket.objects"}, + bson.E{Key: "as", Value: "object"}, + bson.E{Key: "cond", Value: bson.D{ + bson.E{Key: "$and", Value: bson.A{bson.D{ + bson.E{Key: "$eq", Value: bson.A{"$$object.name", "objectName"}}}}}}}}}}}}}}}}}}}} + removeEmptyBucketsFilter := bson.D{ + bson.E{Key: "$project", Value: bson.D{ + bson.E{Key: "_id", Value: 1}, + bson.E{Key: "backendName", Value: 1}, + bson.E{Key: "region", Value: 1}, + bson.E{Key: "type", Value: 1}, + bson.E{Key: "buckets", Value: bson.D{ + bson.E{Key: "$filter", Value: bson.D{ + bson.E{Key: "input", Value: "$buckets"}, + bson.E{Key: "as", Value: "bucket"}, + bson.E{Key: "cond", Value: bson.D{ + bson.E{Key: "$and", Value: bson.A{bson.D{ + bson.E{Key: "$ifNull", Value: bson.A{"$$bucket.numberOfFilteredObjects", false}}}}}}}}}}}, + bson.E{Key: "numberOfBuckets", Value: 1}, + bson.E{Key: "numberOFilteredBuckets", Value: bson.D{ + bson.E{Key: "$size", Value: bson.D{ + bson.E{Key: "$filter", Value: bson.D{ + bson.E{Key: "input", Value: "$buckets"}, + bson.E{Key: "as", Value: "bucket"}, + bson.E{Key: "cond", Value: bson.D{ + bson.E{Key: "$and", Value: bson.A{bson.D{ + bson.E{Key: "$ifNull", Value: bson.A{"$$bucket.numberOfFilteredObjects", false}}}}}}}}}}}}}}}} + + tests := []struct { + title string + input pb.ListMetadataRequest + expectedOutput interface{} + }{ + { + title: "Empty Request", + input: pb.ListMetadataRequest{}, + expectedOutput: []bson.D{sortAscBasedOnBackName}, + }, + { + title: "Query Backend Name", + input: pb.ListMetadataRequest{BackendName: "backendName"}, + expectedOutput: []bson.D{backendNameMatch, sortAscBasedOnBackName}, + }, + { + title: "Query Bucket Name", + input: pb.ListMetadataRequest{BucketName: "bucketName"}, + expectedOutput: []bson.D{bucketNameMatch, bucketNameFilter, sortAscBasedOnBackName}, + }, + { + title: "Query Object Name", + input: pb.ListMetadataRequest{ObjectName: "objectName"}, + expectedOutput: []bson.D{objectNameMatch, objectNameFilter, removeEmptyBucketsFilter, sortAscBasedOnBackName}, + }, + } + + for _, test := range tests { + output := Translate(&test.input) + assert.Equal(t, test.expectedOutput, output, test.title) + } +} diff --git a/metadata/pkg/query-manager/validator.go b/metadata/pkg/query-manager/validator.go index b95098c9b..1a6da4ae9 100644 --- a/metadata/pkg/query-manager/validator.go +++ b/metadata/pkg/query-manager/validator.go @@ -21,11 +21,11 @@ import ( ) type ValidationError struct { - errMsg string + ErrMsg string } func (e *ValidationError) Error() string { - return e.errMsg + return e.ErrMsg } func ValidateInput(in *pb.ListMetadataRequest) (okie bool, err error) { @@ -62,7 +62,7 @@ func isSortParamValid(sortOrder string) (bool, error) { if sortOrder == "" || sortOrder == constants.ASC || sortOrder == constants.DESC { return true, nil } - return false, &ValidationError{errMsg: "Invalid sort order"} + return false, &ValidationError{ErrMsg: "Invalid sort order"} } func isSizeParamsValid(sizeInBytes int64, operator string) (bool, error) { @@ -83,11 +83,11 @@ func isSizeParamsValid(sizeInBytes int64, operator string) (bool, error) { case constants.GREATER_THAN_OPERATOR: default: - return false, &ValidationError{errMsg: "Operator for size should be lte, gt, eq, gte or lt"} + return false, &ValidationError{ErrMsg: "Operator for size should be lte, gt, eq, gte or lt"} } if sizeInBytes < constants.ZERO { - return false, &ValidationError{errMsg: "Size should be always positive."} + return false, &ValidationError{ErrMsg: "Size should be always positive."} } return true, nil @@ -105,7 +105,7 @@ func isValidRegion(region string, cloudType string) (bool, error) { return true, nil default: - return false, &ValidationError{errMsg: "Not a valid cloud type"} + return false, &ValidationError{ErrMsg: "Not a valid cloud type"} } if region == "" { @@ -117,5 +117,5 @@ func isValidRegion(region string, cloudType string) (bool, error) { return true, nil } } - return false, &ValidationError{errMsg: "Not a valid " + cloudType + " region"} + return false, &ValidationError{ErrMsg: "Not a valid " + cloudType + " region"} } diff --git a/metadata/pkg/query-manager/validator_test.go b/metadata/pkg/query-manager/validator_test.go new file mode 100644 index 000000000..7c16ed048 --- /dev/null +++ b/metadata/pkg/query-manager/validator_test.go @@ -0,0 +1,165 @@ +/* + * // Copyright 2023 The SODA 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 query_manager + +import ( + "github.com/stretchr/testify/assert" + "testing" + + "github.com/opensds/multi-cloud/metadata/pkg/constants" + pb "github.com/opensds/multi-cloud/metadata/proto" +) + +func TestValidateInput(t *testing.T) { + tests := []struct { + input *pb.ListMetadataRequest + want bool + err string + }{ + { + input: &pb.ListMetadataRequest{ + SizeOfBucketInBytes: 0, + BucketSizeOperator: "", + SizeOfObjectInBytes: 0, + ObjectSizeOperator: "", + SortOrder: constants.ASC, + Region: "us-west-2", + Type: constants.AWS_S3, + }, + want: true, + err: "", + }, + { + input: &pb.ListMetadataRequest{ + SizeOfBucketInBytes: 0, + BucketSizeOperator: "", + SizeOfObjectInBytes: 0, + ObjectSizeOperator: "", + SortOrder: constants.ASC, + Region: "invalid-region", + Type: constants.AWS_S3, + }, + want: false, + err: "Not a valid AWS_S3 region", + }, + { + input: &pb.ListMetadataRequest{ + SizeOfBucketInBytes: 0, + BucketSizeOperator: "", + SizeOfObjectInBytes: 0, + ObjectSizeOperator: "", + SortOrder: constants.ASC, + Region: "us-west-2", + Type: "invalid-type", + }, + want: false, + err: "Not a valid cloud type", + }, + { + input: &pb.ListMetadataRequest{ + SizeOfBucketInBytes: 0, + BucketSizeOperator: "", + SizeOfObjectInBytes: 0, + ObjectSizeOperator: "", + SortOrder: "invalid-sort", + Region: "us-west-2", + Type: constants.AWS_S3, + }, + want: false, + err: "Invalid sort order", + }, + } + + for _, tc := range tests { + result, _ := ValidateInput(tc.input) + assert.Equal(t, tc.want, result) + } +} + +func TestIsSortParamValid(t *testing.T) { + tests := []struct { + sortOrder string + expected bool + }{ + {"", true}, + {"ASC", true}, + {"DESC", true}, + {"abc", false}, + } + + for _, test := range tests { + result, _ := isSortParamValid(test.sortOrder) + if result != test.expected { + t.Errorf("isSortParamValid(%q) = %v, want %v", test.sortOrder, result, test.expected) + } + } +} + +func TestIsSizeParamsValid(t *testing.T) { + tests := []struct { + sizeInBytes int64 + operator string + expected bool + }{ + {0, "", true}, + {0, "gt", true}, + {0, "lte", true}, + {0, "eq", true}, + {0, "gte", true}, + {0, "lt", true}, + {1, "gt", true}, + {1, "lte", true}, + {1, "eq", true}, + {1, "gte", true}, + {1, "lt", true}, + {-1, "gt", false}, + {-1, "lte", false}, + {-1, "eq", false}, + {-1, "gte", false}, + {-1, "lt", false}, + {1, "abc", false}, + } + + for _, test := range tests { + result, _ := isSizeParamsValid(test.sizeInBytes, test.operator) + if result != test.expected { + t.Errorf("isSizeParamsValid(%d, %q) = %v, want %v", test.sizeInBytes, test.operator, result, test.expected) + } + } +} + +func TestIsValidRegion(t *testing.T) { + tests := []struct { + cloudType string + region string + expected bool + }{ + {"aws-s3", "us-west-1", true}, + {"aws-s3", "us-west-2", true}, + {"aws-s3", "abc", false}, + {"", "", true}, + {"abc", "us-west-1", false}, + } + + for _, test := range tests { + result, _ := isValidRegion(test.region, test.cloudType) + if result != test.expected { + t.Errorf("isValidRegion(%q, %q) = %v, want %v", test.region, test.cloudType, result, test.expected) + } + } +} diff --git a/metadata/pkg/service/service.go b/metadata/pkg/service/service.go index c385fdbe1..6df9e1711 100755 --- a/metadata/pkg/service/service.go +++ b/metadata/pkg/service/service.go @@ -83,7 +83,6 @@ func (myc *S3Cred) Retrieve() (credentials.Value, error) { } func Sync(ctx context.Context, backend *backend.BackendDetail, in *pb.SyncMetadataRequest) error { - log.Debugln("the backend we got now....:%+v", backend) sd, err := driver.CreateStorageDriver(backend.Type, backend) if err != nil { log.Errorln("failed to create driver. err:", err) @@ -99,7 +98,6 @@ func Sync(ctx context.Context, backend *backend.BackendDetail, in *pb.SyncMetada } func (f *metadataService) SyncMetadata(ctx context.Context, in *pb.SyncMetadataRequest, out *pb.BaseResponse) error { - log.Infoln("received sncMetadata request in metadata service:%+v", in) if in.Id != "" { backend, err := utils.GetBackend(ctx, f.backendClient, in.Id) if err != nil { diff --git a/metadata/pkg/service/service_test.go b/metadata/pkg/service/service_test.go new file mode 100644 index 000000000..8be62a250 --- /dev/null +++ b/metadata/pkg/service/service_test.go @@ -0,0 +1,79 @@ +/* + * // Copyright 2023 The SODA 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 service + +import ( + "context" + "github.com/opensds/multi-cloud/metadata/pkg/db" + "github.com/opensds/multi-cloud/metadata/pkg/model" + query_manager "github.com/opensds/multi-cloud/metadata/pkg/query-manager" + pb "github.com/opensds/multi-cloud/metadata/proto" + "github.com/stretchr/testify/assert" + "go.mongodb.org/mongo-driver/bson" + "testing" +) + +var listService = NewMetaService() + +type listMetaDataTestcase struct { + title string + input pb.ListMetadataRequest + expectedOutput pb.ListMetadataResponse + expectedErr error +} + +type mockDbAdapter struct{} + +func (mockDbAdapter) CreateMetadata(ctx context.Context, metaBackend model.MetaBackend) error { + return nil +} + +func (mockDbAdapter) ListMetadata(ctx context.Context, query []bson.D) ([]*model.MetaBackend, error) { + return nil, nil +} + +func Test_ListMetadata(t *testing.T) { + db.DbAdapter = &mockDbAdapter{} + + testcases := []listMetaDataTestcase{ + //* Empty list metadata request + { + title: "Empty request", + input: pb.ListMetadataRequest{}, + expectedOutput: pb.ListMetadataResponse{}, + expectedErr: nil, + }, + //* Invalid list metadata request + { + title: "Invalid request", + input: pb.ListMetadataRequest{BucketSizeOperator: "invalid"}, + expectedOutput: pb.ListMetadataResponse{}, + expectedErr: &query_manager.ValidationError{ErrMsg: "Operator for size should be lte, gt, eq, gte or lt"}, + }, + } + + for _, test := range testcases { + output := pb.ListMetadataResponse{} + err := listService.ListMetadata(context.TODO(), &test.input, &output) + if err != nil { + assert.Equalf(t, err, test.expectedErr, test.title) + } else { + assert.Equalf(t, output, test.expectedOutput, test.title) + } + } +} diff --git a/metadata/pkg/utils/utils_test.go b/metadata/pkg/utils/utils_test.go new file mode 100644 index 000000000..cb4c40869 --- /dev/null +++ b/metadata/pkg/utils/utils_test.go @@ -0,0 +1,72 @@ +/* + * // Copyright 2023 The SODA 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 utils + +import ( + "github.com/opensds/multi-cloud/metadata/pkg/model" + "testing" + "time" +) + +func TestGetBackends(t *testing.T) { + now := time.Now() + unPaginatedResult := []*model.MetaBackend{{ + BackendName: "backend1", + Region: "region1", + Type: "type1", + NumberOfBuckets: 1, + Buckets: []*model.MetaBucket{{ + Name: "bucket1", + Type: "type1", + Region: "region1", + TotalSize: 100, + NumberOfObjects: 1, + CreationDate: &now, + BucketTags: map[string]string{"key1": "value1"}, + Objects: []*model.MetaObject{{ + ObjectName: "object1", + LastModifiedDate: &now, + ServerSideEncryption: "true", + ExpiresDate: &now, + GrantControl: "grantControl1", + RedirectLocation: "redirect1", + ReplicationStatus: "replicated", + ObjectTags: map[string]string{"key1": "value1"}, + Metadata: map[string]string{"key1": "value1"}, + }}, + }}, + }} + + protoBackends := GetBackends(unPaginatedResult) + + if len(protoBackends) != 1 { + t.Errorf("Expected 1 proto backend, but got %d", len(protoBackends)) + } + + if protoBackends[0].BackendName != "backend1" { + t.Errorf("Expected backend name to be backend1, but got %s", protoBackends[0].BackendName) + } + + if len(protoBackends[0].Buckets) != 1 { + t.Errorf("Expected 1 proto bucket, but got %d", len(protoBackends[0].Buckets)) + } + + if len(protoBackends[0].Buckets[0].Objects) != 1 { + t.Errorf("Expected 1 proto object, but got %d", len(protoBackends[0].Buckets[0].Objects)) + } +}