From 331254402accbbeb5a28809d4148f028f273334b Mon Sep 17 00:00:00 2001 From: Damian Debkowski Date: Mon, 15 May 2023 11:57:07 -0700 Subject: [PATCH] feat(loopback): add ability to mock putObject response --- internal/plugin/loopback/loopback.go | 10 +++++-- internal/plugin/loopback/options.go | 45 ++++++++++++++++++++++++++-- internal/plugin/loopback/storage.go | 14 +++++++-- 3 files changed, 60 insertions(+), 9 deletions(-) diff --git a/internal/plugin/loopback/loopback.go b/internal/plugin/loopback/loopback.go index 1ee6a9bbb..4ca0ccb42 100644 --- a/internal/plugin/loopback/loopback.go +++ b/internal/plugin/loopback/loopback.go @@ -52,9 +52,10 @@ func NewLoopbackPlugin(opt ...TestOption) (*LoopbackPlugin, error) { hostMap: make(map[string][]*loopbackPluginHostInfo), }, LoopbackStorage: &LoopbackStorage{ - chunksSize: opts.withChunkSize, - buckets: map[BucketName]Bucket{"default": {}}, - errs: make([]PluginMockError, 0), + chunksSize: opts.withChunkSize, + buckets: map[BucketName]Bucket{"default": {}}, + errs: make([]PluginMockError, 0), + putObjectResponse: make([]PluginMockPutObjectResponse, 0), }, } @@ -80,6 +81,9 @@ func NewLoopbackPlugin(opt ...TestOption) (*LoopbackPlugin, error) { if len(opts.withMockError) > 0 { ret.errs = opts.withMockError } + if len(opts.withMockPutObjectResponse) > 0 { + ret.putObjectResponse = opts.withMockPutObjectResponse + } return ret, nil } diff --git a/internal/plugin/loopback/options.go b/internal/plugin/loopback/options.go index 030673664..f2da84e58 100644 --- a/internal/plugin/loopback/options.go +++ b/internal/plugin/loopback/options.go @@ -5,6 +5,7 @@ package loopback import ( "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/storagebuckets" + plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" "google.golang.org/grpc/codes" ) @@ -82,12 +83,43 @@ func (e PluginMockError) match(bucket *storagebuckets.StorageBucket, key string, return true } +// PluginMockPutObjectResponse is used to mock a response when calling putObject. +type PluginMockPutObjectResponse struct { + BucketName string + BucketPrefix string + ObjectKey string + Response *plgpb.PutObjectResponse +} + +// match compares the given values from the parameters to the values provided in the mocked response. +// The bucket and key parameter values should be provided from the plugin request. +// +// When match returns false, the mocked response should not be used for the plugin response. +// When match returns true, the mocked response should be used for the plugin response. +func (r PluginMockPutObjectResponse) match(bucket *storagebuckets.StorageBucket, key string) bool { + // if the mocked response object key does not match the request's object key, return false. + if r.ObjectKey != key { + return false + } + // if the mocked response bucket name does not match the request's bucket name, return false. + if r.BucketName != bucket.BucketName { + return false + } + // if the request has a bucket prefix and it does not match the mocked response bucket prefix, return false. + if bucket.BucketPrefix != "" && r.BucketPrefix != bucket.BucketPrefix { + return false + } + // all checks comparison checks passed, return true. + return true +} + type TestOption func(*TestOptions) error type TestOptions struct { - withMockBuckets map[BucketName]Bucket - withMockError []PluginMockError - withChunkSize int + withMockBuckets map[BucketName]Bucket + withMockError []PluginMockError + withMockPutObjectResponse []PluginMockPutObjectResponse + withChunkSize int } // getTestOpts - iterate the inbound Options and return a struct @@ -123,6 +155,13 @@ func WithMockError(err PluginMockError) TestOption { } } +func WithMockPutObjectResponse(response PluginMockPutObjectResponse) TestOption { + return func(o *TestOptions) error { + o.withMockPutObjectResponse = append(o.withMockPutObjectResponse, response) + return nil + } +} + // WithChunkSize provides an option to set the chunkSize used for grpc streams. func WithChunkSize(size int) TestOption { return func(o *TestOptions) error { diff --git a/internal/plugin/loopback/storage.go b/internal/plugin/loopback/storage.go index ba847ce0b..07934474b 100644 --- a/internal/plugin/loopback/storage.go +++ b/internal/plugin/loopback/storage.go @@ -109,9 +109,10 @@ type storagePluginStorageInfo struct { type LoopbackStorage struct { m sync.Mutex - chunksSize int - buckets map[BucketName]Bucket - errs []PluginMockError + chunksSize int + buckets map[BucketName]Bucket + errs []PluginMockError + putObjectResponse []PluginMockPutObjectResponse } func (l *LoopbackStorage) onCreateStorageBucket(ctx context.Context, req *plgpb.OnCreateStorageBucketRequest) (*plgpb.OnCreateStorageBucketResponse, error) { @@ -334,11 +335,18 @@ func (l *LoopbackStorage) putObject(ctx context.Context, req *plgpb.PutObjectReq if !ok { return nil, status.Errorf(codes.InvalidArgument, "%s: bucket not found", op) } + // return an expected mock error if one was provided for _, err := range l.errs { if err.match(req.GetBucket(), req.GetKey(), PutObject) { return nil, status.Errorf(err.ErrCode, "%s: %s", op, err.ErrMsg) } } + // return an expected mock response if one was provided + for _, mock := range l.putObjectResponse { + if mock.match(req.GetBucket(), req.GetKey()) { + return mock.Response, nil + } + } lastModified := time.Now() objectData, err := os.ReadFile(req.Path)