mirror of
https://github.com/versity/versitygw.git
synced 2026-10-22 06:12:39 +00:00
Merge pull request #2487 from LuisFigueroaG/fix/list-objects-v2-continuation-token-echo
fix: echo only the request continuation token in ListObjectsV2
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
// Copyright 2026 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 posix
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||
"github.com/versity/versitygw/s3response"
|
||||
)
|
||||
|
||||
// TestListObjectsV2ContinuationTokenEcho checks that ListObjectsV2 echoes
|
||||
// the request continuation token, and only that: start-after must not be
|
||||
// reported back as a continuation token.
|
||||
func TestListObjectsV2ContinuationTokenEcho(t *testing.T) {
|
||||
p := newTestPosix(t, metaModes(t)["xattr"])
|
||||
bucket := "testbucket"
|
||||
createTestBucket(t, p, bucket)
|
||||
|
||||
ctx := context.Background()
|
||||
for _, key := range []string{"a", "b", "c"} {
|
||||
_, err := p.PutObject(ctx, s3response.PutObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: aws.String(key),
|
||||
Body: strings.NewReader("data"),
|
||||
ContentLength: aws.Int64(4),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("put object %q: %v", key, err)
|
||||
}
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
cToken string
|
||||
startAfter string
|
||||
wantToken *string
|
||||
wantKeys []string
|
||||
}{
|
||||
{"no token or start-after", "", "", nil, []string{"a", "b", "c"}},
|
||||
{"start-after only", "", "a", nil, []string{"b", "c"}},
|
||||
{"continuation token only", "a", "", aws.String("a"), []string{"b", "c"}},
|
||||
{"start-after past the continuation token", "a", "b", aws.String("a"), []string{"c"}},
|
||||
{"continuation token past start-after", "b", "a", aws.String("b"), []string{"c"}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
out, err := p.ListObjectsV2(ctx, &s3.ListObjectsV2Input{
|
||||
Bucket: &bucket,
|
||||
ContinuationToken: &tt.cToken,
|
||||
StartAfter: &tt.startAfter,
|
||||
MaxKeys: aws.Int32(1000),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("list objects v2: %v", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(out.ContinuationToken, tt.wantToken) {
|
||||
t.Fatalf("continuation token = %q, want %q",
|
||||
aws.ToString(out.ContinuationToken), aws.ToString(tt.wantToken))
|
||||
}
|
||||
|
||||
var keys []string
|
||||
for _, obj := range out.Contents {
|
||||
keys = append(keys, aws.ToString(obj.Key))
|
||||
}
|
||||
if strings.Join(keys, ",") != strings.Join(tt.wantKeys, ",") {
|
||||
t.Fatalf("keys = %v, want %v", keys, tt.wantKeys)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -7165,7 +7165,7 @@ func (p *Posix) ListObjectsV2Parametrized(ctx context.Context, input *s3.ListObj
|
||||
Name: &bucket,
|
||||
KeyCount: &count,
|
||||
Delimiter: backend.GetPtrFromString(delim),
|
||||
ContinuationToken: backend.GetPtrFromString(marker),
|
||||
ContinuationToken: backend.GetPtrFromString(backend.GetStringFromPtr(input.ContinuationToken)),
|
||||
NextContinuationToken: backend.GetPtrFromString(results.NextMarker),
|
||||
Prefix: backend.GetPtrFromString(prefix),
|
||||
StartAfter: backend.GetPtrFromString(*input.StartAfter),
|
||||
|
||||
@@ -175,6 +175,75 @@ func ListObjectsV2_start_after_empty_result(s *S3Conf) error {
|
||||
})
|
||||
}
|
||||
|
||||
func ListObjectsV2_start_after_continuation_token(s *S3Conf) error {
|
||||
testName := "ListObjectsV2_start_after_continuation_token"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
contents, err := putObjects(s3client, []string{"foo", "bar", "baz", "quxx"}, bucket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// start-after alone is not echoed back as a continuation token
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
out, err := s3client.ListObjectsV2(ctx, &s3.ListObjectsV2Input{
|
||||
Bucket: &bucket,
|
||||
StartAfter: getPtr("bar"),
|
||||
})
|
||||
cancel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if out.ContinuationToken != nil {
|
||||
return fmt.Errorf("expected nil ContinuationToken, instead got %v",
|
||||
*out.ContinuationToken)
|
||||
}
|
||||
if !compareObjects(contents[1:], out.Contents) {
|
||||
return fmt.Errorf("expected the output to be %v, instead got %v",
|
||||
contents[1:], out.Contents)
|
||||
}
|
||||
|
||||
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
||||
out, err = s3client.ListObjectsV2(ctx, &s3.ListObjectsV2Input{
|
||||
Bucket: &bucket,
|
||||
MaxKeys: getPtr(int32(1)),
|
||||
})
|
||||
cancel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
token := getString(out.NextContinuationToken)
|
||||
if token == "" {
|
||||
return fmt.Errorf("expected non-empty NextContinuationToken")
|
||||
}
|
||||
|
||||
// with both, the request continuation token is echoed back even
|
||||
// when start-after is past it
|
||||
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
||||
out, err = s3client.ListObjectsV2(ctx, &s3.ListObjectsV2Input{
|
||||
Bucket: &bucket,
|
||||
ContinuationToken: &token,
|
||||
StartAfter: getPtr("baz"),
|
||||
})
|
||||
cancel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if getString(out.ContinuationToken) != token {
|
||||
return fmt.Errorf("expected ContinuationToken to be %v, instead got %v",
|
||||
token, getString(out.ContinuationToken))
|
||||
}
|
||||
if !compareObjects(contents[2:], out.Contents) {
|
||||
return fmt.Errorf("expected the output to be %v, instead got %v",
|
||||
contents[2:], out.Contents)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func ListObjectsV2_both_delimiter_and_prefix(s *S3Conf) error {
|
||||
testName := "ListObjectsV2_both_delimiter_and_prefix"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
|
||||
@@ -335,6 +335,7 @@ func TestListObjectsV2(ts *TestState) {
|
||||
ts.Run(ListObjectsV2_both_start_after_and_continuation_token)
|
||||
ts.Run(ListObjectsV2_start_after_not_in_list)
|
||||
ts.Run(ListObjectsV2_start_after_empty_result)
|
||||
ts.Run(ListObjectsV2_start_after_continuation_token)
|
||||
ts.Run(ListObjectsV2_both_delimiter_and_prefix)
|
||||
ts.Run(ListObjectsV2_single_dir_object_with_delim_and_prefix)
|
||||
ts.Run(ListObjectsV2_truncated_common_prefixes)
|
||||
@@ -3163,6 +3164,7 @@ func GetIntTests() IntTests {
|
||||
"ListObjectsV2_both_start_after_and_continuation_token": ListObjectsV2_both_start_after_and_continuation_token,
|
||||
"ListObjectsV2_start_after_not_in_list": ListObjectsV2_start_after_not_in_list,
|
||||
"ListObjectsV2_start_after_empty_result": ListObjectsV2_start_after_empty_result,
|
||||
"ListObjectsV2_start_after_continuation_token": ListObjectsV2_start_after_continuation_token,
|
||||
"ListObjectsV2_both_delimiter_and_prefix": ListObjectsV2_both_delimiter_and_prefix,
|
||||
"ListObjectsV2_single_dir_object_with_delim_and_prefix": ListObjectsV2_single_dir_object_with_delim_and_prefix,
|
||||
"ListObjectsV2_truncated_common_prefixes": ListObjectsV2_truncated_common_prefixes,
|
||||
|
||||
@@ -267,9 +267,6 @@ source ./tests/drivers/objects_and_versions.sh
|
||||
|
||||
# tags: curl,ListObjectsV2,start-after,continuation-token
|
||||
@test "ListObjectsV1 - start-after - doesn't include continuation token" {
|
||||
if [ "$DIRECT" != "true" ]; then
|
||||
skip "https://github.com/versity/versitygw/issues/2007"
|
||||
fi
|
||||
run setup_bucket_and_files_v3 "$BUCKET_ONE_NAME" 2
|
||||
assert_success
|
||||
read -r bucket_name file_one file_two <<< "$output"
|
||||
|
||||
Reference in New Issue
Block a user