mirror of
https://github.com/versity/versitygw.git
synced 2026-09-20 15:04:27 +00:00
feat: refactoring of the integration tests
All the integration tests used to be in a single file, which had become large, messy, and difficult to maintain. These changes split `tests.go` into multiple files, organized by logical test groups.
This commit is contained in:
@@ -0,0 +1,376 @@
|
||||
// Copyright 2023 Versity Software
|
||||
// This file is licensed under the Apache License, Version 2.0
|
||||
// (the "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package integration
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||
"github.com/versity/versitygw/s3err"
|
||||
)
|
||||
|
||||
func DeleteObject_non_existing_object(s *S3Conf) error {
|
||||
testName := "DeleteObject_non_existing_object"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := s3client.DeleteObject(ctx, &s3.DeleteObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: getPtr("my-obj"),
|
||||
})
|
||||
cancel()
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func DeleteObject_directory_object_noslash(s *S3Conf) error {
|
||||
testName := "DeleteObject_directory_object_noslash"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
obj := "my-obj/"
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := s3client.PutObject(ctx, &s3.PutObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
})
|
||||
cancel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
obj = "my-obj"
|
||||
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err = s3client.DeleteObject(ctx, &s3.DeleteObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
})
|
||||
cancel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// the delete above should succeed, but the object should not be deleted
|
||||
// since it should not correctly match the directory name
|
||||
// so the below head object should also succeed
|
||||
obj = "my-obj/"
|
||||
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err = s3client.HeadObject(ctx, &s3.HeadObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
})
|
||||
cancel()
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func DeleteObject_non_empty_dir_obj(s *S3Conf) error {
|
||||
testName := "DeleteObject_non_empty_dir_obj"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
objToDel := "foo/"
|
||||
nestedObj := objToDel + "bar"
|
||||
_, err := putObjects(s3client, []string{nestedObj, objToDel}, bucket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err = s3client.DeleteObject(ctx, &s3.DeleteObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &objToDel,
|
||||
})
|
||||
cancel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
||||
res, err := s3client.ListObjects(ctx, &s3.ListObjectsInput{
|
||||
Bucket: &bucket,
|
||||
})
|
||||
cancel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(res.Contents) != 1 {
|
||||
return fmt.Errorf("expected the object list length to be 1, instead got %v",
|
||||
len(res.Contents))
|
||||
}
|
||||
if getString(res.Contents[0].Key) != nestedObj {
|
||||
return fmt.Errorf("expected the object key to be %v, instead got %v",
|
||||
nestedObj, getString(res.Contents[0].Key))
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func DeleteObject_conditional_writes(s *S3Conf) error {
|
||||
testName := "DeleteObject_conditional_writes"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
obj := "my-obj"
|
||||
var etag *string = getPtr("")
|
||||
var size *int64 = getPtr(int64(0))
|
||||
var modTime *time.Time = getPtr(time.Now())
|
||||
|
||||
createObj := func() error {
|
||||
res, err := putObjectWithData(0, &s3.PutObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
Body: bytes.NewReader([]byte("dummy")),
|
||||
}, s3client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// get the exact LastModified time
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
out, err := s3client.HeadObject(ctx, &s3.HeadObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
})
|
||||
cancel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*etag = *res.res.ETag
|
||||
*size = *res.res.Size
|
||||
*modTime = *out.LastModified
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
err := createObj()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
errPrecond := s3err.GetAPIError(s3err.ErrPreconditionFailed)
|
||||
|
||||
for i, test := range []struct {
|
||||
ifMatch *string
|
||||
size *int64
|
||||
modTime *time.Time
|
||||
err error
|
||||
}{
|
||||
// no error cases
|
||||
{etag, size, modTime, nil},
|
||||
{etag, nil, nil, nil},
|
||||
{nil, size, nil, nil},
|
||||
{nil, nil, modTime, nil},
|
||||
{etag, size, nil, nil},
|
||||
{etag, nil, modTime, nil},
|
||||
{nil, size, modTime, nil},
|
||||
// error cases
|
||||
{getPtr("incorrect_etag"), nil, nil, errPrecond},
|
||||
{nil, getPtr(int64(23234)), nil, errPrecond},
|
||||
{nil, nil, getPtr(time.Now().AddDate(-1, -1, -1)), errPrecond},
|
||||
{getPtr("incorrect_etag"), getPtr(int64(23234)), nil, errPrecond},
|
||||
{getPtr("incorrect_etag"), getPtr(int64(23234)), getPtr(time.Now().AddDate(-1, -1, -1)), errPrecond},
|
||||
} {
|
||||
err := createObj()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err = s3client.DeleteObject(ctx, &s3.DeleteObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
IfMatch: test.ifMatch,
|
||||
IfMatchSize: test.size,
|
||||
IfMatchLastModifiedTime: test.modTime,
|
||||
})
|
||||
cancel()
|
||||
if test.err != nil {
|
||||
apiErr, ok := test.err.(s3err.APIError)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid error type: expected s3err.APIError")
|
||||
}
|
||||
if err := checkApiErr(err, apiErr); err != nil {
|
||||
return fmt.Errorf("test case %d failed: %w", i, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func DeleteObject_directory_not_empty(s *S3Conf) error {
|
||||
testName := "DeleteObject_directory_not_empty"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
obj := "dir/my-obj"
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := s3client.PutObject(ctx, &s3.PutObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
})
|
||||
cancel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
obj = "dir/"
|
||||
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err = s3client.DeleteObject(ctx, &s3.DeleteObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
})
|
||||
cancel()
|
||||
// object servers will return no error, but the posix backend returns
|
||||
// a non-standard directory not empty. This test is a posix only test
|
||||
// to validate the specific error response.
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrDirectoryNotEmpty)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func DeleteObject_non_existing_dir_object(s *S3Conf) error {
|
||||
testName := "DeleteObject_non_existing_dir_object"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
obj := "my-obj"
|
||||
_, err := putObjects(s3client, []string{obj}, bucket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
obj = "my-obj/"
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err = s3client.DeleteObject(ctx, &s3.DeleteObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
})
|
||||
cancel()
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func DeleteObject_directory_object(s *S3Conf) error {
|
||||
testName := "DeleteObject_directory_object"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
obj := "foo/bar/"
|
||||
_, err := putObjects(s3client, []string{obj}, bucket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err = s3client.DeleteObject(ctx, &s3.DeleteObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
})
|
||||
cancel()
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func DeleteObject_success(s *S3Conf) error {
|
||||
testName := "DeleteObject_success"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
obj := "my-obj"
|
||||
_, err := putObjects(s3client, []string{obj}, bucket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err = s3client.DeleteObject(ctx, &s3.DeleteObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
})
|
||||
cancel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err = s3client.GetObject(ctx, &s3.GetObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
})
|
||||
defer cancel()
|
||||
if err := checkSdkApiErr(err, "NoSuchKey"); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func DeleteObject_success_status_code(s *S3Conf) error {
|
||||
testName := "DeleteObject_success_status_code"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
obj := "my-obj"
|
||||
_, err := putObjects(s3client, []string{obj}, bucket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req, err := createSignedReq(http.MethodDelete, s.endpoint,
|
||||
fmt.Sprintf("%v/%v", bucket, obj), s.awsID, s.awsSecret, "s3",
|
||||
s.awsRegion, nil, time.Now(), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusNoContent {
|
||||
return fmt.Errorf("expected response status to be %v, instead got %v",
|
||||
http.StatusNoContent, resp.StatusCode)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func DeleteObject_incorrect_expected_bucket_owner(s *S3Conf) error {
|
||||
testName := "DeleteObject_incorrect_expected_bucket_owner"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := s3client.DeleteObject(ctx, &s3.DeleteObjectInput{
|
||||
Bucket: &bucket,
|
||||
// anyways if object doesn't exist, a 200 response should be received
|
||||
Key: getPtr("my-obj"),
|
||||
ExpectedBucketOwner: getPtr(s.awsID + "something"),
|
||||
})
|
||||
cancel()
|
||||
|
||||
return checkApiErr(err, s3err.GetAPIError(s3err.ErrAccessDenied))
|
||||
})
|
||||
}
|
||||
|
||||
func DeleteObject_expected_bucket_owner(s *S3Conf) error {
|
||||
testName := "DeleteObject_expected_bucket_owner"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := s3client.DeleteObject(ctx, &s3.DeleteObjectInput{
|
||||
Bucket: &bucket,
|
||||
// anyways if object doesn't exist, a 200 response should be received
|
||||
Key: getPtr("my-obj"),
|
||||
ExpectedBucketOwner: &s.awsID,
|
||||
})
|
||||
cancel()
|
||||
|
||||
return err
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user