[#192] Add base lifecycle tests

Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
Denis Kirillov 2022-03-05 15:58:04 +03:00 committed by Denis Kirillov
parent 410db92aa2
commit 5bfb8fd291
3 changed files with 186 additions and 14 deletions

View file

@ -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 {

View file

@ -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
}

View 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,
}
}