forked from TrueCloudLab/frostfs-s3-gw
[#192] Add base lifecycle tests
Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
parent
410db92aa2
commit
5bfb8fd291
3 changed files with 186 additions and 14 deletions
|
@ -9,15 +9,15 @@ type (
|
|||
}
|
||||
|
||||
Rule struct {
|
||||
AbortIncompleteMultipartUpload AbortIncompleteMultipartUpload `xml:"AbortIncompleteMultipartUpload" json:"AbortIncompleteMultipartUpload"`
|
||||
Expiration Expiration `xml:"Expiration" json:"Expiration"`
|
||||
Filter LifecycleRuleFilter `xml:"Filter" json:"Filter"`
|
||||
ID string `xml:"ID" json:"ID"`
|
||||
NoncurrentVersionExpiration NoncurrentVersionExpiration `xml:"NoncurrentVersionExpiration" json:"NoncurrentVersionExpiration"`
|
||||
NoncurrentVersionTransitions []NoncurrentVersionTransition `xml:"NoncurrentVersionTransition" json:"NoncurrentVersionTransition"`
|
||||
Prefix string `xml:"Prefix" json:"Prefix"`
|
||||
Status string `xml:"Status" json:"Status"`
|
||||
Transitions []Transition `xml:"Transition" json:"Transition"`
|
||||
AbortIncompleteMultipartUpload *AbortIncompleteMultipartUpload `xml:"AbortIncompleteMultipartUpload" json:"AbortIncompleteMultipartUpload"`
|
||||
Expiration *Expiration `xml:"Expiration" json:"Expiration"`
|
||||
Filter *LifecycleRuleFilter `xml:"Filter" json:"Filter"`
|
||||
ID string `xml:"ID" json:"ID"`
|
||||
NoncurrentVersionExpiration *NoncurrentVersionExpiration `xml:"NoncurrentVersionExpiration" json:"NoncurrentVersionExpiration"`
|
||||
NoncurrentVersionTransitions []NoncurrentVersionTransition `xml:"NoncurrentVersionTransition" json:"NoncurrentVersionTransition"`
|
||||
Prefix string `xml:"Prefix" json:"Prefix"`
|
||||
Status string `xml:"Status" json:"Status"`
|
||||
Transitions []Transition `xml:"Transition" json:"Transition"`
|
||||
}
|
||||
|
||||
AbortIncompleteMultipartUpload struct {
|
||||
|
@ -31,11 +31,11 @@ type (
|
|||
}
|
||||
|
||||
LifecycleRuleFilter struct {
|
||||
And LifecycleRuleAndOperator `xml:"And" json:"And"`
|
||||
ObjectSizeGreaterThan int64 `xml:"ObjectSizeGreaterThan" json:"ObjectSizeGreaterThan"`
|
||||
ObjectSizeLessThan int64 `xml:"ObjectSizeLessThan" json:"ObjectSizeLessThan"`
|
||||
Prefix string `xml:"Prefix" json:"Prefix"`
|
||||
Tag Tag `xml:"Tag" json:"Tag"`
|
||||
And *LifecycleRuleAndOperator `xml:"And" json:"And"`
|
||||
ObjectSizeGreaterThan int64 `xml:"ObjectSizeGreaterThan" json:"ObjectSizeGreaterThan"`
|
||||
ObjectSizeLessThan int64 `xml:"ObjectSizeLessThan" json:"ObjectSizeLessThan"`
|
||||
Prefix string `xml:"Prefix" json:"Prefix"`
|
||||
Tag *Tag `xml:"Tag" json:"Tag"`
|
||||
}
|
||||
|
||||
LifecycleRuleAndOperator struct {
|
||||
|
|
|
@ -123,7 +123,31 @@ func checkLifecycleConfiguration(conf *data.LifecycleConfiguration) error {
|
|||
if rule.Status != enabledValue && rule.Status != disabledValue {
|
||||
return apiErrors.GetAPIError(apiErrors.ErrMalformedXML)
|
||||
}
|
||||
if rule.Filter != nil {
|
||||
if rule.Filter.ObjectSizeGreaterThan < 0 || rule.Filter.ObjectSizeLessThan < 0 {
|
||||
return apiErrors.GetAPIError(apiErrors.ErrMalformedXML)
|
||||
}
|
||||
|
||||
if !filterContainsOneOption(rule.Filter) {
|
||||
return apiErrors.GetAPIError(apiErrors.ErrMalformedXML)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func filterContainsOneOption(filter *data.LifecycleRuleFilter) bool {
|
||||
exactlyOneOption := 0
|
||||
if filter.Prefix != "" {
|
||||
exactlyOneOption++
|
||||
}
|
||||
if filter.And != nil {
|
||||
exactlyOneOption++
|
||||
}
|
||||
if filter.Tag != nil {
|
||||
exactlyOneOption++
|
||||
}
|
||||
|
||||
return exactlyOneOption == 1
|
||||
}
|
||||
|
|
148
api/handler/lifecycle_test.go
Normal file
148
api/handler/lifecycle_test.go
Normal file
|
@ -0,0 +1,148 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/xml"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neofs-s3-gw/api/data"
|
||||
apiErrors "github.com/nspcc-dev/neofs-s3-gw/api/errors"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCheckLifecycleConfiguration(t *testing.T) {
|
||||
numRules := 1001
|
||||
rules := make([]data.Rule, numRules)
|
||||
for i := 0; i < numRules; i++ {
|
||||
rules[i] = data.Rule{ID: strconv.Itoa(i), Status: disabledValue}
|
||||
}
|
||||
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
configuration *data.LifecycleConfiguration
|
||||
noError bool
|
||||
}{
|
||||
{
|
||||
name: "basic",
|
||||
configuration: &data.LifecycleConfiguration{Rules: []data.Rule{{
|
||||
ID: "Some ID",
|
||||
Status: "Disabled",
|
||||
}}},
|
||||
noError: true,
|
||||
},
|
||||
{
|
||||
name: "invalid status",
|
||||
configuration: &data.LifecycleConfiguration{Rules: []data.Rule{{
|
||||
ID: "Some ID",
|
||||
Status: "",
|
||||
}}},
|
||||
},
|
||||
{
|
||||
name: "zero rules",
|
||||
configuration: &data.LifecycleConfiguration{},
|
||||
},
|
||||
{
|
||||
name: "more than max rules",
|
||||
configuration: &data.LifecycleConfiguration{Rules: rules},
|
||||
},
|
||||
{
|
||||
name: "invalid empty filter",
|
||||
configuration: &data.LifecycleConfiguration{Rules: []data.Rule{{
|
||||
Status: enabledValue,
|
||||
Filter: &data.LifecycleRuleFilter{},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
name: "invalid filter not exactly one option",
|
||||
configuration: &data.LifecycleConfiguration{Rules: []data.Rule{{
|
||||
Status: enabledValue,
|
||||
Filter: &data.LifecycleRuleFilter{
|
||||
Prefix: "prefix",
|
||||
Tag: &data.Tag{},
|
||||
},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
name: "invalid filter greater obj size",
|
||||
configuration: &data.LifecycleConfiguration{Rules: []data.Rule{{
|
||||
Status: enabledValue,
|
||||
Filter: &data.LifecycleRuleFilter{
|
||||
ObjectSizeGreaterThan: -1,
|
||||
},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
name: "invalid filter less obj size",
|
||||
configuration: &data.LifecycleConfiguration{Rules: []data.Rule{{
|
||||
Status: enabledValue,
|
||||
Filter: &data.LifecycleRuleFilter{
|
||||
ObjectSizeLessThan: -1,
|
||||
},
|
||||
}}},
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err := checkLifecycleConfiguration(tc.configuration)
|
||||
if tc.noError {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
require.Error(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBucketLifecycleConfiguration(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
hc := prepareHandlerContext(t)
|
||||
|
||||
bktName := "bucket-for-lifecycle"
|
||||
createTestBucket(ctx, t, hc, bktName)
|
||||
|
||||
w, r := prepareTestRequest(t, bktName, "", nil)
|
||||
hc.Handler().GetBucketLifecycleHandler(w, r)
|
||||
assertS3Error(t, w, apiErrors.GetAPIError(apiErrors.ErrNoSuchLifecycleConfiguration))
|
||||
|
||||
lifecycleConf := &data.LifecycleConfiguration{
|
||||
XMLName: xmlName("LifecycleConfiguration"),
|
||||
Rules: []data.Rule{
|
||||
{
|
||||
AbortIncompleteMultipartUpload: &data.AbortIncompleteMultipartUpload{},
|
||||
ID: "Test",
|
||||
Status: "Disabled",
|
||||
},
|
||||
}}
|
||||
w, r = prepareTestRequest(t, bktName, "", lifecycleConf)
|
||||
hc.Handler().PutBucketLifecycleHandler(w, r)
|
||||
require.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
w, r = prepareTestRequest(t, bktName, "", nil)
|
||||
hc.Handler().GetBucketLifecycleHandler(w, r)
|
||||
assertXMLEqual(t, w, lifecycleConf, &data.LifecycleConfiguration{})
|
||||
|
||||
w, r = prepareTestRequest(t, bktName, "", lifecycleConf)
|
||||
hc.Handler().DeleteBucketLifecycleHandler(w, r)
|
||||
require.Equal(t, http.StatusNoContent, w.Code)
|
||||
|
||||
// make sure deleting is idempotent operation
|
||||
w, r = prepareTestRequest(t, bktName, "", lifecycleConf)
|
||||
hc.Handler().DeleteBucketLifecycleHandler(w, r)
|
||||
require.Equal(t, http.StatusNoContent, w.Code)
|
||||
}
|
||||
|
||||
func assertXMLEqual(t *testing.T, w *httptest.ResponseRecorder, expected, actual interface{}) {
|
||||
err := xml.NewDecoder(w.Result().Body).Decode(actual)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expected, actual)
|
||||
require.Equal(t, http.StatusOK, w.Code)
|
||||
}
|
||||
|
||||
func xmlName(local string) xml.Name {
|
||||
return xml.Name{
|
||||
Space: "http://s3.amazonaws.com/doc/2006-03-01/",
|
||||
Local: local,
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue