package handler import ( "encoding/xml" "net/http" "net/http/httptest" "net/url" "sort" "testing" apierr "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware" "github.com/stretchr/testify/require" ) func TestHandler_ListBucketsHandler(t *testing.T) { const defaultConstraint = "default" region := "us-west-1" hc := prepareHandlerContext(t) hc.config.putLocationConstraint(region) props := []Bucket{ {Name: "first"}, {Name: "regional", BucketRegion: "us-west-1"}, {Name: "third"}, } sort.Slice(props, func(i, j int) bool { return props[i].Name < props[j].Name }) for _, bkt := range props { createBucketWithConstraint(hc, bkt.Name, bkt.BucketRegion) } for _, tt := range []struct { title string token string prefix string bucketRegion string maxBuckets string expectErr bool expected []Bucket expectedToken string }{ { title: "no params", expected: []Bucket{ {Name: "first", BucketRegion: defaultConstraint}, {Name: "regional", BucketRegion: "us-west-1"}, {Name: "third", BucketRegion: defaultConstraint}, }, }, { title: "negative max-buckets", maxBuckets: "-1", expected: []Bucket{}, expectErr: true, }, { title: "zero max-buckets", maxBuckets: "0", expected: []Bucket{}, }, { title: "prefix", prefix: "thi", expected: []Bucket{{Name: "third", BucketRegion: defaultConstraint}}, }, { title: "wrong prefix", prefix: "sdh", expected: []Bucket{}, }, { title: "bucket region", bucketRegion: region, expected: []Bucket{{Name: "regional", BucketRegion: "us-west-1"}}, }, { title: "default bucket region", bucketRegion: defaultConstraint, expected: []Bucket{ {Name: "first", BucketRegion: defaultConstraint}, {Name: "third", BucketRegion: defaultConstraint}, }, }, { title: "wrong bucket region", bucketRegion: "sj dfdlsj", expected: []Bucket{}, }, } { t.Run(tt.title, func(t *testing.T) { if tt.expectErr { listBucketsErr(hc, tt.prefix, tt.token, tt.bucketRegion, tt.maxBuckets, apierr.GetAPIError(apierr.ErrInvalidMaxKeys)) return } resp := listBuckets(hc, tt.prefix, tt.token, tt.bucketRegion, tt.maxBuckets) require.Len(t, resp.Buckets.Buckets, len(tt.expected)) require.Equal(t, tt.prefix, resp.Prefix) require.Equal(t, hc.owner.String(), resp.Owner.ID) if len(resp.Buckets.Buckets) > 0 { t.Log(resp.Buckets.Buckets[0].Name) } for i, bkt := range resp.Buckets.Buckets { require.Equal(t, tt.expected[i].Name, bkt.Name) require.Equal(t, tt.expected[i].BucketRegion, bkt.BucketRegion) } }) } t.Run("pagination", func(t *testing.T) { t.Run("happy path", func(t *testing.T) { resp := listBuckets(hc, "", "", "", "1") require.Len(t, resp.Buckets.Buckets, 1) require.Equal(t, props[0].Name, resp.Buckets.Buckets[0].Name) require.NotEmpty(t, resp.ContinuationToken) resp = listBuckets(hc, "", resp.ContinuationToken, "", "1") require.Len(t, resp.Buckets.Buckets, 1) require.Equal(t, props[1].Name, resp.Buckets.Buckets[0].Name) require.NotEmpty(t, resp.ContinuationToken) resp = listBuckets(hc, "", resp.ContinuationToken, "", "1") require.Len(t, resp.Buckets.Buckets, 1) require.Equal(t, props[2].Name, resp.Buckets.Buckets[0].Name) require.Empty(t, resp.ContinuationToken) }) t.Run("wrong continuation-token", func(t *testing.T) { resp := listBuckets(hc, "", "CebuVwfRpdMqi9dvgV2SUNbrkfteGtudchKKhNabXUu9", "", "1") require.Len(t, resp.Buckets.Buckets, 0) require.Empty(t, resp.ContinuationToken) }) }) } func listBuckets(hc *handlerContext, prefix, token, bucketRegion, maxBuckets string) ListBucketsResponse { query := url.Values{ middleware.QueryPrefix: []string{prefix}, middleware.QueryContinuationToken: []string{token}, middleware.QueryBucketRegion: []string{bucketRegion}, middleware.QueryMaxBuckets: []string{maxBuckets}, } w := listBucketsBase(hc, bucketPrm{query: query}) assertStatus(hc.t, w, http.StatusOK) var resp ListBucketsResponse err := xml.NewDecoder(w.Body).Decode(&resp) require.NoError(hc.t, err) return resp } func listBucketsErr(hc *handlerContext, prefix, token, bucketRegion, maxBuckets string, err apierr.Error) { query := url.Values{ middleware.QueryPrefix: []string{prefix}, middleware.QueryContinuationToken: []string{token}, middleware.QueryBucketRegion: []string{bucketRegion}, middleware.QueryMaxBuckets: []string{maxBuckets}, } w := listBucketsBase(hc, bucketPrm{query: query}) assertS3Error(hc.t, w, err) } func listBucketsBase(hc *handlerContext, prm bucketPrm) *httptest.ResponseRecorder { box, _ := createAccessBox(hc.t) w, r := prepareTestFullRequest(hc, "", "", prm.query, nil) ctx := middleware.SetBox(r.Context(), &middleware.Box{AccessBox: box}) r = r.WithContext(ctx) hc.Handler().ListBucketsHandler(w, r) return w }