delimiter_test.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. package basic
  2. import (
  3. "fmt"
  4. "math/rand"
  5. "strings"
  6. "testing"
  7. "time"
  8. "github.com/aws/aws-sdk-go/aws"
  9. "github.com/aws/aws-sdk-go/service/s3"
  10. "github.com/stretchr/testify/assert"
  11. "github.com/stretchr/testify/require"
  12. )
  13. // TestS3ListDelimiterWithDirectoryKeyObjects tests the specific scenario from
  14. // test_bucket_list_delimiter_not_skip_special where directory key objects
  15. // should be properly grouped into common prefixes when using delimiters
  16. func TestS3ListDelimiterWithDirectoryKeyObjects(t *testing.T) {
  17. bucketName := fmt.Sprintf("test-delimiter-dir-key-%d", rand.Int31())
  18. // Create bucket
  19. _, err := svc.CreateBucket(&s3.CreateBucketInput{
  20. Bucket: aws.String(bucketName),
  21. })
  22. require.NoError(t, err)
  23. defer cleanupBucket(t, bucketName)
  24. // Create objects matching the failing test scenario:
  25. // ['0/'] + ['0/1000', '0/1001', '0/1002'] + ['1999', '1999#', '1999+', '2000']
  26. objects := []string{
  27. "0/", // Directory key object
  28. "0/1000", // Objects under 0/ prefix
  29. "0/1001",
  30. "0/1002",
  31. "1999", // Objects without delimiter
  32. "1999#",
  33. "1999+",
  34. "2000",
  35. }
  36. // Create all objects
  37. for _, key := range objects {
  38. _, err := svc.PutObject(&s3.PutObjectInput{
  39. Bucket: aws.String(bucketName),
  40. Key: aws.String(key),
  41. Body: strings.NewReader(fmt.Sprintf("content for %s", key)),
  42. })
  43. require.NoError(t, err, "Failed to create object %s", key)
  44. }
  45. // Test with delimiter='/'
  46. resp, err := svc.ListObjects(&s3.ListObjectsInput{
  47. Bucket: aws.String(bucketName),
  48. Delimiter: aws.String("/"),
  49. })
  50. require.NoError(t, err)
  51. // Extract keys and prefixes
  52. var keys []string
  53. for _, content := range resp.Contents {
  54. keys = append(keys, *content.Key)
  55. }
  56. var prefixes []string
  57. for _, prefix := range resp.CommonPrefixes {
  58. prefixes = append(prefixes, *prefix.Prefix)
  59. }
  60. // Expected results:
  61. // Keys should be: ['1999', '1999#', '1999+', '2000'] (objects without delimiters)
  62. // Prefixes should be: ['0/'] (grouping '0/' and all '0/xxxx' objects)
  63. expectedKeys := []string{"1999", "1999#", "1999+", "2000"}
  64. expectedPrefixes := []string{"0/"}
  65. t.Logf("Actual keys: %v", keys)
  66. t.Logf("Actual prefixes: %v", prefixes)
  67. assert.ElementsMatch(t, expectedKeys, keys, "Keys should only include objects without delimiters")
  68. assert.ElementsMatch(t, expectedPrefixes, prefixes, "CommonPrefixes should group directory key object with other objects sharing prefix")
  69. // Additional validation
  70. assert.Equal(t, "/", *resp.Delimiter, "Delimiter should be set correctly")
  71. assert.Contains(t, prefixes, "0/", "Directory key object '0/' should be grouped into common prefix '0/'")
  72. assert.NotContains(t, keys, "0/", "Directory key object '0/' should NOT appear as individual key when delimiter is used")
  73. // Verify none of the '0/xxxx' objects appear as individual keys
  74. for _, key := range keys {
  75. assert.False(t, strings.HasPrefix(key, "0/"), "No object with '0/' prefix should appear as individual key, found: %s", key)
  76. }
  77. }
  78. // TestS3ListWithoutDelimiter tests that directory key objects appear as individual keys when no delimiter is used
  79. func TestS3ListWithoutDelimiter(t *testing.T) {
  80. bucketName := fmt.Sprintf("test-no-delimiter-%d", rand.Int31())
  81. // Create bucket
  82. _, err := svc.CreateBucket(&s3.CreateBucketInput{
  83. Bucket: aws.String(bucketName),
  84. })
  85. require.NoError(t, err)
  86. defer cleanupBucket(t, bucketName)
  87. // Create objects
  88. objects := []string{"0/", "0/1000", "1999"}
  89. for _, key := range objects {
  90. _, err := svc.PutObject(&s3.PutObjectInput{
  91. Bucket: aws.String(bucketName),
  92. Key: aws.String(key),
  93. Body: strings.NewReader(fmt.Sprintf("content for %s", key)),
  94. })
  95. require.NoError(t, err)
  96. }
  97. // Test without delimiter
  98. resp, err := svc.ListObjects(&s3.ListObjectsInput{
  99. Bucket: aws.String(bucketName),
  100. // No delimiter specified
  101. })
  102. require.NoError(t, err)
  103. // Extract keys
  104. var keys []string
  105. for _, content := range resp.Contents {
  106. keys = append(keys, *content.Key)
  107. }
  108. // When no delimiter is used, all objects should be returned as individual keys
  109. expectedKeys := []string{"0/", "0/1000", "1999"}
  110. assert.ElementsMatch(t, expectedKeys, keys, "All objects should be individual keys when no delimiter is used")
  111. // No common prefixes should be present
  112. assert.Empty(t, resp.CommonPrefixes, "No common prefixes should be present when no delimiter is used")
  113. assert.Contains(t, keys, "0/", "Directory key object '0/' should appear as individual key when no delimiter is used")
  114. }
  115. func cleanupBucket(t *testing.T, bucketName string) {
  116. // Delete all objects
  117. resp, err := svc.ListObjects(&s3.ListObjectsInput{
  118. Bucket: aws.String(bucketName),
  119. })
  120. if err != nil {
  121. t.Logf("Failed to list objects for cleanup: %v", err)
  122. return
  123. }
  124. for _, obj := range resp.Contents {
  125. _, err := svc.DeleteObject(&s3.DeleteObjectInput{
  126. Bucket: aws.String(bucketName),
  127. Key: obj.Key,
  128. })
  129. if err != nil {
  130. t.Logf("Failed to delete object %s: %v", *obj.Key, err)
  131. }
  132. }
  133. // Give some time for eventual consistency
  134. time.Sleep(100 * time.Millisecond)
  135. // Delete bucket
  136. _, err = svc.DeleteBucket(&s3.DeleteBucketInput{
  137. Bucket: aws.String(bucketName),
  138. })
  139. if err != nil {
  140. t.Logf("Failed to delete bucket %s: %v", bucketName, err)
  141. }
  142. }