You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
135 lines
4.2 KiB
135 lines
4.2 KiB
package delete
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
|
|
"github.com/aws/aws-sdk-go-v2/aws"
|
|
"github.com/aws/aws-sdk-go-v2/service/s3"
|
|
"github.com/aws/aws-sdk-go-v2/service/s3/types"
|
|
"github.com/aws/smithy-go"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestConditionalDeleteIfMatchOnLatestVersion(t *testing.T) {
|
|
client := getTestClient(t)
|
|
bucket := createTestBucket(t, client)
|
|
defer cleanupBucket(t, client, bucket)
|
|
|
|
key := "conditional-delete.txt"
|
|
putResp, err := client.PutObject(context.TODO(), &s3.PutObjectInput{
|
|
Bucket: aws.String(bucket),
|
|
Key: aws.String(key),
|
|
Body: bytes.NewReader([]byte("versioned body")),
|
|
})
|
|
require.NoError(t, err)
|
|
require.NotNil(t, putResp.ETag)
|
|
|
|
_, err = client.DeleteObject(context.TODO(), &s3.DeleteObjectInput{
|
|
Bucket: aws.String(bucket),
|
|
Key: aws.String(key),
|
|
IfMatch: aws.String(`"not-the-current-etag"`),
|
|
})
|
|
require.Error(t, err, "DeleteObject should reject a mismatched If-Match header")
|
|
|
|
var apiErr smithy.APIError
|
|
if assert.True(t, errors.As(err, &apiErr), "Expected smithy API error for conditional delete") {
|
|
assert.Equal(t, "PreconditionFailed", apiErr.ErrorCode())
|
|
}
|
|
|
|
_, err = client.HeadObject(context.TODO(), &s3.HeadObjectInput{
|
|
Bucket: aws.String(bucket),
|
|
Key: aws.String(key),
|
|
})
|
|
require.NoError(t, err, "Object should remain current after a failed conditional delete")
|
|
|
|
deleteResp, err := client.DeleteObject(context.TODO(), &s3.DeleteObjectInput{
|
|
Bucket: aws.String(bucket),
|
|
Key: aws.String(key),
|
|
IfMatch: putResp.ETag,
|
|
})
|
|
require.NoError(t, err)
|
|
require.NotNil(t, deleteResp.DeleteMarker)
|
|
assert.True(t, *deleteResp.DeleteMarker, "Successful conditional delete on a versioned bucket should create a delete marker")
|
|
require.NotNil(t, deleteResp.VersionId)
|
|
|
|
_, err = client.HeadObject(context.TODO(), &s3.HeadObjectInput{
|
|
Bucket: aws.String(bucket),
|
|
Key: aws.String(key),
|
|
})
|
|
require.Error(t, err, "Delete marker should hide the current object after a successful conditional delete")
|
|
}
|
|
|
|
func TestConditionalMultiDeletePerObjectETag(t *testing.T) {
|
|
client := getTestClient(t)
|
|
bucket := createTestBucket(t, client)
|
|
defer cleanupBucket(t, client, bucket)
|
|
|
|
okKey := "delete-ok.txt"
|
|
failKey := "delete-fail.txt"
|
|
|
|
okPutResp, err := client.PutObject(context.TODO(), &s3.PutObjectInput{
|
|
Bucket: aws.String(bucket),
|
|
Key: aws.String(okKey),
|
|
Body: bytes.NewReader([]byte("delete me")),
|
|
})
|
|
require.NoError(t, err)
|
|
require.NotNil(t, okPutResp.ETag)
|
|
|
|
_, err = client.PutObject(context.TODO(), &s3.PutObjectInput{
|
|
Bucket: aws.String(bucket),
|
|
Key: aws.String(failKey),
|
|
Body: bytes.NewReader([]byte("keep me")),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
deleteResp, err := client.DeleteObjects(context.TODO(), &s3.DeleteObjectsInput{
|
|
Bucket: aws.String(bucket),
|
|
Delete: &types.Delete{
|
|
Objects: []types.ObjectIdentifier{
|
|
{
|
|
Key: aws.String(okKey),
|
|
ETag: okPutResp.ETag,
|
|
},
|
|
{
|
|
Key: aws.String(failKey),
|
|
ETag: aws.String(`"mismatched-etag"`),
|
|
},
|
|
},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
require.Len(t, deleteResp.Deleted, 1, "One object should satisfy its ETag precondition")
|
|
require.Len(t, deleteResp.Errors, 1, "One object should report a precondition failure")
|
|
deletedKeys := make([]string, 0, len(deleteResp.Deleted))
|
|
for _, deleted := range deleteResp.Deleted {
|
|
deletedKeys = append(deletedKeys, aws.ToString(deleted.Key))
|
|
}
|
|
assert.Contains(t, deletedKeys, okKey)
|
|
|
|
var matchedError *types.Error
|
|
for i := range deleteResp.Errors {
|
|
if aws.ToString(deleteResp.Errors[i].Key) == failKey {
|
|
matchedError = &deleteResp.Errors[i]
|
|
break
|
|
}
|
|
}
|
|
if assert.NotNil(t, matchedError, "Expected error entry for failed key") {
|
|
assert.Equal(t, "PreconditionFailed", aws.ToString(matchedError.Code))
|
|
}
|
|
|
|
_, err = client.HeadObject(context.TODO(), &s3.HeadObjectInput{
|
|
Bucket: aws.String(bucket),
|
|
Key: aws.String(okKey),
|
|
})
|
|
require.Error(t, err, "Successfully deleted key should no longer be current")
|
|
|
|
_, err = client.HeadObject(context.TODO(), &s3.HeadObjectInput{
|
|
Bucket: aws.String(bucket),
|
|
Key: aws.String(failKey),
|
|
})
|
|
require.NoError(t, err, "Object with mismatched ETag should remain untouched")
|
|
}
|