[#585] Add ListBucketHandler test
Signed-off-by: Nikita Zinkevich <n.zinkevich@yadro.com>
This commit is contained in:
parent
bb906c92e4
commit
fa66f9a135
5 changed files with 189 additions and 26 deletions
|
@ -26,7 +26,7 @@ func TestPutObjectACLErrorAPE(t *testing.T) {
|
|||
hc := prepareHandlerContext(t)
|
||||
bktName, objName := "bucket-for-acl-ape", "object"
|
||||
|
||||
info := createBucket(hc, bktName)
|
||||
info := createBucket(hc, bucketPrm{bktName: bktName})
|
||||
|
||||
putObjectWithHeadersAssertS3Error(hc, bktName, objName, map[string]string{api.AmzACL: basicACLPublic}, apierr.ErrAccessControlListNotSupported)
|
||||
putObjectWithHeaders(hc, bktName, objName, map[string]string{api.AmzACL: basicACLPrivate}) // only `private` canned acl is allowed, that is actually ignored
|
||||
|
@ -43,7 +43,7 @@ func TestCreateObjectACLErrorAPE(t *testing.T) {
|
|||
hc := prepareHandlerContext(t)
|
||||
bktName, objName, objNameCopy := "bucket-for-acl-ape", "object", "copy"
|
||||
|
||||
createBucket(hc, bktName)
|
||||
createBucket(hc, bucketPrm{bktName: bktName})
|
||||
|
||||
putObject(hc, bktName, objName)
|
||||
copyObject(hc, bktName, objName, objNameCopy, CopyMeta{Headers: map[string]string{api.AmzACL: basicACLPublic}}, http.StatusBadRequest)
|
||||
|
@ -57,7 +57,7 @@ func TestBucketACLAPE(t *testing.T) {
|
|||
hc := prepareHandlerContext(t)
|
||||
bktName := "bucket-for-acl-ape"
|
||||
|
||||
info := createBucket(hc, bktName)
|
||||
info := createBucket(hc, bucketPrm{bktName: bktName})
|
||||
|
||||
aclBody := &AccessControlPolicy{}
|
||||
putBucketACLAssertS3Error(hc, bktName, info.Box, nil, aclBody, apierr.ErrAccessControlListNotSupported)
|
||||
|
@ -297,30 +297,30 @@ type createBucketInfo struct {
|
|||
Key *keys.PrivateKey
|
||||
}
|
||||
|
||||
func createBucket(hc *handlerContext, bktName string) *createBucketInfo {
|
||||
box, key := createAccessBox(hc.t)
|
||||
func createBucket(hc *handlerContext, prm bucketPrm) *createBucketInfo {
|
||||
prm.box, prm.key = createAccessBox(hc.t)
|
||||
|
||||
w := createBucketBase(hc, bktName, box)
|
||||
w := createBucketBase(hc, prm)
|
||||
assertStatus(hc.t, w, http.StatusOK)
|
||||
|
||||
bktInfo, err := hc.Layer().GetBucketInfo(hc.Context(), bktName)
|
||||
bktInfo, err := hc.Layer().GetBucketInfo(hc.Context(), prm.bktName)
|
||||
require.NoError(hc.t, err)
|
||||
|
||||
return &createBucketInfo{
|
||||
BktInfo: bktInfo,
|
||||
Box: box,
|
||||
Key: key,
|
||||
Box: prm.box,
|
||||
Key: prm.key,
|
||||
}
|
||||
}
|
||||
|
||||
func createBucketAssertS3Error(hc *handlerContext, bktName string, box *accessbox.Box, code apierr.ErrorCode) {
|
||||
w := createBucketBase(hc, bktName, box)
|
||||
func createBucketAssertS3Error(hc *handlerContext, prm bucketPrm, code apierr.ErrorCode) {
|
||||
w := createBucketBase(hc, prm)
|
||||
assertS3Error(hc.t, w, apierr.GetAPIError(code))
|
||||
}
|
||||
|
||||
func createBucketBase(hc *handlerContext, bktName string, box *accessbox.Box) *httptest.ResponseRecorder {
|
||||
w, r := prepareTestRequest(hc, bktName, "", nil)
|
||||
ctx := middleware.SetBox(r.Context(), &middleware.Box{AccessBox: box})
|
||||
func createBucketBase(hc *handlerContext, prm bucketPrm) *httptest.ResponseRecorder {
|
||||
w, r := prepareTestFullRequest(hc, prm.bktName, "", nil, prm.body)
|
||||
ctx := middleware.SetBox(r.Context(), &middleware.Box{AccessBox: prm.box})
|
||||
r = r.WithContext(ctx)
|
||||
hc.Handler().CreateBucketHandler(w, r)
|
||||
return w
|
||||
|
|
147
api/handler/bucket_list_test.go
Normal file
147
api/handler/bucket_list_test.go
Normal file
|
@ -0,0 +1,147 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestHandler_ListBucketsHandler(t *testing.T) {
|
||||
const defaultConstraint = "default"
|
||||
|
||||
type bktProp struct {
|
||||
name string
|
||||
region string
|
||||
cnrID cid.ID
|
||||
}
|
||||
|
||||
region := "us-west-1"
|
||||
hc := prepareHandlerContext(t)
|
||||
hc.config.putLocationConstraint(region)
|
||||
|
||||
props := []bktProp{
|
||||
{"first", "", cid.ID{}},
|
||||
{"regional", "us-west-1", cid.ID{}},
|
||||
{"third", "", cid.ID{}},
|
||||
}
|
||||
for i, bkt := range props {
|
||||
var body createBucketParams
|
||||
if bkt.region != "" {
|
||||
body.LocationConstraint = bkt.region
|
||||
}
|
||||
info := createBucket(hc, bucketPrm{bktName: bkt.name, body: body})
|
||||
props[i].cnrID = info.BktInfo.CID
|
||||
}
|
||||
|
||||
for _, tt := range []struct {
|
||||
title string
|
||||
query url.Values
|
||||
expected []bktProp
|
||||
expectedToken string
|
||||
}{
|
||||
{
|
||||
title: "without params",
|
||||
query: url.Values{},
|
||||
expected: []bktProp{
|
||||
{"first", defaultConstraint, cid.ID{}},
|
||||
{"regional", "us-west-1", cid.ID{}},
|
||||
{"third", defaultConstraint, cid.ID{}},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "with prefix",
|
||||
query: url.Values{"prefix": []string{"thi"}},
|
||||
expected: []bktProp{{"third", defaultConstraint, cid.ID{}}},
|
||||
},
|
||||
{
|
||||
title: "wrong prefix",
|
||||
query: url.Values{"prefix": []string{"sdh"}},
|
||||
expected: []bktProp{},
|
||||
},
|
||||
{
|
||||
title: "with bucket region",
|
||||
query: url.Values{"bucket-region": []string{region}},
|
||||
expected: []bktProp{{"regional", "us-west-1", cid.ID{}}},
|
||||
},
|
||||
{
|
||||
title: "with default bucket region",
|
||||
query: url.Values{"bucket-region": []string{"default"}},
|
||||
expected: []bktProp{
|
||||
{"first", defaultConstraint, cid.ID{}},
|
||||
{"third", defaultConstraint, cid.ID{}},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "wrong bucket region",
|
||||
query: url.Values{"bucket-region": []string{"sjdfjl"}},
|
||||
expected: []bktProp{},
|
||||
},
|
||||
{
|
||||
title: "pagination max-buckets",
|
||||
query: url.Values{"max-buckets": []string{"1"}},
|
||||
expected: []bktProp{{"first", defaultConstraint, cid.ID{}}},
|
||||
expectedToken: props[1].cnrID.String(),
|
||||
},
|
||||
{
|
||||
title: "pagination continuation token",
|
||||
query: url.Values{
|
||||
"max-buckets": []string{"1"},
|
||||
"continuation-token": []string{props[1].cnrID.String()},
|
||||
},
|
||||
expected: []bktProp{{"regional", region, cid.ID{}}},
|
||||
expectedToken: props[2].cnrID.String(),
|
||||
},
|
||||
{
|
||||
title: "pagination last element",
|
||||
query: url.Values{
|
||||
"max-buckets": []string{"1"},
|
||||
"continuation-token": []string{props[2].cnrID.String()},
|
||||
},
|
||||
expected: []bktProp{{"third", defaultConstraint, cid.ID{}}},
|
||||
expectedToken: "",
|
||||
},
|
||||
} {
|
||||
t.Run(tt.title, func(t *testing.T) {
|
||||
params := bucketPrm{query: tt.query}
|
||||
resp := listBuckets(hc, params)
|
||||
require.Len(t, resp.Buckets.Buckets, len(tt.expected))
|
||||
require.Equal(t, params.query.Get("prefix"), resp.Prefix)
|
||||
require.Equal(t, tt.expectedToken, resp.ContinuationToken)
|
||||
respProps := make([]bktProp, 0, len(resp.Buckets.Buckets))
|
||||
for _, bkt := range resp.Buckets.Buckets {
|
||||
respProps = append(respProps, bktProp{bkt.Name, bkt.BucketRegion, cid.ID{}})
|
||||
}
|
||||
sort.Slice(respProps, func(i, j int) bool {
|
||||
return respProps[i].name < respProps[j].name
|
||||
})
|
||||
require.Equal(t, tt.expected, respProps)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func listBuckets(hc *handlerContext, prm bucketPrm) ListBucketsResponse {
|
||||
prm.box, prm.key = createAccessBox(hc.t)
|
||||
w := listBucketsBase(hc, prm)
|
||||
assertStatus(hc.t, w, http.StatusOK)
|
||||
var resp ListBucketsResponse
|
||||
err := xml.NewDecoder(w.Body).Decode(&resp)
|
||||
require.NoError(hc.t, err)
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
func listBucketsBase(hc *handlerContext, prm bucketPrm) *httptest.ResponseRecorder {
|
||||
w, r := prepareTestFullRequest(hc, "", "", prm.query, nil)
|
||||
ctx := middleware.SetBox(r.Context(), &middleware.Box{AccessBox: prm.box})
|
||||
r = r.WithContext(ctx)
|
||||
hc.Handler().ListBucketsHandler(w, r)
|
||||
|
||||
return w
|
||||
}
|
|
@ -23,6 +23,7 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer/frostfs"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/resolver"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/pkg/service/tree"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||
|
@ -74,6 +75,7 @@ func (hc *handlerContextBase) Context() context.Context {
|
|||
|
||||
type configMock struct {
|
||||
defaultPolicy netmap.PlacementPolicy
|
||||
placementPolicies map[string]netmap.PlacementPolicy
|
||||
copiesNumbers map[string][]uint32
|
||||
defaultCopiesNumbers []uint32
|
||||
bypassContentEncodingInChunks bool
|
||||
|
@ -85,8 +87,9 @@ func (c *configMock) DefaultPlacementPolicy(_ string) netmap.PlacementPolicy {
|
|||
return c.defaultPolicy
|
||||
}
|
||||
|
||||
func (c *configMock) PlacementPolicy(_, _ string) (netmap.PlacementPolicy, bool) {
|
||||
return netmap.PlacementPolicy{}, false
|
||||
func (c *configMock) PlacementPolicy(_, constraint string) (netmap.PlacementPolicy, bool) {
|
||||
policy, ok := c.placementPolicies[constraint]
|
||||
return policy, ok
|
||||
}
|
||||
|
||||
func (c *configMock) CopiesNumbers(_, locationConstraint string) ([]uint32, bool) {
|
||||
|
@ -146,6 +149,10 @@ func (c *configMock) TLSTerminationHeader() string {
|
|||
return c.tlsTerminationHeader
|
||||
}
|
||||
|
||||
func (c *configMock) putLocationConstraint(constraint string) {
|
||||
c.placementPolicies[constraint] = c.defaultPolicy
|
||||
}
|
||||
|
||||
func prepareHandlerContext(t *testing.T) *handlerContext {
|
||||
hc, err := prepareHandlerContextBase(layer.DefaultCachesConfigs(zap.NewExample()))
|
||||
require.NoError(t, err)
|
||||
|
@ -212,7 +219,8 @@ func prepareHandlerContextBase(cacheCfg *layer.CachesConfig) (*handlerContextBas
|
|||
}
|
||||
|
||||
cfg := &configMock{
|
||||
defaultPolicy: pp,
|
||||
defaultPolicy: pp,
|
||||
placementPolicies: make(map[string]netmap.PlacementPolicy),
|
||||
}
|
||||
h := &handler{
|
||||
log: log,
|
||||
|
@ -394,8 +402,16 @@ func (f *frostfsidMock) GetUserKey(account, user string) (string, error) {
|
|||
return hex.EncodeToString(res.Bytes()), nil
|
||||
}
|
||||
|
||||
type bucketPrm struct {
|
||||
bktName string
|
||||
query url.Values
|
||||
box *accessbox.Box
|
||||
key *keys.PrivateKey
|
||||
body any
|
||||
}
|
||||
|
||||
func createTestBucket(hc *handlerContext, bktName string) *data.BucketInfo {
|
||||
info := createBucket(hc, bktName)
|
||||
info := createBucket(hc, bucketPrm{bktName: bktName})
|
||||
return info.BktInfo
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ func TestPutBucketLifecycleConfiguration(t *testing.T) {
|
|||
hc := prepareHandlerContextWithMinCache(t)
|
||||
|
||||
bktName := "bucket-lifecycle"
|
||||
createBucket(hc, bktName)
|
||||
createBucket(hc, bucketPrm{bktName: bktName})
|
||||
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
|
@ -429,7 +429,7 @@ func TestPutBucketLifecycleIDGeneration(t *testing.T) {
|
|||
hc := prepareHandlerContext(t)
|
||||
|
||||
bktName := "bucket-lifecycle-id"
|
||||
createBucket(hc, bktName)
|
||||
createBucket(hc, bucketPrm{bktName: bktName})
|
||||
|
||||
lifecycle := &data.LifecycleConfiguration{
|
||||
Rules: []data.LifecycleRule{
|
||||
|
@ -459,7 +459,7 @@ func TestPutBucketLifecycleInvalidMD5(t *testing.T) {
|
|||
hc := prepareHandlerContext(t)
|
||||
|
||||
bktName := "bucket-lifecycle-md5"
|
||||
createBucket(hc, bktName)
|
||||
createBucket(hc, bucketPrm{bktName: bktName})
|
||||
|
||||
lifecycle := &data.LifecycleConfiguration{
|
||||
Rules: []data.LifecycleRule{
|
||||
|
@ -491,7 +491,7 @@ func TestPutBucketLifecycleInvalidXML(t *testing.T) {
|
|||
hc := prepareHandlerContext(t)
|
||||
|
||||
bktName := "bucket-lifecycle-invalid-xml"
|
||||
createBucket(hc, bktName)
|
||||
createBucket(hc, bucketPrm{bktName: bktName})
|
||||
|
||||
cfg := &data.CORSConfiguration{}
|
||||
body, err := xml.Marshal(cfg)
|
||||
|
|
|
@ -546,11 +546,11 @@ func TestCreateBucket(t *testing.T) {
|
|||
hc := prepareHandlerContext(t)
|
||||
bktName := "bkt-name"
|
||||
|
||||
info := createBucket(hc, bktName)
|
||||
createBucketAssertS3Error(hc, bktName, info.Box, apierr.ErrBucketAlreadyOwnedByYou)
|
||||
info := createBucket(hc, bucketPrm{bktName: bktName})
|
||||
createBucketAssertS3Error(hc, bucketPrm{bktName: bktName, box: info.Box}, apierr.ErrBucketAlreadyOwnedByYou)
|
||||
|
||||
box2, _ := createAccessBox(t)
|
||||
createBucketAssertS3Error(hc, bktName, box2, apierr.ErrBucketAlreadyExists)
|
||||
createBucketAssertS3Error(hc, bucketPrm{bktName: bktName, box: box2}, apierr.ErrBucketAlreadyExists)
|
||||
}
|
||||
|
||||
func TestCreateBucketWithoutPermissions(t *testing.T) {
|
||||
|
@ -560,7 +560,7 @@ func TestCreateBucketWithoutPermissions(t *testing.T) {
|
|||
hc.h.ape.(*apeMock).err = errors.New("no permissions")
|
||||
|
||||
box, _ := createAccessBox(t)
|
||||
createBucketAssertS3Error(hc, bktName, box, apierr.ErrInternalError)
|
||||
createBucketAssertS3Error(hc, bucketPrm{bktName: bktName, box: box}, apierr.ErrInternalError)
|
||||
|
||||
_, err := hc.tp.ContainerID(bktName)
|
||||
require.Errorf(t, err, "container exists after failed creation, but shouldn't")
|
||||
|
|
Loading…
Reference in a new issue