2021-04-18 15:41:13 -04:00
// Copyright (c) 2015-2021 MinIO, Inc.
//
// This file is part of MinIO Object Storage stack
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
2016-03-16 20:30:22 -04:00
2016-08-18 19:23:42 -04:00
package cmd
2016-03-16 20:30:22 -04:00
import (
"bytes"
2018-03-15 16:27:16 -04:00
"context"
2017-10-22 01:30:34 -04:00
"crypto/md5"
"encoding/hex"
2016-03-16 20:30:22 -04:00
"fmt"
"strconv"
"strings"
"testing"
)
2021-12-02 11:46:33 -05:00
func TestListObjectsVersionedFolders ( t * testing . T ) {
ExecObjectLayerTest ( t , testListObjectsVersionedFolders )
}
func testListObjectsVersionedFolders ( obj ObjectLayer , instanceType string , t1 TestErrHandler ) {
t , _ := t1 . ( * testing . T )
testBuckets := [ ] string {
// This bucket is used for testing ListObject operations.
"test-bucket-folders" ,
2021-12-08 20:34:52 -05:00
// This bucket has file delete marker.
"test-bucket-files" ,
2021-12-02 11:46:33 -05:00
}
for _ , bucket := range testBuckets {
2022-12-23 10:46:00 -05:00
err := obj . MakeBucket ( context . Background ( ) , bucket , MakeBucketOptions {
2021-12-02 11:46:33 -05:00
VersioningEnabled : true ,
} )
if err != nil {
t . Fatalf ( "%s : %s" , instanceType , err . Error ( ) )
}
}
var err error
testObjects := [ ] struct {
parentBucket string
name string
content string
meta map [ string ] string
addDeleteMarker bool
} {
{ testBuckets [ 0 ] , "unique/folder/" , "" , nil , true } ,
{ testBuckets [ 0 ] , "unique/folder/1.txt" , "content" , nil , false } ,
2021-12-08 20:34:52 -05:00
{ testBuckets [ 1 ] , "unique/folder/1.txt" , "content" , nil , true } ,
2021-12-02 11:46:33 -05:00
}
for _ , object := range testObjects {
md5Bytes := md5 . Sum ( [ ] byte ( object . content ) )
_ , err = obj . PutObject ( context . Background ( ) , object . parentBucket , object . name , mustGetPutObjReader ( t , bytes . NewBufferString ( object . content ) ,
int64 ( len ( object . content ) ) , hex . EncodeToString ( md5Bytes [ : ] ) , "" ) , ObjectOptions {
2022-05-06 22:05:28 -04:00
Versioned : globalBucketVersioningSys . PrefixEnabled ( object . parentBucket , object . name ) ,
2021-12-02 11:46:33 -05:00
UserDefined : object . meta ,
} )
if err != nil {
t . Fatalf ( "%s : %s" , instanceType , err . Error ( ) )
}
if object . addDeleteMarker {
oi , err := obj . DeleteObject ( context . Background ( ) , object . parentBucket , object . name , ObjectOptions {
2022-05-06 22:05:28 -04:00
Versioned : globalBucketVersioningSys . PrefixEnabled ( object . parentBucket , object . name ) ,
2021-12-02 11:46:33 -05:00
} )
if err != nil {
t . Fatalf ( "%s : %s" , instanceType , err . Error ( ) )
}
if oi . DeleteMarker != object . addDeleteMarker {
t . Fatalf ( "Expected, marker %t : got %t" , object . addDeleteMarker , oi . DeleteMarker )
}
}
}
// Formulating the result data set to be expected from ListObjects call inside the tests,
// This will be used in testCases and used for asserting the correctness of ListObjects output in the tests.
resultCases := [ ] ListObjectsInfo {
{
IsTruncated : false ,
Prefixes : [ ] string { "unique/folder/" } ,
} ,
{
IsTruncated : false ,
Objects : [ ] ObjectInfo {
{ Name : "unique/folder/1.txt" } ,
} ,
} ,
2021-12-08 20:34:52 -05:00
{
IsTruncated : false ,
Objects : [ ] ObjectInfo { } ,
} ,
2021-12-02 11:46:33 -05:00
}
2021-12-09 17:59:23 -05:00
resultCasesV := [ ] ListObjectVersionsInfo {
{
IsTruncated : false ,
Prefixes : [ ] string { "unique/folder/" } ,
} ,
{
IsTruncated : false ,
Objects : [ ] ObjectInfo {
{
Name : "unique/folder/" ,
DeleteMarker : true ,
} ,
{
Name : "unique/folder/" ,
DeleteMarker : false ,
} ,
{
Name : "unique/folder/1.txt" ,
DeleteMarker : false ,
} ,
} ,
} ,
}
2021-12-02 11:46:33 -05:00
testCases := [ ] struct {
// Inputs to ListObjects.
bucketName string
prefix string
marker string
delimiter string
2021-12-09 17:59:23 -05:00
maxKeys int
versioned bool
2021-12-02 11:46:33 -05:00
// Expected output of ListObjects.
2021-12-09 17:59:23 -05:00
resultL ListObjectsInfo
resultV ListObjectVersionsInfo
err error
2021-12-02 11:46:33 -05:00
// Flag indicating whether the test is expected to pass or not.
shouldPass bool
} {
2021-12-09 17:59:23 -05:00
{ testBuckets [ 0 ] , "unique/" , "" , "/" , 1000 , false , resultCases [ 0 ] , ListObjectVersionsInfo { } , nil , true } ,
{ testBuckets [ 0 ] , "unique/folder" , "" , "/" , 1000 , false , resultCases [ 0 ] , ListObjectVersionsInfo { } , nil , true } ,
{ testBuckets [ 0 ] , "unique/" , "" , "" , 1000 , false , resultCases [ 1 ] , ListObjectVersionsInfo { } , nil , true } ,
{ testBuckets [ 1 ] , "unique/" , "" , "/" , 1000 , false , resultCases [ 0 ] , ListObjectVersionsInfo { } , nil , true } ,
{ testBuckets [ 1 ] , "unique/folder/" , "" , "/" , 1000 , false , resultCases [ 2 ] , ListObjectVersionsInfo { } , nil , true } ,
{ testBuckets [ 0 ] , "unique/" , "" , "/" , 1000 , true , ListObjectsInfo { } , resultCasesV [ 0 ] , nil , true } ,
{ testBuckets [ 0 ] , "unique/" , "" , "" , 1000 , true , ListObjectsInfo { } , resultCasesV [ 1 ] , nil , true } ,
2021-12-02 11:46:33 -05:00
}
for i , testCase := range testCases {
testCase := testCase
t . Run ( fmt . Sprintf ( "%s-Test%d" , instanceType , i + 1 ) , func ( t * testing . T ) {
2021-12-09 17:59:23 -05:00
t . Log ( "ListObjects, bucket:" , testCase . bucketName , "prefix:" ,
testCase . prefix , "marker:" , testCase . marker , "delimiter:" ,
testCase . delimiter , "maxkeys:" , testCase . maxKeys )
var err error
var resultL ListObjectsInfo
var resultV ListObjectVersionsInfo
if testCase . versioned {
resultV , err = obj . ListObjectVersions ( context . Background ( ) , testCase . bucketName ,
testCase . prefix , testCase . marker , "" , testCase . delimiter , testCase . maxKeys )
} else {
resultL , err = obj . ListObjects ( context . Background ( ) , testCase . bucketName ,
testCase . prefix , testCase . marker , testCase . delimiter , testCase . maxKeys )
}
2021-12-02 11:46:33 -05:00
if err != nil && testCase . shouldPass {
t . Errorf ( "Test %d: %s: Expected to pass, but failed with: <ERROR> %s" , i + 1 , instanceType , err . Error ( ) )
}
if err == nil && ! testCase . shouldPass {
t . Errorf ( "Test %d: %s: Expected to fail with <ERROR> \"%s\", but passed instead" , i + 1 , instanceType , testCase . err . Error ( ) )
}
// Failed as expected, but does it fail for the expected reason.
if err != nil && ! testCase . shouldPass {
if ! strings . Contains ( err . Error ( ) , testCase . err . Error ( ) ) {
t . Errorf ( "Test %d: %s: Expected to fail with error \"%s\", but instead failed with error \"%s\" instead" , i + 1 , instanceType , testCase . err . Error ( ) , err . Error ( ) )
}
}
// Since there are cases for which ListObjects fails, this is
// necessary. Test passes as expected, but the output values
// are verified for correctness here.
if err == nil && testCase . shouldPass {
// The length of the expected ListObjectsResult.Objects
// should match in both expected result from test cases
// and in the output. On failure calling t.Fatalf,
// otherwise it may lead to index out of range error in
// assertion following this.
2021-12-09 17:59:23 -05:00
if ! testCase . versioned {
if len ( testCase . resultL . Objects ) != len ( resultL . Objects ) {
t . Logf ( "want: %v" , objInfoNames ( testCase . resultL . Objects ) )
t . Logf ( "got: %v" , objInfoNames ( resultL . Objects ) )
t . Errorf ( "Test %d: %s: Expected number of object in the result to be '%d', but found '%d' objects instead" , i + 1 , instanceType , len ( testCase . resultL . Objects ) , len ( resultL . Objects ) )
2021-12-02 11:46:33 -05:00
}
2021-12-09 17:59:23 -05:00
for j := 0 ; j < len ( testCase . resultL . Objects ) ; j ++ {
if j >= len ( resultL . Objects ) {
t . Errorf ( "Test %d: %s: Expected object name to be \"%s\", but not nothing instead" , i + 1 , instanceType , testCase . resultL . Objects [ j ] . Name )
continue
}
if testCase . resultL . Objects [ j ] . Name != resultL . Objects [ j ] . Name {
t . Errorf ( "Test %d: %s: Expected object name to be \"%s\", but found \"%s\" instead" , i + 1 , instanceType , testCase . resultL . Objects [ j ] . Name , resultL . Objects [ j ] . Name )
}
2021-12-02 11:46:33 -05:00
}
2021-12-09 17:59:23 -05:00
if len ( testCase . resultL . Prefixes ) != len ( resultL . Prefixes ) {
t . Logf ( "want: %v" , testCase . resultL . Prefixes )
t . Logf ( "got: %v" , resultL . Prefixes )
t . Errorf ( "Test %d: %s: Expected number of prefixes in the result to be '%d', but found '%d' prefixes instead" , i + 1 , instanceType , len ( testCase . resultL . Prefixes ) , len ( resultL . Prefixes ) )
2021-12-02 11:46:33 -05:00
}
2021-12-09 17:59:23 -05:00
for j := 0 ; j < len ( testCase . resultL . Prefixes ) ; j ++ {
if j >= len ( resultL . Prefixes ) {
t . Errorf ( "Test %d: %s: Expected prefix name to be \"%s\", but found no result" , i + 1 , instanceType , testCase . resultL . Prefixes [ j ] )
continue
}
if testCase . resultL . Prefixes [ j ] != resultL . Prefixes [ j ] {
t . Errorf ( "Test %d: %s: Expected prefix name to be \"%s\", but found \"%s\" instead" , i + 1 , instanceType , testCase . resultL . Prefixes [ j ] , resultL . Prefixes [ j ] )
}
2021-12-02 11:46:33 -05:00
}
2021-12-09 17:59:23 -05:00
if testCase . resultL . IsTruncated != resultL . IsTruncated {
// Allow an extra continuation token.
if ! resultL . IsTruncated || len ( resultL . Objects ) == 0 {
t . Errorf ( "Test %d: %s: Expected IsTruncated flag to be %v, but instead found it to be %v" , i + 1 , instanceType , testCase . resultL . IsTruncated , resultL . IsTruncated )
}
2021-12-02 11:46:33 -05:00
}
2021-12-09 17:59:23 -05:00
if testCase . resultL . IsTruncated && resultL . NextMarker == "" {
t . Errorf ( "Test %d: %s: Expected NextMarker to contain a string since listing is truncated, but instead found it to be empty" , i + 1 , instanceType )
}
2021-12-02 11:46:33 -05:00
2021-12-09 17:59:23 -05:00
if ! testCase . resultL . IsTruncated && resultL . NextMarker != "" {
if ! resultL . IsTruncated || len ( resultL . Objects ) == 0 {
t . Errorf ( "Test %d: %s: Expected NextMarker to be empty since listing is not truncated, but instead found `%v`" , i + 1 , instanceType , resultL . NextMarker )
}
}
} else {
if len ( testCase . resultV . Objects ) != len ( resultV . Objects ) {
t . Logf ( "want: %v" , objInfoNames ( testCase . resultV . Objects ) )
t . Logf ( "got: %v" , objInfoNames ( resultV . Objects ) )
t . Errorf ( "Test %d: %s: Expected number of object in the result to be '%d', but found '%d' objects instead" , i + 1 , instanceType , len ( testCase . resultV . Objects ) , len ( resultV . Objects ) )
}
for j := 0 ; j < len ( testCase . resultV . Objects ) ; j ++ {
if j >= len ( resultV . Objects ) {
t . Errorf ( "Test %d: %s: Expected object name to be \"%s\", but not nothing instead" , i + 1 , instanceType , testCase . resultV . Objects [ j ] . Name )
continue
}
if testCase . resultV . Objects [ j ] . Name != resultV . Objects [ j ] . Name {
t . Errorf ( "Test %d: %s: Expected object name to be \"%s\", but found \"%s\" instead" , i + 1 , instanceType , testCase . resultV . Objects [ j ] . Name , resultV . Objects [ j ] . Name )
}
2021-12-02 11:46:33 -05:00
}
2021-12-09 17:59:23 -05:00
if len ( testCase . resultV . Prefixes ) != len ( resultV . Prefixes ) {
t . Logf ( "want: %v" , testCase . resultV . Prefixes )
t . Logf ( "got: %v" , resultV . Prefixes )
t . Errorf ( "Test %d: %s: Expected number of prefixes in the result to be '%d', but found '%d' prefixes instead" , i + 1 , instanceType , len ( testCase . resultV . Prefixes ) , len ( resultV . Prefixes ) )
}
for j := 0 ; j < len ( testCase . resultV . Prefixes ) ; j ++ {
if j >= len ( resultV . Prefixes ) {
t . Errorf ( "Test %d: %s: Expected prefix name to be \"%s\", but found no result" , i + 1 , instanceType , testCase . resultV . Prefixes [ j ] )
continue
}
if testCase . resultV . Prefixes [ j ] != resultV . Prefixes [ j ] {
t . Errorf ( "Test %d: %s: Expected prefix name to be \"%s\", but found \"%s\" instead" , i + 1 , instanceType , testCase . resultV . Prefixes [ j ] , resultV . Prefixes [ j ] )
}
}
if testCase . resultV . IsTruncated != resultV . IsTruncated {
// Allow an extra continuation token.
if ! resultV . IsTruncated || len ( resultV . Objects ) == 0 {
t . Errorf ( "Test %d: %s: Expected IsTruncated flag to be %v, but instead found it to be %v" , i + 1 , instanceType , testCase . resultV . IsTruncated , resultV . IsTruncated )
}
}
if testCase . resultV . IsTruncated && resultV . NextMarker == "" {
t . Errorf ( "Test %d: %s: Expected NextMarker to contain a string since listing is truncated, but instead found it to be empty" , i + 1 , instanceType )
}
if ! testCase . resultV . IsTruncated && resultV . NextMarker != "" {
if ! resultV . IsTruncated || len ( resultV . Objects ) == 0 {
t . Errorf ( "Test %d: %s: Expected NextMarker to be empty since listing is not truncated, but instead found `%v`" , i + 1 , instanceType , resultV . NextMarker )
}
}
}
2021-12-02 11:46:33 -05:00
}
} )
}
}
// Wrapper for calling ListObjectsOnVersionedBuckets tests for both
// Erasure multiple disks and single node setup.
func TestListObjectsOnVersionedBuckets ( t * testing . T ) {
ExecObjectLayerTest ( t , testListObjectsOnVersionedBuckets )
}
// Wrapper for calling ListObjects tests for both Erasure multiple
// disks and single node setup.
2016-03-16 20:30:22 -04:00
func TestListObjects ( t * testing . T ) {
2016-05-06 14:57:04 -04:00
ExecObjectLayerTest ( t , testListObjects )
}
2016-03-16 20:30:22 -04:00
2021-12-02 11:46:33 -05:00
// Unit test for ListObjects on VersionedBucket.
func testListObjectsOnVersionedBuckets ( obj ObjectLayer , instanceType string , t1 TestErrHandler ) {
_testListObjects ( obj , instanceType , t1 , true )
}
// Unit test for ListObjects.
2019-05-02 01:06:57 -04:00
func testListObjects ( obj ObjectLayer , instanceType string , t1 TestErrHandler ) {
2021-12-02 11:46:33 -05:00
_testListObjects ( obj , instanceType , t1 , false )
}
func _testListObjects ( obj ObjectLayer , instanceType string , t1 TestErrHandler , versioned bool ) {
2019-05-02 01:06:57 -04:00
t , _ := t1 . ( * testing . T )
2016-07-24 18:52:12 -04:00
testBuckets := [ ] string {
// This bucket is used for testing ListObject operations.
2022-11-22 11:51:04 -05:00
0 : "test-bucket-list-object" ,
2019-05-06 10:52:42 -04:00
// This bucket will be tested with empty directories
2022-11-22 11:51:04 -05:00
1 : "test-bucket-empty-dir" ,
2016-07-24 18:52:12 -04:00
// Will not store any objects in this bucket,
// Its to test ListObjects on an empty bucket.
2022-11-22 11:51:04 -05:00
2 : "empty-bucket" ,
2020-03-13 20:43:00 -04:00
// Listing the case where the marker > last object.
2022-11-22 11:51:04 -05:00
3 : "test-bucket-single-object" ,
2020-11-04 10:56:58 -05:00
// Listing uncommon delimiter.
2022-11-22 11:51:04 -05:00
4 : "test-bucket-delimiter" ,
2020-12-19 12:36:04 -05:00
// Listing prefixes > maxKeys
2022-11-22 11:51:04 -05:00
5 : "test-bucket-max-keys-prefixes" ,
// Listing custom delimiters
6 : "test-bucket-custom-delimiter" ,
2016-03-16 20:30:22 -04:00
}
2016-07-24 18:52:12 -04:00
for _ , bucket := range testBuckets {
2022-12-23 10:46:00 -05:00
err := obj . MakeBucket ( context . Background ( ) , bucket , MakeBucketOptions {
2021-12-02 11:46:33 -05:00
VersioningEnabled : versioned ,
} )
2016-03-16 20:30:22 -04:00
if err != nil {
2016-05-06 14:57:04 -04:00
t . Fatalf ( "%s : %s" , instanceType , err . Error ( ) )
2016-03-16 20:30:22 -04:00
}
}
2016-07-24 18:52:12 -04:00
var err error
testObjects := [ ] struct {
2019-05-06 10:52:42 -04:00
parentBucket string
name string
content string
meta map [ string ] string
2016-07-24 18:52:12 -04:00
} {
2019-05-06 10:52:42 -04:00
{ testBuckets [ 0 ] , "Asia-maps.png" , "asis-maps" , map [ string ] string { "content-type" : "image/png" } } ,
{ testBuckets [ 0 ] , "Asia/India/India-summer-photos-1" , "contentstring" , nil } ,
{ testBuckets [ 0 ] , "Asia/India/Karnataka/Bangalore/Koramangala/pics" , "contentstring" , nil } ,
{ testBuckets [ 0 ] , "newPrefix0" , "newPrefix0" , nil } ,
{ testBuckets [ 0 ] , "newPrefix1" , "newPrefix1" , nil } ,
{ testBuckets [ 0 ] , "newzen/zen/recurse/again/again/again/pics" , "recurse" , nil } ,
{ testBuckets [ 0 ] , "obj0" , "obj0" , nil } ,
{ testBuckets [ 0 ] , "obj1" , "obj1" , nil } ,
{ testBuckets [ 0 ] , "obj2" , "obj2" , nil } ,
{ testBuckets [ 1 ] , "obj1" , "obj1" , nil } ,
{ testBuckets [ 1 ] , "obj2" , "obj2" , nil } ,
{ testBuckets [ 1 ] , "temporary/0/" , "" , nil } ,
2020-03-13 20:43:00 -04:00
{ testBuckets [ 3 ] , "A/B" , "contentstring" , nil } ,
2020-11-04 10:56:58 -05:00
{ testBuckets [ 4 ] , "file1/receipt.json" , "content" , nil } ,
{ testBuckets [ 4 ] , "file1/guidSplunk-aaaa/file" , "content" , nil } ,
2020-12-19 12:36:04 -05:00
{ testBuckets [ 5 ] , "dir/day_id=2017-10-10/issue" , "content" , nil } ,
{ testBuckets [ 5 ] , "dir/day_id=2017-10-11/issue" , "content" , nil } ,
2021-08-18 10:40:53 -04:00
{ testBuckets [ 5 ] , "foo/201910/1122" , "content" , nil } ,
{ testBuckets [ 5 ] , "foo/201910/1112" , "content" , nil } ,
{ testBuckets [ 5 ] , "foo/201910/2112" , "content" , nil } ,
{ testBuckets [ 5 ] , "foo/201910_txt" , "content" , nil } ,
2021-08-21 03:12:29 -04:00
{ testBuckets [ 5 ] , "201910/foo/bar/xl.meta/1.txt" , "content" , nil } ,
2022-11-22 11:51:04 -05:00
{ testBuckets [ 6 ] , "aaa" , "content" , nil } ,
{ testBuckets [ 6 ] , "bbb_aaa" , "content" , nil } ,
{ testBuckets [ 6 ] , "bbb_aaa" , "content" , nil } ,
{ testBuckets [ 6 ] , "ccc" , "content" , nil } ,
2016-07-24 18:52:12 -04:00
}
for _ , object := range testObjects {
2017-10-22 01:30:34 -04:00
md5Bytes := md5 . Sum ( [ ] byte ( object . content ) )
2021-12-02 11:46:33 -05:00
_ , err = obj . PutObject ( context . Background ( ) , object . parentBucket , object . name ,
mustGetPutObjReader ( t , bytes . NewBufferString ( object . content ) ,
int64 ( len ( object . content ) ) , hex . EncodeToString ( md5Bytes [ : ] ) , "" ) , ObjectOptions {
2022-05-06 22:05:28 -04:00
Versioned : globalBucketVersioningSys . PrefixEnabled ( object . parentBucket , object . name ) ,
2021-12-02 11:46:33 -05:00
UserDefined : object . meta ,
} )
2016-03-16 20:30:22 -04:00
if err != nil {
2016-05-06 14:57:04 -04:00
t . Fatalf ( "%s : %s" , instanceType , err . Error ( ) )
2016-03-16 20:30:22 -04:00
}
2016-07-24 18:52:12 -04:00
2016-03-16 20:30:22 -04:00
}
2021-12-02 11:46:33 -05:00
// Formulating the result data set to be expected from ListObjects call inside the tests,
2016-03-16 20:30:22 -04:00
// This will be used in testCases and used for asserting the correctness of ListObjects output in the tests.
objectAPI: Fix object API interface, remove unnecessary structs.
ObjectAPI changes.
```
ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, *probe.Error)
ListMultipartUploads(bucket, objectPrefix, keyMarker, uploadIDMarker, delimiter string, maxUploads int) (ListMultipartsInfo, *probe.Error)
ListObjectParts(bucket, object, uploadID string, partNumberMarker, maxParts int) (ListPartsInfo, *probe.Error)
CompleteMultipartUpload(bucket string, object string, uploadID string, parts []completePart) (ObjectInfo, *probe.Error)
```
2016-04-03 04:34:20 -04:00
resultCases := [ ] ListObjectsInfo {
2016-03-16 20:30:22 -04:00
// ListObjectsResult-0.
// Testing for listing all objects in the bucket, (testCase 20,21,22).
2023-09-25 11:13:08 -04:00
0 : {
2016-03-16 20:30:22 -04:00
IsTruncated : false ,
Objects : [ ] ObjectInfo {
2017-04-04 12:14:03 -04:00
{ Name : "Asia-maps.png" } ,
2016-03-22 06:18:22 -04:00
{ Name : "Asia/India/India-summer-photos-1" } ,
{ Name : "Asia/India/Karnataka/Bangalore/Koramangala/pics" } ,
{ Name : "newPrefix0" } ,
{ Name : "newPrefix1" } ,
{ Name : "newzen/zen/recurse/again/again/again/pics" } ,
{ Name : "obj0" } ,
{ Name : "obj1" } ,
{ Name : "obj2" } ,
2016-03-16 20:30:22 -04:00
} ,
} ,
// ListObjectsResult-1.
// Used for asserting the truncated case, (testCase 23).
2023-09-25 11:13:08 -04:00
1 : {
2016-03-16 20:30:22 -04:00
IsTruncated : true ,
Objects : [ ] ObjectInfo {
2017-04-04 12:14:03 -04:00
{ Name : "Asia-maps.png" } ,
2016-03-22 06:18:22 -04:00
{ Name : "Asia/India/India-summer-photos-1" } ,
{ Name : "Asia/India/Karnataka/Bangalore/Koramangala/pics" } ,
{ Name : "newPrefix0" } ,
{ Name : "newPrefix1" } ,
2016-03-16 20:30:22 -04:00
} ,
} ,
// ListObjectsResult-2.
// (TestCase 24).
2023-09-25 11:13:08 -04:00
2 : {
2016-03-16 20:30:22 -04:00
IsTruncated : true ,
Objects : [ ] ObjectInfo {
2017-04-04 12:14:03 -04:00
{ Name : "Asia-maps.png" } ,
2016-03-22 06:18:22 -04:00
{ Name : "Asia/India/India-summer-photos-1" } ,
{ Name : "Asia/India/Karnataka/Bangalore/Koramangala/pics" } ,
{ Name : "newPrefix0" } ,
2016-03-16 20:30:22 -04:00
} ,
} ,
// ListObjectsResult-3.
// (TestCase 25).
2023-09-25 11:13:08 -04:00
3 : {
2016-03-16 20:30:22 -04:00
IsTruncated : true ,
Objects : [ ] ObjectInfo {
2017-04-04 12:14:03 -04:00
{ Name : "Asia-maps.png" } ,
2016-03-22 06:18:22 -04:00
{ Name : "Asia/India/India-summer-photos-1" } ,
{ Name : "Asia/India/Karnataka/Bangalore/Koramangala/pics" } ,
2016-03-16 20:30:22 -04:00
} ,
} ,
// ListObjectsResult-4.
// Again used for truncated case.
// (TestCase 26).
2023-09-25 11:13:08 -04:00
4 : {
2016-03-16 20:30:22 -04:00
IsTruncated : true ,
Objects : [ ] ObjectInfo {
2017-04-04 12:14:03 -04:00
{ Name : "Asia-maps.png" } ,
2016-03-16 20:30:22 -04:00
} ,
} ,
// ListObjectsResult-5.
// Used for Asserting prefixes.
// Used for test case with prefix "new", (testCase 27-29).
2023-09-25 11:13:08 -04:00
5 : {
2016-03-16 20:30:22 -04:00
IsTruncated : false ,
Objects : [ ] ObjectInfo {
2016-03-22 06:18:22 -04:00
{ Name : "newPrefix0" } ,
{ Name : "newPrefix1" } ,
{ Name : "newzen/zen/recurse/again/again/again/pics" } ,
2016-03-16 20:30:22 -04:00
} ,
} ,
// ListObjectsResult-6.
// Used for Asserting prefixes.
// Used for test case with prefix = "obj", (testCase 30).
2023-09-25 11:13:08 -04:00
6 : {
2016-03-16 20:30:22 -04:00
IsTruncated : false ,
Objects : [ ] ObjectInfo {
2016-03-22 06:18:22 -04:00
{ Name : "obj0" } ,
{ Name : "obj1" } ,
{ Name : "obj2" } ,
2016-03-16 20:30:22 -04:00
} ,
} ,
// ListObjectsResult-7.
// Used for Asserting prefixes and truncation.
// Used for test case with prefix = "new" and maxKeys = 1, (testCase 31).
2023-09-25 11:13:08 -04:00
7 : {
2016-03-16 20:30:22 -04:00
IsTruncated : true ,
Objects : [ ] ObjectInfo {
2016-03-22 06:18:22 -04:00
{ Name : "newPrefix0" } ,
2016-03-16 20:30:22 -04:00
} ,
} ,
// ListObjectsResult-8.
// Used for Asserting prefixes.
// Used for test case with prefix = "obj" and maxKeys = 2, (testCase 32).
2023-09-25 11:13:08 -04:00
8 : {
2016-03-16 20:30:22 -04:00
IsTruncated : true ,
Objects : [ ] ObjectInfo {
2016-03-22 06:18:22 -04:00
{ Name : "obj0" } ,
{ Name : "obj1" } ,
2016-03-16 20:30:22 -04:00
} ,
} ,
// ListObjectsResult-9.
// Used for asserting the case with marker, but without prefix.
2021-11-16 12:28:29 -05:00
// marker is set to "newPrefix0" in the testCase, (testCase 33).
2023-09-25 11:13:08 -04:00
9 : {
2016-03-16 20:30:22 -04:00
IsTruncated : false ,
Objects : [ ] ObjectInfo {
2016-03-22 06:18:22 -04:00
{ Name : "newPrefix1" } ,
{ Name : "newzen/zen/recurse/again/again/again/pics" } ,
{ Name : "obj0" } ,
{ Name : "obj1" } ,
{ Name : "obj2" } ,
2016-03-16 20:30:22 -04:00
} ,
} ,
// ListObjectsResult-10.
2021-11-16 12:28:29 -05:00
// marker is set to "newPrefix1" in the testCase, (testCase 34).
2023-09-25 11:13:08 -04:00
10 : {
2016-03-16 20:30:22 -04:00
IsTruncated : false ,
Objects : [ ] ObjectInfo {
2016-03-22 06:18:22 -04:00
{ Name : "newzen/zen/recurse/again/again/again/pics" } ,
{ Name : "obj0" } ,
{ Name : "obj1" } ,
{ Name : "obj2" } ,
2016-03-16 20:30:22 -04:00
} ,
} ,
// ListObjectsResult-11.
2021-11-16 12:28:29 -05:00
// marker is set to "obj0" in the testCase, (testCase 35).
2023-09-25 11:13:08 -04:00
11 : {
2016-03-16 20:30:22 -04:00
IsTruncated : false ,
Objects : [ ] ObjectInfo {
2016-03-22 06:18:22 -04:00
{ Name : "obj1" } ,
{ Name : "obj2" } ,
2016-03-16 20:30:22 -04:00
} ,
} ,
// ListObjectsResult-12.
// Marker is set to "obj1" in the testCase, (testCase 36).
2023-09-25 11:13:08 -04:00
12 : {
2016-03-16 20:30:22 -04:00
IsTruncated : false ,
Objects : [ ] ObjectInfo {
2016-03-22 06:18:22 -04:00
{ Name : "obj2" } ,
2016-03-16 20:30:22 -04:00
} ,
} ,
// ListObjectsResult-13.
// Marker is set to "man" in the testCase, (testCase37).
2023-09-25 11:13:08 -04:00
13 : {
2016-03-16 20:30:22 -04:00
IsTruncated : false ,
Objects : [ ] ObjectInfo {
2016-03-22 06:18:22 -04:00
{ Name : "newPrefix0" } ,
{ Name : "newPrefix1" } ,
{ Name : "newzen/zen/recurse/again/again/again/pics" } ,
{ Name : "obj0" } ,
{ Name : "obj1" } ,
{ Name : "obj2" } ,
2016-03-16 20:30:22 -04:00
} ,
} ,
// ListObjectsResult-14.
// Marker is set to "Abc" in the testCase, (testCase 39).
2023-09-25 11:13:08 -04:00
14 : {
2016-03-16 20:30:22 -04:00
IsTruncated : false ,
Objects : [ ] ObjectInfo {
2017-04-04 12:14:03 -04:00
{ Name : "Asia-maps.png" } ,
2016-03-22 06:18:22 -04:00
{ Name : "Asia/India/India-summer-photos-1" } ,
{ Name : "Asia/India/Karnataka/Bangalore/Koramangala/pics" } ,
{ Name : "newPrefix0" } ,
{ Name : "newPrefix1" } ,
{ Name : "newzen/zen/recurse/again/again/again/pics" } ,
{ Name : "obj0" } ,
{ Name : "obj1" } ,
{ Name : "obj2" } ,
2016-03-16 20:30:22 -04:00
} ,
} ,
// ListObjectsResult-15.
// Marker is set to "Asia/India/India-summer-photos-1" in the testCase, (testCase 40).
2023-09-25 11:13:08 -04:00
15 : {
2016-03-16 20:30:22 -04:00
IsTruncated : false ,
Objects : [ ] ObjectInfo {
2016-03-22 06:18:22 -04:00
{ Name : "Asia/India/Karnataka/Bangalore/Koramangala/pics" } ,
{ Name : "newPrefix0" } ,
{ Name : "newPrefix1" } ,
{ Name : "newzen/zen/recurse/again/again/again/pics" } ,
{ Name : "obj0" } ,
{ Name : "obj1" } ,
{ Name : "obj2" } ,
2016-03-16 20:30:22 -04:00
} ,
} ,
// ListObjectsResult-16.
// Marker is set to "Asia/India/Karnataka/Bangalore/Koramangala/pics" in the testCase, (testCase 41).
2023-09-25 11:13:08 -04:00
16 : {
2016-03-16 20:30:22 -04:00
IsTruncated : false ,
Objects : [ ] ObjectInfo {
2016-03-22 06:18:22 -04:00
{ Name : "newPrefix0" } ,
{ Name : "newPrefix1" } ,
{ Name : "newzen/zen/recurse/again/again/again/pics" } ,
{ Name : "obj0" } ,
{ Name : "obj1" } ,
{ Name : "obj2" } ,
2016-03-16 20:30:22 -04:00
} ,
} ,
// ListObjectsResult-17.
// Used for asserting the case with marker, without prefix but with truncation.
// Marker = "newPrefix0" & maxKeys = 3 in the testCase, (testCase42).
// Output truncated to 3 values.
2023-09-25 11:13:08 -04:00
17 : {
2016-03-16 20:30:22 -04:00
IsTruncated : true ,
Objects : [ ] ObjectInfo {
2016-03-22 06:18:22 -04:00
{ Name : "newPrefix1" } ,
{ Name : "newzen/zen/recurse/again/again/again/pics" } ,
{ Name : "obj0" } ,
2016-03-16 20:30:22 -04:00
} ,
} ,
// ListObjectsResult-18.
// Marker = "newPrefix1" & maxkeys = 1 in the testCase, (testCase43).
// Output truncated to 1 value.
2023-09-25 11:13:08 -04:00
18 : {
2016-03-16 20:30:22 -04:00
IsTruncated : true ,
Objects : [ ] ObjectInfo {
2016-03-22 06:18:22 -04:00
{ Name : "newzen/zen/recurse/again/again/again/pics" } ,
2016-03-16 20:30:22 -04:00
} ,
} ,
// ListObjectsResult-19.
// Marker = "obj0" & maxKeys = 1 in the testCase, (testCase44).
// Output truncated to 1 value.
2023-09-25 11:13:08 -04:00
19 : {
2016-03-16 20:30:22 -04:00
IsTruncated : true ,
Objects : [ ] ObjectInfo {
2016-03-22 06:18:22 -04:00
{ Name : "obj1" } ,
2016-03-16 20:30:22 -04:00
} ,
} ,
// ListObjectsResult-20.
// Marker = "obj0" & prefix = "obj" in the testCase, (testCase 45).
2023-09-25 11:13:08 -04:00
20 : {
2016-03-16 20:30:22 -04:00
IsTruncated : false ,
Objects : [ ] ObjectInfo {
2016-03-22 06:18:22 -04:00
{ Name : "obj1" } ,
{ Name : "obj2" } ,
2016-03-16 20:30:22 -04:00
} ,
} ,
// ListObjectsResult-21.
// Marker = "obj1" & prefix = "obj" in the testCase, (testCase 46).
2023-09-25 11:13:08 -04:00
21 : {
2016-03-16 20:30:22 -04:00
IsTruncated : false ,
Objects : [ ] ObjectInfo {
2016-03-22 06:18:22 -04:00
{ Name : "obj2" } ,
2016-03-16 20:30:22 -04:00
} ,
} ,
// ListObjectsResult-22.
// Marker = "newPrefix0" & prefix = "new" in the testCase,, (testCase 47).
2023-09-25 11:13:08 -04:00
22 : {
2016-03-16 20:30:22 -04:00
IsTruncated : false ,
Objects : [ ] ObjectInfo {
2016-03-22 06:18:22 -04:00
{ Name : "newPrefix1" } ,
{ Name : "newzen/zen/recurse/again/again/again/pics" } ,
2016-03-16 20:30:22 -04:00
} ,
} ,
// ListObjectsResult-23.
// Prefix is set to "Asia/India/" in the testCase, and delimiter is not set (testCase 55).
2023-09-25 11:13:08 -04:00
23 : {
2016-03-16 20:30:22 -04:00
IsTruncated : false ,
Objects : [ ] ObjectInfo {
2016-03-22 06:18:22 -04:00
{ Name : "Asia/India/India-summer-photos-1" } ,
{ Name : "Asia/India/Karnataka/Bangalore/Koramangala/pics" } ,
2016-03-16 20:30:22 -04:00
} ,
} ,
// ListObjectsResult-24.
// Prefix is set to "Asia" in the testCase, and delimiter is not set (testCase 56).
2023-09-25 11:13:08 -04:00
24 : {
2016-03-16 20:30:22 -04:00
IsTruncated : false ,
Objects : [ ] ObjectInfo {
2017-04-04 12:14:03 -04:00
{ Name : "Asia-maps.png" } ,
2016-03-22 06:18:22 -04:00
{ Name : "Asia/India/India-summer-photos-1" } ,
{ Name : "Asia/India/Karnataka/Bangalore/Koramangala/pics" } ,
2016-03-16 20:30:22 -04:00
} ,
} ,
// ListObjectsResult-25.
// Prefix is set to "Asia" in the testCase, and delimiter is set (testCase 57).
2023-09-25 11:13:08 -04:00
25 : {
2016-03-16 20:30:22 -04:00
IsTruncated : false ,
Objects : [ ] ObjectInfo {
2017-04-04 12:14:03 -04:00
{ Name : "Asia-maps.png" } ,
2016-03-16 20:30:22 -04:00
} ,
2019-05-06 10:52:42 -04:00
Prefixes : [ ] string { "Asia/" } ,
2016-03-16 20:30:22 -04:00
} ,
// ListObjectsResult-26.
// prefix = "new" and delimiter is set in the testCase.(testCase 58).
2023-09-25 11:13:08 -04:00
26 : {
2016-03-16 20:30:22 -04:00
IsTruncated : false ,
Objects : [ ] ObjectInfo {
2016-03-22 06:18:22 -04:00
{ Name : "newPrefix0" } ,
{ Name : "newPrefix1" } ,
2016-03-16 20:30:22 -04:00
} ,
2019-05-06 10:52:42 -04:00
Prefixes : [ ] string { "newzen/" } ,
2016-03-16 20:30:22 -04:00
} ,
// ListObjectsResult-27.
// Prefix is set to "Asia/India/" in the testCase, and delimiter is set to forward slash '/' (testCase 59).
2023-09-25 11:13:08 -04:00
27 : {
2016-03-16 20:30:22 -04:00
IsTruncated : false ,
Objects : [ ] ObjectInfo {
2016-03-22 06:18:22 -04:00
{ Name : "Asia/India/India-summer-photos-1" } ,
2016-03-16 20:30:22 -04:00
} ,
2019-05-06 10:52:42 -04:00
Prefixes : [ ] string { "Asia/India/Karnataka/" } ,
2016-03-16 20:30:22 -04:00
} ,
// ListObjectsResult-28.
// Marker is set to "Asia/India/India-summer-photos-1" and delimiter set in the testCase, (testCase 60).
2023-09-25 11:13:08 -04:00
28 : {
2016-03-16 20:30:22 -04:00
IsTruncated : false ,
Objects : [ ] ObjectInfo {
2016-03-22 06:18:22 -04:00
{ Name : "newPrefix0" } ,
{ Name : "newPrefix1" } ,
{ Name : "obj0" } ,
{ Name : "obj1" } ,
{ Name : "obj2" } ,
2016-03-16 20:30:22 -04:00
} ,
2019-05-06 10:52:42 -04:00
Prefixes : [ ] string { "newzen/" } ,
2016-03-16 20:30:22 -04:00
} ,
// ListObjectsResult-29.
2020-08-18 15:19:44 -04:00
// Marker is set to "Asia/India/Karnataka/Bangalore/Koramangala/pics" in the testCase and delimiter set, (testCase 61).
2023-09-25 11:13:08 -04:00
29 : {
2016-03-16 20:30:22 -04:00
IsTruncated : false ,
Objects : [ ] ObjectInfo {
2016-03-22 06:18:22 -04:00
{ Name : "newPrefix0" } ,
{ Name : "newPrefix1" } ,
{ Name : "obj0" } ,
{ Name : "obj1" } ,
{ Name : "obj2" } ,
2016-03-16 20:30:22 -04:00
} ,
2019-05-06 10:52:42 -04:00
Prefixes : [ ] string { "newzen/" } ,
2016-03-16 20:30:22 -04:00
} ,
2016-05-27 18:43:51 -04:00
// ListObjectsResult-30.
// Prefix and Delimiter is set to '/', (testCase 62).
2023-09-25 11:13:08 -04:00
30 : {
2016-05-27 18:43:51 -04:00
IsTruncated : false ,
Objects : [ ] ObjectInfo { } ,
} ,
2019-05-06 10:52:42 -04:00
// ListObjectsResult-31 Empty directory, recursive listing
2023-09-25 11:13:08 -04:00
31 : {
2019-05-06 10:52:42 -04:00
IsTruncated : false ,
Objects : [ ] ObjectInfo {
{ Name : "obj1" } ,
{ Name : "obj2" } ,
{ Name : "temporary/0/" } ,
} ,
} ,
// ListObjectsResult-32 Empty directory, non recursive listing
2023-09-25 11:13:08 -04:00
32 : {
2019-05-06 10:52:42 -04:00
IsTruncated : false ,
Objects : [ ] ObjectInfo {
{ Name : "obj1" } ,
{ Name : "obj2" } ,
} ,
Prefixes : [ ] string { "temporary/" } ,
} ,
// ListObjectsResult-33 Listing empty directory only
2023-09-25 11:13:08 -04:00
33 : {
2019-05-06 10:52:42 -04:00
IsTruncated : false ,
Objects : [ ] ObjectInfo {
{ Name : "temporary/0/" } ,
} ,
} ,
2020-08-14 23:13:24 -04:00
// ListObjectsResult-34:
// * Listing with marker > last object should return empty
// * Listing an object with a trailing slash and '/' delimiter
2023-09-25 11:13:08 -04:00
34 : {
2020-03-13 20:43:00 -04:00
IsTruncated : false ,
Objects : [ ] ObjectInfo { } ,
} ,
2020-11-04 10:56:58 -05:00
// ListObjectsResult-35 list with custom uncommon delimiter
2023-09-25 11:13:08 -04:00
35 : {
2020-11-04 10:56:58 -05:00
IsTruncated : false ,
Objects : [ ] ObjectInfo {
{ Name : "file1/receipt.json" } ,
} ,
Prefixes : [ ] string { "file1/guidSplunk" } ,
} ,
2020-12-19 12:36:04 -05:00
// ListObjectsResult-36 list with nextmarker prefix and maxKeys set to 1.
2023-09-25 11:13:08 -04:00
36 : {
2020-12-19 12:36:04 -05:00
IsTruncated : true ,
Prefixes : [ ] string { "dir/day_id=2017-10-10/" } ,
} ,
2021-08-18 10:40:53 -04:00
// ListObjectsResult-37 list with prefix match 2 levels deep
2023-09-25 11:13:08 -04:00
37 : {
2021-08-18 10:40:53 -04:00
IsTruncated : false ,
Objects : [ ] ObjectInfo {
{ Name : "foo/201910/1112" } ,
{ Name : "foo/201910/1122" } ,
} ,
} ,
// ListObjectsResult-38 list with prefix match 1 level deep
2023-09-25 11:13:08 -04:00
38 : {
2021-08-18 10:40:53 -04:00
IsTruncated : false ,
Objects : [ ] ObjectInfo {
{ Name : "foo/201910/1112" } ,
{ Name : "foo/201910/1122" } ,
{ Name : "foo/201910/2112" } ,
{ Name : "foo/201910_txt" } ,
} ,
} ,
2021-08-21 03:12:29 -04:00
// ListObjectsResult-39 list with prefix match 1 level deep
2023-09-25 11:13:08 -04:00
39 : {
2021-08-21 03:12:29 -04:00
IsTruncated : false ,
Objects : [ ] ObjectInfo {
{ Name : "201910/foo/bar/xl.meta/1.txt" } ,
} ,
} ,
2022-11-22 11:51:04 -05:00
// ListObjectsResult-40
2023-09-25 11:13:08 -04:00
40 : {
2022-11-22 11:51:04 -05:00
IsTruncated : false ,
Objects : [ ] ObjectInfo {
{ Name : "aaa" } ,
{ Name : "ccc" } ,
} ,
Prefixes : [ ] string { "bbb_" } ,
} ,
2016-03-16 20:30:22 -04:00
}
testCases := [ ] struct {
// Inputs to ListObjects.
bucketName string
prefix string
marker string
2020-08-18 15:19:44 -04:00
delimiter string
2017-08-05 05:36:47 -04:00
maxKeys int32
2016-03-16 20:30:22 -04:00
// Expected output of ListObjects.
objectAPI: Fix object API interface, remove unnecessary structs.
ObjectAPI changes.
```
ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, *probe.Error)
ListMultipartUploads(bucket, objectPrefix, keyMarker, uploadIDMarker, delimiter string, maxUploads int) (ListMultipartsInfo, *probe.Error)
ListObjectParts(bucket, object, uploadID string, partNumberMarker, maxParts int) (ListPartsInfo, *probe.Error)
CompleteMultipartUpload(bucket string, object string, uploadID string, parts []completePart) (ObjectInfo, *probe.Error)
```
2016-04-03 04:34:20 -04:00
result ListObjectsInfo
2016-03-16 20:30:22 -04:00
err error
// Flag indicating whether the test is expected to pass or not.
shouldPass bool
} {
// Test cases with invalid bucket names ( Test number 1-4 ).
2018-04-23 23:27:33 -04:00
{ ".test" , "" , "" , "" , 0 , ListObjectsInfo { } , BucketNotFound { Bucket : ".test" } , false } ,
{ "Test" , "" , "" , "" , 0 , ListObjectsInfo { } , BucketNotFound { Bucket : "Test" } , false } ,
{ "---" , "" , "" , "" , 0 , ListObjectsInfo { } , BucketNotFound { Bucket : "---" } , false } ,
{ "ad" , "" , "" , "" , 0 , ListObjectsInfo { } , BucketNotFound { Bucket : "ad" } , false } ,
2016-03-16 20:30:22 -04:00
// Using an existing file for bucket name, but its not a directory (5).
objectAPI: Fix object API interface, remove unnecessary structs.
ObjectAPI changes.
```
ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, *probe.Error)
ListMultipartUploads(bucket, objectPrefix, keyMarker, uploadIDMarker, delimiter string, maxUploads int) (ListMultipartsInfo, *probe.Error)
ListObjectParts(bucket, object, uploadID string, partNumberMarker, maxParts int) (ListPartsInfo, *probe.Error)
CompleteMultipartUpload(bucket string, object string, uploadID string, parts []completePart) (ObjectInfo, *probe.Error)
```
2016-04-03 04:34:20 -04:00
{ "simple-file.txt" , "" , "" , "" , 0 , ListObjectsInfo { } , BucketNotFound { Bucket : "simple-file.txt" } , false } ,
2024-01-18 02:03:17 -05:00
// Valid bucket names, but they do not exist (6-8).
objectAPI: Fix object API interface, remove unnecessary structs.
ObjectAPI changes.
```
ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, *probe.Error)
ListMultipartUploads(bucket, objectPrefix, keyMarker, uploadIDMarker, delimiter string, maxUploads int) (ListMultipartsInfo, *probe.Error)
ListObjectParts(bucket, object, uploadID string, partNumberMarker, maxParts int) (ListPartsInfo, *probe.Error)
CompleteMultipartUpload(bucket string, object string, uploadID string, parts []completePart) (ObjectInfo, *probe.Error)
```
2016-04-03 04:34:20 -04:00
{ "volatile-bucket-1" , "" , "" , "" , 0 , ListObjectsInfo { } , BucketNotFound { Bucket : "volatile-bucket-1" } , false } ,
{ "volatile-bucket-2" , "" , "" , "" , 0 , ListObjectsInfo { } , BucketNotFound { Bucket : "volatile-bucket-2" } , false } ,
{ "volatile-bucket-3" , "" , "" , "" , 0 , ListObjectsInfo { } , BucketNotFound { Bucket : "volatile-bucket-3" } , false } ,
2023-09-25 11:13:08 -04:00
// If marker is *after* the last possible object from the prefix it should return an empty list.
{ "test-bucket-list-object" , "Asia" , "europe-object" , "" , 0 , ListObjectsInfo { } , nil , true } ,
// If the marker is *before* the first possible object from the prefix it should return the first object.
{ "test-bucket-list-object" , "Asia" , "A" , "" , 1 , resultCases [ 4 ] , nil , true } ,
2016-07-24 18:52:12 -04:00
// Setting a non-existing directory to be prefix (12-13).
objectAPI: Fix object API interface, remove unnecessary structs.
ObjectAPI changes.
```
ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, *probe.Error)
ListMultipartUploads(bucket, objectPrefix, keyMarker, uploadIDMarker, delimiter string, maxUploads int) (ListMultipartsInfo, *probe.Error)
ListObjectParts(bucket, object, uploadID string, partNumberMarker, maxParts int) (ListPartsInfo, *probe.Error)
CompleteMultipartUpload(bucket string, object string, uploadID string, parts []completePart) (ObjectInfo, *probe.Error)
```
2016-04-03 04:34:20 -04:00
{ "empty-bucket" , "europe/france/" , "" , "" , 1 , ListObjectsInfo { } , nil , true } ,
2016-09-16 16:06:49 -04:00
{ "empty-bucket" , "africa/tunisia/" , "" , "" , 1 , ListObjectsInfo { } , nil , true } ,
2016-07-24 18:52:12 -04:00
// Testing on empty bucket, that is, bucket without any objects in it (14).
objectAPI: Fix object API interface, remove unnecessary structs.
ObjectAPI changes.
```
ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, *probe.Error)
ListMultipartUploads(bucket, objectPrefix, keyMarker, uploadIDMarker, delimiter string, maxUploads int) (ListMultipartsInfo, *probe.Error)
ListObjectParts(bucket, object, uploadID string, partNumberMarker, maxParts int) (ListPartsInfo, *probe.Error)
CompleteMultipartUpload(bucket string, object string, uploadID string, parts []completePart) (ObjectInfo, *probe.Error)
```
2016-04-03 04:34:20 -04:00
{ "empty-bucket" , "" , "" , "" , 0 , ListObjectsInfo { } , nil , true } ,
2016-07-24 18:52:12 -04:00
// Setting maxKeys to negative value (15-16).
objectAPI: Fix object API interface, remove unnecessary structs.
ObjectAPI changes.
```
ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, *probe.Error)
ListMultipartUploads(bucket, objectPrefix, keyMarker, uploadIDMarker, delimiter string, maxUploads int) (ListMultipartsInfo, *probe.Error)
ListObjectParts(bucket, object, uploadID string, partNumberMarker, maxParts int) (ListPartsInfo, *probe.Error)
CompleteMultipartUpload(bucket string, object string, uploadID string, parts []completePart) (ObjectInfo, *probe.Error)
```
2016-04-03 04:34:20 -04:00
{ "empty-bucket" , "" , "" , "" , - 1 , ListObjectsInfo { } , nil , true } ,
{ "empty-bucket" , "" , "" , "" , 1 , ListObjectsInfo { } , nil , true } ,
2016-07-24 18:52:12 -04:00
// Setting maxKeys to a very large value (17).
2017-08-05 05:36:47 -04:00
{ "empty-bucket" , "" , "" , "" , 111100000 , ListObjectsInfo { } , nil , true } ,
2018-09-05 19:38:03 -04:00
// Testing for all 10 objects in the bucket (18).
{ "test-bucket-list-object" , "" , "" , "" , 10 , resultCases [ 0 ] , nil , true } ,
2021-11-16 12:28:29 -05:00
// Testing for negative value of maxKey, this should set maxKeys to listObjectsLimit (19).
2016-03-16 20:30:22 -04:00
{ "test-bucket-list-object" , "" , "" , "" , - 1 , resultCases [ 0 ] , nil , true } ,
2016-07-24 18:52:12 -04:00
// Testing for very large value of maxKey, this should set maxKeys to listObjectsLimit (20).
2017-08-05 05:36:47 -04:00
{ "test-bucket-list-object" , "" , "" , "" , 1234567890 , resultCases [ 0 ] , nil , true } ,
2016-07-24 18:52:12 -04:00
// Testing for trancated value (21-24).
2016-03-16 20:30:22 -04:00
{ "test-bucket-list-object" , "" , "" , "" , 5 , resultCases [ 1 ] , nil , true } ,
{ "test-bucket-list-object" , "" , "" , "" , 4 , resultCases [ 2 ] , nil , true } ,
{ "test-bucket-list-object" , "" , "" , "" , 3 , resultCases [ 3 ] , nil , true } ,
{ "test-bucket-list-object" , "" , "" , "" , 1 , resultCases [ 4 ] , nil , true } ,
2016-07-24 18:52:12 -04:00
// Testing with prefix (25-28).
2016-03-16 20:30:22 -04:00
{ "test-bucket-list-object" , "new" , "" , "" , 3 , resultCases [ 5 ] , nil , true } ,
{ "test-bucket-list-object" , "new" , "" , "" , 4 , resultCases [ 5 ] , nil , true } ,
{ "test-bucket-list-object" , "new" , "" , "" , 5 , resultCases [ 5 ] , nil , true } ,
{ "test-bucket-list-object" , "obj" , "" , "" , 3 , resultCases [ 6 ] , nil , true } ,
2021-01-13 12:44:11 -05:00
{ "test-bucket-list-object" , "/obj" , "" , "" , 0 , ListObjectsInfo { } , nil , true } ,
2016-07-24 18:52:12 -04:00
// Testing with prefix and truncation (29-30).
2016-03-16 20:30:22 -04:00
{ "test-bucket-list-object" , "new" , "" , "" , 1 , resultCases [ 7 ] , nil , true } ,
{ "test-bucket-list-object" , "obj" , "" , "" , 2 , resultCases [ 8 ] , nil , true } ,
2016-07-24 18:52:12 -04:00
// Testing with marker, but without prefix and truncation (31-35).
2018-09-05 19:38:03 -04:00
{ "test-bucket-list-object" , "" , "newPrefix0" , "" , 6 , resultCases [ 9 ] , nil , true } ,
{ "test-bucket-list-object" , "" , "newPrefix1" , "" , 5 , resultCases [ 10 ] , nil , true } ,
{ "test-bucket-list-object" , "" , "obj0" , "" , 4 , resultCases [ 11 ] , nil , true } ,
{ "test-bucket-list-object" , "" , "obj1" , "" , 2 , resultCases [ 12 ] , nil , true } ,
{ "test-bucket-list-object" , "" , "man" , "" , 11 , resultCases [ 13 ] , nil , true } ,
2016-07-24 18:52:12 -04:00
// Marker being set to a value which is greater than and all object names when sorted (36).
2016-03-16 20:30:22 -04:00
// Expected to send an empty response in this case.
objectAPI: Fix object API interface, remove unnecessary structs.
ObjectAPI changes.
```
ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, *probe.Error)
ListMultipartUploads(bucket, objectPrefix, keyMarker, uploadIDMarker, delimiter string, maxUploads int) (ListMultipartsInfo, *probe.Error)
ListObjectParts(bucket, object, uploadID string, partNumberMarker, maxParts int) (ListPartsInfo, *probe.Error)
CompleteMultipartUpload(bucket string, object string, uploadID string, parts []completePart) (ObjectInfo, *probe.Error)
```
2016-04-03 04:34:20 -04:00
{ "test-bucket-list-object" , "" , "zen" , "" , 10 , ListObjectsInfo { } , nil , true } ,
2016-07-24 18:52:12 -04:00
// Marker being set to a value which is lesser than and all object names when sorted (37).
2016-03-16 20:30:22 -04:00
// Expected to send all the objects in the bucket in this case.
{ "test-bucket-list-object" , "" , "Abc" , "" , 10 , resultCases [ 14 ] , nil , true } ,
2016-07-24 18:52:12 -04:00
// Marker is to a hierarhical value (38-39).
2016-03-16 20:30:22 -04:00
{ "test-bucket-list-object" , "" , "Asia/India/India-summer-photos-1" , "" , 10 , resultCases [ 15 ] , nil , true } ,
{ "test-bucket-list-object" , "" , "Asia/India/Karnataka/Bangalore/Koramangala/pics" , "" , 10 , resultCases [ 16 ] , nil , true } ,
2016-07-24 18:52:12 -04:00
// Testing with marker and truncation, but no prefix (40-42).
2016-03-16 20:30:22 -04:00
{ "test-bucket-list-object" , "" , "newPrefix0" , "" , 3 , resultCases [ 17 ] , nil , true } ,
{ "test-bucket-list-object" , "" , "newPrefix1" , "" , 1 , resultCases [ 18 ] , nil , true } ,
{ "test-bucket-list-object" , "" , "obj0" , "" , 1 , resultCases [ 19 ] , nil , true } ,
2016-07-24 18:52:12 -04:00
// Testing with both marker and prefix, but without truncation (43-45).
2016-03-16 20:30:22 -04:00
// The valid combination of marker and prefix should satisfy strings.HasPrefix(marker, prefix).
{ "test-bucket-list-object" , "obj" , "obj0" , "" , 2 , resultCases [ 20 ] , nil , true } ,
{ "test-bucket-list-object" , "obj" , "obj1" , "" , 1 , resultCases [ 21 ] , nil , true } ,
{ "test-bucket-list-object" , "new" , "newPrefix0" , "" , 2 , resultCases [ 22 ] , nil , true } ,
2016-07-24 18:52:12 -04:00
// Testing with maxKeys set to 0 (46-52).
2016-03-16 20:30:22 -04:00
// The parameters have to valid.
objectAPI: Fix object API interface, remove unnecessary structs.
ObjectAPI changes.
```
ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, *probe.Error)
ListMultipartUploads(bucket, objectPrefix, keyMarker, uploadIDMarker, delimiter string, maxUploads int) (ListMultipartsInfo, *probe.Error)
ListObjectParts(bucket, object, uploadID string, partNumberMarker, maxParts int) (ListPartsInfo, *probe.Error)
CompleteMultipartUpload(bucket string, object string, uploadID string, parts []completePart) (ObjectInfo, *probe.Error)
```
2016-04-03 04:34:20 -04:00
{ "test-bucket-list-object" , "" , "obj1" , "" , 0 , ListObjectsInfo { } , nil , true } ,
{ "test-bucket-list-object" , "" , "obj0" , "" , 0 , ListObjectsInfo { } , nil , true } ,
{ "test-bucket-list-object" , "new" , "" , "" , 0 , ListObjectsInfo { } , nil , true } ,
{ "test-bucket-list-object" , "obj" , "" , "" , 0 , ListObjectsInfo { } , nil , true } ,
{ "test-bucket-list-object" , "obj" , "obj0" , "" , 0 , ListObjectsInfo { } , nil , true } ,
{ "test-bucket-list-object" , "obj" , "obj1" , "" , 0 , ListObjectsInfo { } , nil , true } ,
{ "test-bucket-list-object" , "new" , "newPrefix0" , "" , 0 , ListObjectsInfo { } , nil , true } ,
2016-03-16 20:30:22 -04:00
// Tests on hierarchical key names as prefix.
// Without delimteter the code should recurse into the prefix Dir.
2016-07-24 18:52:12 -04:00
// Tests with prefix, but without delimiter (53-54).
2016-03-16 20:30:22 -04:00
{ "test-bucket-list-object" , "Asia/India/" , "" , "" , 10 , resultCases [ 23 ] , nil , true } ,
{ "test-bucket-list-object" , "Asia" , "" , "" , 10 , resultCases [ 24 ] , nil , true } ,
2016-07-24 18:52:12 -04:00
// Tests with prefix and delimiter (55-57).
2020-08-18 15:19:44 -04:00
// With delimiter the code should not recurse into the sub-directories of prefix Dir.
2019-08-06 15:08:58 -04:00
{ "test-bucket-list-object" , "Asia" , "" , SlashSeparator , 10 , resultCases [ 25 ] , nil , true } ,
{ "test-bucket-list-object" , "new" , "" , SlashSeparator , 10 , resultCases [ 26 ] , nil , true } ,
{ "test-bucket-list-object" , "Asia/India/" , "" , SlashSeparator , 10 , resultCases [ 27 ] , nil , true } ,
2016-07-24 18:52:12 -04:00
// Test with marker set as hierarhical value and with delimiter. (58-59)
2019-08-06 15:08:58 -04:00
{ "test-bucket-list-object" , "" , "Asia/India/India-summer-photos-1" , SlashSeparator , 10 , resultCases [ 28 ] , nil , true } ,
{ "test-bucket-list-object" , "" , "Asia/India/Karnataka/Bangalore/Koramangala/pics" , SlashSeparator , 10 , resultCases [ 29 ] , nil , true } ,
2016-07-24 18:52:12 -04:00
// Test with prefix and delimiter set to '/'. (60)
2019-08-06 15:08:58 -04:00
{ "test-bucket-list-object" , SlashSeparator , "" , SlashSeparator , 10 , resultCases [ 30 ] , nil , true } ,
2016-09-16 16:06:49 -04:00
// Test with invalid prefix (61)
2020-02-11 22:38:02 -05:00
{ "test-bucket-list-object" , "\\" , "" , SlashSeparator , 10 , ListObjectsInfo { } , nil , true } ,
2019-05-06 10:52:42 -04:00
// Test listing an empty directory in recursive mode (62)
{ "test-bucket-empty-dir" , "" , "" , "" , 10 , resultCases [ 31 ] , nil , true } ,
// Test listing an empty directory in a non recursive mode (63)
2019-08-06 15:08:58 -04:00
{ "test-bucket-empty-dir" , "" , "" , SlashSeparator , 10 , resultCases [ 32 ] , nil , true } ,
2019-05-06 10:52:42 -04:00
// Test listing a directory which contains an empty directory (64)
{ "test-bucket-empty-dir" , "" , "temporary/" , "" , 10 , resultCases [ 33 ] , nil , true } ,
2020-03-13 20:43:00 -04:00
// Test listing with marker > last object such that response should be empty (65)
{ "test-bucket-single-object" , "" , "A/C" , "" , 1000 , resultCases [ 34 ] , nil , true } ,
2020-08-14 23:13:24 -04:00
// Test listing an object with a trailing slash and a slash delimiter (66)
{ "test-bucket-list-object" , "Asia-maps.png/" , "" , "/" , 1000 , resultCases [ 34 ] , nil , true } ,
2020-11-04 10:56:58 -05:00
// Test listing an object with uncommon delimiter
{ testBuckets [ 4 ] , "" , "" , "guidSplunk" , 1000 , resultCases [ 35 ] , nil , true } ,
// Test listing an object with uncommon delimiter and matching prefix
{ testBuckets [ 4 ] , "file1/" , "" , "guidSplunk" , 1000 , resultCases [ 35 ] , nil , true } ,
2020-12-19 12:36:04 -05:00
// Test listing at prefix with expected prefix markers
{ testBuckets [ 5 ] , "dir/" , "" , SlashSeparator , 1 , resultCases [ 36 ] , nil , true } ,
2021-08-18 10:40:53 -04:00
// Test listing with prefix match
{ testBuckets [ 5 ] , "foo/201910/11" , "" , "" , 1000 , resultCases [ 37 ] , nil , true } ,
{ testBuckets [ 5 ] , "foo/201910" , "" , "" , 1000 , resultCases [ 38 ] , nil , true } ,
2021-08-21 03:12:29 -04:00
// Test listing with prefix match with 'xl.meta'
{ testBuckets [ 5 ] , "201910/foo/bar" , "" , "" , 1000 , resultCases [ 39 ] , nil , true } ,
2022-11-22 11:51:04 -05:00
// Test listing with custom prefix
{ testBuckets [ 6 ] , "" , "" , "_" , 1000 , resultCases [ 40 ] , nil , true } ,
2016-03-16 20:30:22 -04:00
}
for i , testCase := range testCases {
2019-05-02 01:06:57 -04:00
testCase := testCase
2020-06-26 02:20:12 -04:00
t . Run ( fmt . Sprintf ( "%s-Test%d" , instanceType , i + 1 ) , func ( t * testing . T ) {
2020-10-28 12:18:35 -04:00
t . Log ( "ListObjects, bucket:" , testCase . bucketName , "prefix:" , testCase . prefix , "marker:" , testCase . marker , "delimiter:" , testCase . delimiter , "maxkeys:" , testCase . maxKeys )
2019-05-02 01:06:57 -04:00
result , err := obj . ListObjects ( context . Background ( ) , testCase . bucketName ,
2020-08-18 15:19:44 -04:00
testCase . prefix , testCase . marker , testCase . delimiter , int ( testCase . maxKeys ) )
2019-05-02 01:06:57 -04:00
if err != nil && testCase . shouldPass {
t . Errorf ( "Test %d: %s: Expected to pass, but failed with: <ERROR> %s" , i + 1 , instanceType , err . Error ( ) )
2016-03-16 20:30:22 -04:00
}
2019-05-02 01:06:57 -04:00
if err == nil && ! testCase . shouldPass {
t . Errorf ( "Test %d: %s: Expected to fail with <ERROR> \"%s\", but passed instead" , i + 1 , instanceType , testCase . err . Error ( ) )
2016-03-16 20:30:22 -04:00
}
2019-05-02 01:06:57 -04:00
// Failed as expected, but does it fail for the expected reason.
if err != nil && ! testCase . shouldPass {
if ! strings . Contains ( err . Error ( ) , testCase . err . Error ( ) ) {
t . Errorf ( "Test %d: %s: Expected to fail with error \"%s\", but instead failed with error \"%s\" instead" , i + 1 , instanceType , testCase . err . Error ( ) , err . Error ( ) )
2016-03-16 20:30:22 -04:00
}
2019-05-02 01:06:57 -04:00
}
// Since there are cases for which ListObjects fails, this is
// necessary. Test passes as expected, but the output values
// are verified for correctness here.
if err == nil && testCase . shouldPass {
// The length of the expected ListObjectsResult.Objects
// should match in both expected result from test cases
// and in the output. On failure calling t.Fatalf,
// otherwise it may lead to index out of range error in
// assertion following this.
if len ( testCase . result . Objects ) != len ( result . Objects ) {
2020-10-28 12:18:35 -04:00
t . Logf ( "want: %v" , objInfoNames ( testCase . result . Objects ) )
t . Logf ( "got: %v" , objInfoNames ( result . Objects ) )
t . Errorf ( "Test %d: %s: Expected number of object in the result to be '%d', but found '%d' objects instead" , i + 1 , instanceType , len ( testCase . result . Objects ) , len ( result . Objects ) )
2016-11-13 14:48:02 -05:00
}
2019-05-02 01:06:57 -04:00
for j := 0 ; j < len ( testCase . result . Objects ) ; j ++ {
2020-10-28 12:18:35 -04:00
if j >= len ( result . Objects ) {
t . Errorf ( "Test %d: %s: Expected object name to be \"%s\", but not nothing instead" , i + 1 , instanceType , testCase . result . Objects [ j ] . Name )
continue
}
2019-05-02 01:06:57 -04:00
if testCase . result . Objects [ j ] . Name != result . Objects [ j ] . Name {
t . Errorf ( "Test %d: %s: Expected object name to be \"%s\", but found \"%s\" instead" , i + 1 , instanceType , testCase . result . Objects [ j ] . Name , result . Objects [ j ] . Name )
}
}
2019-05-06 10:52:42 -04:00
if len ( testCase . result . Prefixes ) != len ( result . Prefixes ) {
2020-10-28 12:18:35 -04:00
t . Logf ( "want: %v" , testCase . result . Prefixes )
t . Logf ( "got: %v" , result . Prefixes )
t . Errorf ( "Test %d: %s: Expected number of prefixes in the result to be '%d', but found '%d' prefixes instead" , i + 1 , instanceType , len ( testCase . result . Prefixes ) , len ( result . Prefixes ) )
2019-05-06 10:52:42 -04:00
}
for j := 0 ; j < len ( testCase . result . Prefixes ) ; j ++ {
2020-10-28 12:18:35 -04:00
if j >= len ( result . Prefixes ) {
t . Errorf ( "Test %d: %s: Expected prefix name to be \"%s\", but found no result" , i + 1 , instanceType , testCase . result . Prefixes [ j ] )
continue
}
2019-05-06 10:52:42 -04:00
if testCase . result . Prefixes [ j ] != result . Prefixes [ j ] {
t . Errorf ( "Test %d: %s: Expected prefix name to be \"%s\", but found \"%s\" instead" , i + 1 , instanceType , testCase . result . Prefixes [ j ] , result . Prefixes [ j ] )
}
}
2019-05-02 01:06:57 -04:00
if testCase . result . IsTruncated != result . IsTruncated {
2020-10-28 12:18:35 -04:00
// Allow an extra continuation token.
if ! result . IsTruncated || len ( result . Objects ) == 0 {
t . Errorf ( "Test %d: %s: Expected IsTruncated flag to be %v, but instead found it to be %v" , i + 1 , instanceType , testCase . result . IsTruncated , result . IsTruncated )
}
2019-05-02 01:06:57 -04:00
}
2016-03-16 20:30:22 -04:00
2019-05-02 01:06:57 -04:00
if testCase . result . IsTruncated && result . NextMarker == "" {
2020-12-19 12:36:04 -05:00
t . Errorf ( "Test %d: %s: Expected NextMarker to contain a string since listing is truncated, but instead found it to be empty" , i + 1 , instanceType )
2019-05-02 01:06:57 -04:00
}
2018-12-20 16:30:25 -05:00
2019-05-02 01:06:57 -04:00
if ! testCase . result . IsTruncated && result . NextMarker != "" {
2020-10-28 12:18:35 -04:00
if ! result . IsTruncated || len ( result . Objects ) == 0 {
2020-12-19 12:36:04 -05:00
t . Errorf ( "Test %d: %s: Expected NextMarker to be empty since listing is not truncated, but instead found `%v`" , i + 1 , instanceType , result . NextMarker )
2020-10-28 12:18:35 -04:00
}
2019-05-02 01:06:57 -04:00
}
2018-12-20 16:30:25 -05:00
2016-07-24 18:52:12 -04:00
}
2020-08-18 15:19:44 -04:00
} )
}
}
2020-10-28 12:18:35 -04:00
func objInfoNames ( o [ ] ObjectInfo ) [ ] string {
2022-01-02 12:15:06 -05:00
res := make ( [ ] string , len ( o ) )
2020-10-28 12:18:35 -04:00
for i := range o {
res [ i ] = o [ i ] . Name
}
return res
}
2021-11-30 21:30:06 -05:00
func TestDeleteObjectVersionMarker ( t * testing . T ) {
ExecObjectLayerTest ( t , testDeleteObjectVersion )
}
func testDeleteObjectVersion ( obj ObjectLayer , instanceType string , t1 TestErrHandler ) {
t , _ := t1 . ( * testing . T )
testBuckets := [ ] string {
"bucket-suspended-version" ,
"bucket-suspended-version-id" ,
}
for _ , bucket := range testBuckets {
2022-12-23 10:46:00 -05:00
err := obj . MakeBucket ( context . Background ( ) , bucket , MakeBucketOptions {
2021-11-30 21:30:06 -05:00
VersioningEnabled : true ,
} )
if err != nil {
t . Fatalf ( "%s : %s" , instanceType , err )
}
meta , err := loadBucketMetadata ( context . Background ( ) , obj , bucket )
if err != nil {
t . Fatalf ( "%s : %s" , instanceType , err )
}
meta . VersioningConfigXML = [ ] byte ( ` <VersioningConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Status>Suspended</Status></VersioningConfiguration> ` )
if err := meta . Save ( context . Background ( ) , obj ) ; err != nil {
t . Fatalf ( "%s : %s" , instanceType , err )
}
globalBucketMetadataSys . Set ( bucket , meta )
globalNotificationSys . LoadBucketMetadata ( context . Background ( ) , bucket )
}
testObjects := [ ] struct {
parentBucket string
name string
content string
meta map [ string ] string
versionID string
expectDelMarker bool
} {
{ testBuckets [ 0 ] , "delete-file" , "contentstring" , nil , "" , true } ,
{ testBuckets [ 1 ] , "delete-file" , "contentstring" , nil , "null" , false } ,
}
for _ , object := range testObjects {
md5Bytes := md5 . Sum ( [ ] byte ( object . content ) )
_ , err := obj . PutObject ( context . Background ( ) , object . parentBucket , object . name ,
mustGetPutObjReader ( t , bytes . NewBufferString ( object . content ) ,
int64 ( len ( object . content ) ) , hex . EncodeToString ( md5Bytes [ : ] ) , "" ) , ObjectOptions {
2022-05-06 22:05:28 -04:00
Versioned : globalBucketVersioningSys . PrefixEnabled ( object . parentBucket , object . name ) ,
VersionSuspended : globalBucketVersioningSys . PrefixSuspended ( object . parentBucket , object . name ) ,
2021-11-30 21:30:06 -05:00
UserDefined : object . meta ,
} )
if err != nil {
t . Fatalf ( "%s : %s" , instanceType , err )
}
obj , err := obj . DeleteObject ( context . Background ( ) , object . parentBucket , object . name , ObjectOptions {
2022-05-06 22:05:28 -04:00
Versioned : globalBucketVersioningSys . PrefixEnabled ( object . parentBucket , object . name ) ,
VersionSuspended : globalBucketVersioningSys . PrefixSuspended ( object . parentBucket , object . name ) ,
2021-11-30 21:30:06 -05:00
VersionID : object . versionID ,
} )
if err != nil {
if object . versionID != "" {
if ! isErrVersionNotFound ( err ) {
t . Fatalf ( "%s : %s" , instanceType , err )
}
} else {
if ! isErrObjectNotFound ( err ) {
t . Fatalf ( "%s : %s" , instanceType , err )
}
}
}
if obj . DeleteMarker != object . expectDelMarker {
t . Fatalf ( "%s : expected deleted marker %t, found %t" , instanceType , object . expectDelMarker , obj . DeleteMarker )
}
}
}
2020-08-18 15:19:44 -04:00
// Wrapper for calling ListObjectVersions tests for both Erasure multiple disks and single node setup.
func TestListObjectVersions ( t * testing . T ) {
ExecObjectLayerTest ( t , testListObjectVersions )
}
// Unit test for ListObjectVersions
func testListObjectVersions ( obj ObjectLayer , instanceType string , t1 TestErrHandler ) {
t , _ := t1 . ( * testing . T )
testBuckets := [ ] string {
// This bucket is used for testing ListObject operations.
"test-bucket-list-object" ,
// This bucket will be tested with empty directories
"test-bucket-empty-dir" ,
// Will not store any objects in this bucket,
// Its to test ListObjects on an empty bucket.
"empty-bucket" ,
// Listing the case where the marker > last object.
"test-bucket-single-object" ,
2020-11-04 10:56:58 -05:00
// Listing uncommon delimiter.
"test-bucket-delimiter" ,
2020-12-19 12:36:04 -05:00
// Listing prefixes > maxKeys
"test-bucket-max-keys-prefixes" ,
2020-08-18 15:19:44 -04:00
}
for _ , bucket := range testBuckets {
2022-12-23 10:46:00 -05:00
err := obj . MakeBucket ( context . Background ( ) , bucket , MakeBucketOptions { VersioningEnabled : true } )
2020-08-18 15:19:44 -04:00
if err != nil {
t . Fatalf ( "%s : %s" , instanceType , err . Error ( ) )
}
}
var err error
testObjects := [ ] struct {
parentBucket string
name string
content string
meta map [ string ] string
} {
{ testBuckets [ 0 ] , "Asia-maps.png" , "asis-maps" , map [ string ] string { "content-type" : "image/png" } } ,
{ testBuckets [ 0 ] , "Asia/India/India-summer-photos-1" , "contentstring" , nil } ,
{ testBuckets [ 0 ] , "Asia/India/Karnataka/Bangalore/Koramangala/pics" , "contentstring" , nil } ,
{ testBuckets [ 0 ] , "newPrefix0" , "newPrefix0" , nil } ,
{ testBuckets [ 0 ] , "newPrefix1" , "newPrefix1" , nil } ,
{ testBuckets [ 0 ] , "newzen/zen/recurse/again/again/again/pics" , "recurse" , nil } ,
{ testBuckets [ 0 ] , "obj0" , "obj0" , nil } ,
{ testBuckets [ 0 ] , "obj1" , "obj1" , nil } ,
{ testBuckets [ 0 ] , "obj2" , "obj2" , nil } ,
{ testBuckets [ 1 ] , "obj1" , "obj1" , nil } ,
{ testBuckets [ 1 ] , "obj2" , "obj2" , nil } ,
{ testBuckets [ 1 ] , "temporary/0/" , "" , nil } ,
{ testBuckets [ 3 ] , "A/B" , "contentstring" , nil } ,
2020-11-04 10:56:58 -05:00
{ testBuckets [ 4 ] , "file1/receipt.json" , "content" , nil } ,
{ testBuckets [ 4 ] , "file1/guidSplunk-aaaa/file" , "content" , nil } ,
2020-12-19 12:36:04 -05:00
{ testBuckets [ 5 ] , "dir/day_id=2017-10-10/issue" , "content" , nil } ,
{ testBuckets [ 5 ] , "dir/day_id=2017-10-11/issue" , "content" , nil } ,
2020-08-18 15:19:44 -04:00
}
2020-12-19 12:36:04 -05:00
2020-08-18 15:19:44 -04:00
for _ , object := range testObjects {
md5Bytes := md5 . Sum ( [ ] byte ( object . content ) )
_ , err = obj . PutObject ( context . Background ( ) , object . parentBucket , object . name , mustGetPutObjReader ( t , bytes . NewBufferString ( object . content ) ,
int64 ( len ( object . content ) ) , hex . EncodeToString ( md5Bytes [ : ] ) , "" ) , ObjectOptions { UserDefined : object . meta } )
if err != nil {
t . Fatalf ( "%s : %s" , instanceType , err . Error ( ) )
}
}
// Formualting the result data set to be expected from ListObjects call inside the tests,
// This will be used in testCases and used for asserting the correctness of ListObjects output in the tests.
resultCases := [ ] ListObjectsInfo {
// ListObjectsResult-0.
// Testing for listing all objects in the bucket, (testCase 20,21,22).
{
IsTruncated : false ,
Objects : [ ] ObjectInfo {
{ Name : "Asia-maps.png" } ,
{ Name : "Asia/India/India-summer-photos-1" } ,
{ Name : "Asia/India/Karnataka/Bangalore/Koramangala/pics" } ,
{ Name : "newPrefix0" } ,
{ Name : "newPrefix1" } ,
{ Name : "newzen/zen/recurse/again/again/again/pics" } ,
{ Name : "obj0" } ,
{ Name : "obj1" } ,
{ Name : "obj2" } ,
} ,
} ,
// ListObjectsResult-1.
// Used for asserting the truncated case, (testCase 23).
{
IsTruncated : true ,
Objects : [ ] ObjectInfo {
{ Name : "Asia-maps.png" } ,
{ Name : "Asia/India/India-summer-photos-1" } ,
{ Name : "Asia/India/Karnataka/Bangalore/Koramangala/pics" } ,
{ Name : "newPrefix0" } ,
{ Name : "newPrefix1" } ,
} ,
} ,
// ListObjectsResult-2.
// (TestCase 24).
{
IsTruncated : true ,
Objects : [ ] ObjectInfo {
{ Name : "Asia-maps.png" } ,
{ Name : "Asia/India/India-summer-photos-1" } ,
{ Name : "Asia/India/Karnataka/Bangalore/Koramangala/pics" } ,
{ Name : "newPrefix0" } ,
} ,
} ,
// ListObjectsResult-3.
// (TestCase 25).
{
IsTruncated : true ,
Objects : [ ] ObjectInfo {
{ Name : "Asia-maps.png" } ,
{ Name : "Asia/India/India-summer-photos-1" } ,
{ Name : "Asia/India/Karnataka/Bangalore/Koramangala/pics" } ,
} ,
} ,
// ListObjectsResult-4.
// Again used for truncated case.
// (TestCase 26).
{
IsTruncated : true ,
Objects : [ ] ObjectInfo {
{ Name : "Asia-maps.png" } ,
} ,
} ,
// ListObjectsResult-5.
// Used for Asserting prefixes.
// Used for test case with prefix "new", (testCase 27-29).
{
IsTruncated : false ,
Objects : [ ] ObjectInfo {
{ Name : "newPrefix0" } ,
{ Name : "newPrefix1" } ,
{ Name : "newzen/zen/recurse/again/again/again/pics" } ,
} ,
} ,
// ListObjectsResult-6.
// Used for Asserting prefixes.
// Used for test case with prefix = "obj", (testCase 30).
{
IsTruncated : false ,
Objects : [ ] ObjectInfo {
{ Name : "obj0" } ,
{ Name : "obj1" } ,
{ Name : "obj2" } ,
} ,
} ,
// ListObjectsResult-7.
// Used for Asserting prefixes and truncation.
// Used for test case with prefix = "new" and maxKeys = 1, (testCase 31).
{
IsTruncated : true ,
Objects : [ ] ObjectInfo {
{ Name : "newPrefix0" } ,
} ,
} ,
// ListObjectsResult-8.
// Used for Asserting prefixes.
// Used for test case with prefix = "obj" and maxKeys = 2, (testCase 32).
{
IsTruncated : true ,
Objects : [ ] ObjectInfo {
{ Name : "obj0" } ,
{ Name : "obj1" } ,
} ,
} ,
// ListObjectsResult-9.
// Used for asserting the case with marker, but without prefix.
2021-11-16 12:28:29 -05:00
// marker is set to "newPrefix0" in the testCase, (testCase 33).
2020-08-18 15:19:44 -04:00
{
IsTruncated : false ,
Objects : [ ] ObjectInfo {
{ Name : "newPrefix1" } ,
{ Name : "newzen/zen/recurse/again/again/again/pics" } ,
{ Name : "obj0" } ,
{ Name : "obj1" } ,
{ Name : "obj2" } ,
} ,
} ,
// ListObjectsResult-10.
2021-11-16 12:28:29 -05:00
// marker is set to "newPrefix1" in the testCase, (testCase 34).
2020-08-18 15:19:44 -04:00
{
IsTruncated : false ,
Objects : [ ] ObjectInfo {
{ Name : "newzen/zen/recurse/again/again/again/pics" } ,
{ Name : "obj0" } ,
{ Name : "obj1" } ,
{ Name : "obj2" } ,
} ,
} ,
// ListObjectsResult-11.
2021-11-16 12:28:29 -05:00
// marker is set to "obj0" in the testCase, (testCase 35).
2020-08-18 15:19:44 -04:00
{
IsTruncated : false ,
Objects : [ ] ObjectInfo {
{ Name : "obj1" } ,
{ Name : "obj2" } ,
} ,
} ,
// ListObjectsResult-12.
// Marker is set to "obj1" in the testCase, (testCase 36).
{
IsTruncated : false ,
Objects : [ ] ObjectInfo {
{ Name : "obj2" } ,
} ,
} ,
// ListObjectsResult-13.
// Marker is set to "man" in the testCase, (testCase37).
{
IsTruncated : false ,
Objects : [ ] ObjectInfo {
{ Name : "newPrefix0" } ,
{ Name : "newPrefix1" } ,
{ Name : "newzen/zen/recurse/again/again/again/pics" } ,
{ Name : "obj0" } ,
{ Name : "obj1" } ,
{ Name : "obj2" } ,
} ,
} ,
// ListObjectsResult-14.
// Marker is set to "Abc" in the testCase, (testCase 39).
{
IsTruncated : false ,
Objects : [ ] ObjectInfo {
{ Name : "Asia-maps.png" } ,
{ Name : "Asia/India/India-summer-photos-1" } ,
{ Name : "Asia/India/Karnataka/Bangalore/Koramangala/pics" } ,
{ Name : "newPrefix0" } ,
{ Name : "newPrefix1" } ,
{ Name : "newzen/zen/recurse/again/again/again/pics" } ,
{ Name : "obj0" } ,
{ Name : "obj1" } ,
{ Name : "obj2" } ,
} ,
} ,
// ListObjectsResult-15.
// Marker is set to "Asia/India/India-summer-photos-1" in the testCase, (testCase 40).
{
IsTruncated : false ,
Objects : [ ] ObjectInfo {
{ Name : "Asia/India/Karnataka/Bangalore/Koramangala/pics" } ,
{ Name : "newPrefix0" } ,
{ Name : "newPrefix1" } ,
{ Name : "newzen/zen/recurse/again/again/again/pics" } ,
{ Name : "obj0" } ,
{ Name : "obj1" } ,
{ Name : "obj2" } ,
} ,
} ,
// ListObjectsResult-16.
// Marker is set to "Asia/India/Karnataka/Bangalore/Koramangala/pics" in the testCase, (testCase 41).
{
IsTruncated : false ,
Objects : [ ] ObjectInfo {
{ Name : "newPrefix0" } ,
{ Name : "newPrefix1" } ,
{ Name : "newzen/zen/recurse/again/again/again/pics" } ,
{ Name : "obj0" } ,
{ Name : "obj1" } ,
{ Name : "obj2" } ,
} ,
} ,
// ListObjectsResult-17.
// Used for asserting the case with marker, without prefix but with truncation.
// Marker = "newPrefix0" & maxKeys = 3 in the testCase, (testCase42).
// Output truncated to 3 values.
{
IsTruncated : true ,
Objects : [ ] ObjectInfo {
{ Name : "newPrefix1" } ,
{ Name : "newzen/zen/recurse/again/again/again/pics" } ,
{ Name : "obj0" } ,
} ,
} ,
// ListObjectsResult-18.
// Marker = "newPrefix1" & maxkeys = 1 in the testCase, (testCase43).
// Output truncated to 1 value.
{
IsTruncated : true ,
Objects : [ ] ObjectInfo {
{ Name : "newzen/zen/recurse/again/again/again/pics" } ,
} ,
} ,
// ListObjectsResult-19.
// Marker = "obj0" & maxKeys = 1 in the testCase, (testCase44).
// Output truncated to 1 value.
{
IsTruncated : true ,
Objects : [ ] ObjectInfo {
{ Name : "obj1" } ,
} ,
} ,
// ListObjectsResult-20.
// Marker = "obj0" & prefix = "obj" in the testCase, (testCase 45).
{
IsTruncated : false ,
Objects : [ ] ObjectInfo {
{ Name : "obj1" } ,
{ Name : "obj2" } ,
} ,
} ,
// ListObjectsResult-21.
// Marker = "obj1" & prefix = "obj" in the testCase, (testCase 46).
{
IsTruncated : false ,
Objects : [ ] ObjectInfo {
{ Name : "obj2" } ,
} ,
} ,
// ListObjectsResult-22.
// Marker = "newPrefix0" & prefix = "new" in the testCase,, (testCase 47).
{
IsTruncated : false ,
Objects : [ ] ObjectInfo {
{ Name : "newPrefix1" } ,
{ Name : "newzen/zen/recurse/again/again/again/pics" } ,
} ,
} ,
// ListObjectsResult-23.
// Prefix is set to "Asia/India/" in the testCase, and delimiter is not set (testCase 55).
{
IsTruncated : false ,
Objects : [ ] ObjectInfo {
{ Name : "Asia/India/India-summer-photos-1" } ,
{ Name : "Asia/India/Karnataka/Bangalore/Koramangala/pics" } ,
} ,
} ,
// ListObjectsResult-24.
// Prefix is set to "Asia" in the testCase, and delimiter is not set (testCase 56).
{
IsTruncated : false ,
Objects : [ ] ObjectInfo {
{ Name : "Asia-maps.png" } ,
{ Name : "Asia/India/India-summer-photos-1" } ,
{ Name : "Asia/India/Karnataka/Bangalore/Koramangala/pics" } ,
} ,
} ,
// ListObjectsResult-25.
// Prefix is set to "Asia" in the testCase, and delimiter is set (testCase 57).
{
IsTruncated : false ,
Objects : [ ] ObjectInfo {
{ Name : "Asia-maps.png" } ,
} ,
Prefixes : [ ] string { "Asia/" } ,
} ,
// ListObjectsResult-26.
// prefix = "new" and delimiter is set in the testCase.(testCase 58).
{
IsTruncated : false ,
Objects : [ ] ObjectInfo {
{ Name : "newPrefix0" } ,
{ Name : "newPrefix1" } ,
} ,
Prefixes : [ ] string { "newzen/" } ,
} ,
// ListObjectsResult-27.
// Prefix is set to "Asia/India/" in the testCase, and delimiter is set to forward slash '/' (testCase 59).
{
IsTruncated : false ,
Objects : [ ] ObjectInfo {
{ Name : "Asia/India/India-summer-photos-1" } ,
} ,
Prefixes : [ ] string { "Asia/India/Karnataka/" } ,
} ,
// ListObjectsResult-28.
// Marker is set to "Asia/India/India-summer-photos-1" and delimiter set in the testCase, (testCase 60).
{
IsTruncated : false ,
Objects : [ ] ObjectInfo {
{ Name : "newPrefix0" } ,
{ Name : "newPrefix1" } ,
{ Name : "obj0" } ,
{ Name : "obj1" } ,
{ Name : "obj2" } ,
} ,
Prefixes : [ ] string { "newzen/" } ,
} ,
// ListObjectsResult-29.
// Marker is set to "Asia/India/Karnataka/Bangalore/Koramangala/pics" in the testCase and delimiter set, (testCase 61).
{
IsTruncated : false ,
Objects : [ ] ObjectInfo {
{ Name : "newPrefix0" } ,
{ Name : "newPrefix1" } ,
{ Name : "obj0" } ,
{ Name : "obj1" } ,
{ Name : "obj2" } ,
} ,
Prefixes : [ ] string { "newzen/" } ,
} ,
// ListObjectsResult-30.
// Prefix and Delimiter is set to '/', (testCase 62).
{
IsTruncated : false ,
Objects : [ ] ObjectInfo { } ,
} ,
// ListObjectsResult-31 Empty directory, recursive listing
{
IsTruncated : false ,
Objects : [ ] ObjectInfo {
{ Name : "obj1" } ,
{ Name : "obj2" } ,
{ Name : "temporary/0/" } ,
} ,
} ,
// ListObjectsResult-32 Empty directory, non recursive listing
{
IsTruncated : false ,
Objects : [ ] ObjectInfo {
{ Name : "obj1" } ,
{ Name : "obj2" } ,
} ,
Prefixes : [ ] string { "temporary/" } ,
} ,
// ListObjectsResult-33 Listing empty directory only
{
IsTruncated : false ,
Objects : [ ] ObjectInfo {
{ Name : "temporary/0/" } ,
} ,
} ,
// ListObjectsResult-34:
// * Listing with marker > last object should return empty
// * Listing an object with a trailing slash and '/' delimiter
{
IsTruncated : false ,
Objects : [ ] ObjectInfo { } ,
} ,
2020-11-04 10:56:58 -05:00
// ListObjectsResult-35 list with custom uncommon delimiter
{
IsTruncated : false ,
Objects : [ ] ObjectInfo {
{ Name : "file1/receipt.json" } ,
} ,
Prefixes : [ ] string { "file1/guidSplunk" } ,
} ,
2020-12-19 12:36:04 -05:00
// ListObjectsResult-36 list with nextmarker prefix and maxKeys set to 1.
{
IsTruncated : true ,
Prefixes : [ ] string { "dir/day_id=2017-10-10/" } ,
} ,
2020-08-18 15:19:44 -04:00
}
testCases := [ ] struct {
// Inputs to ListObjects.
bucketName string
prefix string
marker string
delimiter string
maxKeys int32
// Expected output of ListObjects.
result ListObjectsInfo
err error
// Flag indicating whether the test is expected to pass or not.
shouldPass bool
} {
// Test cases with invalid bucket names ( Test number 1-4).
{ ".test" , "" , "" , "" , 0 , ListObjectsInfo { } , BucketNotFound { Bucket : ".test" } , false } ,
{ "Test" , "" , "" , "" , 0 , ListObjectsInfo { } , BucketNotFound { Bucket : "Test" } , false } ,
{ "---" , "" , "" , "" , 0 , ListObjectsInfo { } , BucketNotFound { Bucket : "---" } , false } ,
{ "ad" , "" , "" , "" , 0 , ListObjectsInfo { } , BucketNotFound { Bucket : "ad" } , false } ,
// Using an existing file for bucket name, but its not a directory (5).
{ "simple-file.txt" , "" , "" , "" , 0 , ListObjectsInfo { } , BucketNotFound { Bucket : "simple-file.txt" } , false } ,
2024-01-18 02:03:17 -05:00
// Valid bucket names, but they do not exist (6-8).
2020-08-18 15:19:44 -04:00
{ "volatile-bucket-1" , "" , "" , "" , 0 , ListObjectsInfo { } , BucketNotFound { Bucket : "volatile-bucket-1" } , false } ,
{ "volatile-bucket-2" , "" , "" , "" , 0 , ListObjectsInfo { } , BucketNotFound { Bucket : "volatile-bucket-2" } , false } ,
{ "volatile-bucket-3" , "" , "" , "" , 0 , ListObjectsInfo { } , BucketNotFound { Bucket : "volatile-bucket-3" } , false } ,
2023-09-25 11:13:08 -04:00
// If marker is *after* the last possible object from the prefix it should return an empty list.
{ "test-bucket-list-object" , "Asia" , "europe-object" , "" , 0 , ListObjectsInfo { } , nil , true } ,
2020-08-18 15:19:44 -04:00
// Setting a non-existing directory to be prefix (10-11).
{ "empty-bucket" , "europe/france/" , "" , "" , 1 , ListObjectsInfo { } , nil , true } ,
{ "empty-bucket" , "africa/tunisia/" , "" , "" , 1 , ListObjectsInfo { } , nil , true } ,
// Testing on empty bucket, that is, bucket without any objects in it (12).
{ "empty-bucket" , "" , "" , "" , 0 , ListObjectsInfo { } , nil , true } ,
// Setting maxKeys to negative value (13-14).
{ "empty-bucket" , "" , "" , "" , - 1 , ListObjectsInfo { } , nil , true } ,
{ "empty-bucket" , "" , "" , "" , 1 , ListObjectsInfo { } , nil , true } ,
// Setting maxKeys to a very large value (15).
{ "empty-bucket" , "" , "" , "" , 111100000 , ListObjectsInfo { } , nil , true } ,
// Testing for all 10 objects in the bucket (16).
{ "test-bucket-list-object" , "" , "" , "" , 10 , resultCases [ 0 ] , nil , true } ,
2021-11-16 12:28:29 -05:00
// Testing for negative value of maxKey, this should set maxKeys to listObjectsLimit (17).
2020-08-18 15:19:44 -04:00
{ "test-bucket-list-object" , "" , "" , "" , - 1 , resultCases [ 0 ] , nil , true } ,
// Testing for very large value of maxKey, this should set maxKeys to listObjectsLimit (18).
{ "test-bucket-list-object" , "" , "" , "" , 1234567890 , resultCases [ 0 ] , nil , true } ,
// Testing for trancated value (19-22).
{ "test-bucket-list-object" , "" , "" , "" , 5 , resultCases [ 1 ] , nil , true } ,
{ "test-bucket-list-object" , "" , "" , "" , 4 , resultCases [ 2 ] , nil , true } ,
{ "test-bucket-list-object" , "" , "" , "" , 3 , resultCases [ 3 ] , nil , true } ,
{ "test-bucket-list-object" , "" , "" , "" , 1 , resultCases [ 4 ] , nil , true } ,
// Testing with prefix (23-26).
{ "test-bucket-list-object" , "new" , "" , "" , 3 , resultCases [ 5 ] , nil , true } ,
{ "test-bucket-list-object" , "new" , "" , "" , 4 , resultCases [ 5 ] , nil , true } ,
{ "test-bucket-list-object" , "new" , "" , "" , 5 , resultCases [ 5 ] , nil , true } ,
{ "test-bucket-list-object" , "obj" , "" , "" , 3 , resultCases [ 6 ] , nil , true } ,
// Testing with prefix and truncation (27-28).
{ "test-bucket-list-object" , "new" , "" , "" , 1 , resultCases [ 7 ] , nil , true } ,
{ "test-bucket-list-object" , "obj" , "" , "" , 2 , resultCases [ 8 ] , nil , true } ,
// Testing with marker, but without prefix and truncation (29-33).
{ "test-bucket-list-object" , "" , "newPrefix0" , "" , 6 , resultCases [ 9 ] , nil , true } ,
{ "test-bucket-list-object" , "" , "newPrefix1" , "" , 5 , resultCases [ 10 ] , nil , true } ,
{ "test-bucket-list-object" , "" , "obj0" , "" , 4 , resultCases [ 11 ] , nil , true } ,
{ "test-bucket-list-object" , "" , "obj1" , "" , 2 , resultCases [ 12 ] , nil , true } ,
{ "test-bucket-list-object" , "" , "man" , "" , 11 , resultCases [ 13 ] , nil , true } ,
// Marker being set to a value which is greater than and all object names when sorted (34).
// Expected to send an empty response in this case.
{ "test-bucket-list-object" , "" , "zen" , "" , 10 , ListObjectsInfo { } , nil , true } ,
// Marker being set to a value which is lesser than and all object names when sorted (35).
// Expected to send all the objects in the bucket in this case.
{ "test-bucket-list-object" , "" , "Abc" , "" , 10 , resultCases [ 14 ] , nil , true } ,
// Marker is to a hierarhical value (36-37).
{ "test-bucket-list-object" , "" , "Asia/India/India-summer-photos-1" , "" , 10 , resultCases [ 15 ] , nil , true } ,
{ "test-bucket-list-object" , "" , "Asia/India/Karnataka/Bangalore/Koramangala/pics" , "" , 10 , resultCases [ 16 ] , nil , true } ,
// Testing with marker and truncation, but no prefix (38-40).
{ "test-bucket-list-object" , "" , "newPrefix0" , "" , 3 , resultCases [ 17 ] , nil , true } ,
{ "test-bucket-list-object" , "" , "newPrefix1" , "" , 1 , resultCases [ 18 ] , nil , true } ,
{ "test-bucket-list-object" , "" , "obj0" , "" , 1 , resultCases [ 19 ] , nil , true } ,
// Testing with both marker and prefix, but without truncation (41-43).
// The valid combination of marker and prefix should satisfy strings.HasPrefix(marker, prefix).
{ "test-bucket-list-object" , "obj" , "obj0" , "" , 2 , resultCases [ 20 ] , nil , true } ,
{ "test-bucket-list-object" , "obj" , "obj1" , "" , 1 , resultCases [ 21 ] , nil , true } ,
{ "test-bucket-list-object" , "new" , "newPrefix0" , "" , 2 , resultCases [ 22 ] , nil , true } ,
// Testing with maxKeys set to 0 (44-50).
// The parameters have to valid.
{ "test-bucket-list-object" , "" , "obj1" , "" , 0 , ListObjectsInfo { } , nil , true } ,
{ "test-bucket-list-object" , "" , "obj0" , "" , 0 , ListObjectsInfo { } , nil , true } ,
{ "test-bucket-list-object" , "new" , "" , "" , 0 , ListObjectsInfo { } , nil , true } ,
{ "test-bucket-list-object" , "obj" , "" , "" , 0 , ListObjectsInfo { } , nil , true } ,
{ "test-bucket-list-object" , "obj" , "obj0" , "" , 0 , ListObjectsInfo { } , nil , true } ,
{ "test-bucket-list-object" , "obj" , "obj1" , "" , 0 , ListObjectsInfo { } , nil , true } ,
{ "test-bucket-list-object" , "new" , "newPrefix0" , "" , 0 , ListObjectsInfo { } , nil , true } ,
// Tests on hierarchical key names as prefix.
// Without delimteter the code should recurse into the prefix Dir.
// Tests with prefix, but without delimiter (51-52).
{ "test-bucket-list-object" , "Asia/India/" , "" , "" , 10 , resultCases [ 23 ] , nil , true } ,
{ "test-bucket-list-object" , "Asia" , "" , "" , 10 , resultCases [ 24 ] , nil , true } ,
// Tests with prefix and delimiter (53-55).
// With delimiter the code should not recurse into the sub-directories of prefix Dir.
{ "test-bucket-list-object" , "Asia" , "" , SlashSeparator , 10 , resultCases [ 25 ] , nil , true } ,
{ "test-bucket-list-object" , "new" , "" , SlashSeparator , 10 , resultCases [ 26 ] , nil , true } ,
{ "test-bucket-list-object" , "Asia/India/" , "" , SlashSeparator , 10 , resultCases [ 27 ] , nil , true } ,
// Test with marker set as hierarhical value and with delimiter. (56-57)
{ "test-bucket-list-object" , "" , "Asia/India/India-summer-photos-1" , SlashSeparator , 10 , resultCases [ 28 ] , nil , true } ,
{ "test-bucket-list-object" , "" , "Asia/India/Karnataka/Bangalore/Koramangala/pics" , SlashSeparator , 10 , resultCases [ 29 ] , nil , true } ,
// Test with prefix and delimiter set to '/'. (58)
{ "test-bucket-list-object" , SlashSeparator , "" , SlashSeparator , 10 , resultCases [ 30 ] , nil , true } ,
// Test with invalid prefix (59)
{ "test-bucket-list-object" , "\\" , "" , SlashSeparator , 10 , ListObjectsInfo { } , nil , true } ,
// Test listing an empty directory in recursive mode (60)
{ "test-bucket-empty-dir" , "" , "" , "" , 10 , resultCases [ 31 ] , nil , true } ,
// Test listing an empty directory in a non recursive mode (61)
{ "test-bucket-empty-dir" , "" , "" , SlashSeparator , 10 , resultCases [ 32 ] , nil , true } ,
// Test listing a directory which contains an empty directory (62)
{ "test-bucket-empty-dir" , "" , "temporary/" , "" , 10 , resultCases [ 33 ] , nil , true } ,
// Test listing with marker > last object such that response should be empty (63)
{ "test-bucket-single-object" , "" , "A/C" , "" , 1000 , resultCases [ 34 ] , nil , true } ,
// Test listing an object with a trailing slash and a slash delimiter (64)
{ "test-bucket-list-object" , "Asia-maps.png/" , "" , "/" , 1000 , resultCases [ 34 ] , nil , true } ,
2020-11-04 10:56:58 -05:00
// Test listing an object with uncommon delimiter
{ testBuckets [ 4 ] , "" , "" , "guidSplunk" , 1000 , resultCases [ 35 ] , nil , true } ,
// Test listing an object with uncommon delimiter and matching prefix
{ testBuckets [ 4 ] , "file1/" , "" , "guidSplunk" , 1000 , resultCases [ 35 ] , nil , true } ,
2020-12-19 12:36:04 -05:00
// Test listing at prefix with expected prefix markers
{ testBuckets [ 5 ] , "dir/" , "" , SlashSeparator , 1 , resultCases [ 36 ] , nil , true } ,
2023-09-25 11:13:08 -04:00
{ "test-bucket-list-object" , "Asia" , "A" , "" , 1 , resultCases [ 4 ] , nil , true } ,
2020-08-18 15:19:44 -04:00
}
for i , testCase := range testCases {
testCase := testCase
t . Run ( fmt . Sprintf ( "%s-Test%d" , instanceType , i + 1 ) , func ( t * testing . T ) {
result , err := obj . ListObjectVersions ( context . Background ( ) , testCase . bucketName ,
testCase . prefix , testCase . marker , "" , testCase . delimiter , int ( testCase . maxKeys ) )
if err != nil && testCase . shouldPass {
t . Errorf ( "%s: Expected to pass, but failed with: <ERROR> %s" , instanceType , err . Error ( ) )
}
if err == nil && ! testCase . shouldPass {
t . Errorf ( "%s: Expected to fail with <ERROR> \"%s\", but passed instead" , instanceType , testCase . err . Error ( ) )
}
// Failed as expected, but does it fail for the expected reason.
if err != nil && ! testCase . shouldPass {
if ! strings . Contains ( err . Error ( ) , testCase . err . Error ( ) ) {
t . Errorf ( "%s: Expected to fail with error \"%s\", but instead failed with error \"%s\" instead" , instanceType , testCase . err . Error ( ) , err . Error ( ) )
}
}
// Since there are cases for which ListObjects fails, this is
// necessary. Test passes as expected, but the output values
// are verified for correctness here.
if err == nil && testCase . shouldPass {
// The length of the expected ListObjectsResult.Objects
// should match in both expected result from test cases
// and in the output. On failure calling t.Fatalf,
// otherwise it may lead to index out of range error in
// assertion following this.
if len ( testCase . result . Objects ) != len ( result . Objects ) {
t . Fatalf ( "%s: Expected number of object in the result to be '%d', but found '%d' objects instead" , instanceType , len ( testCase . result . Objects ) , len ( result . Objects ) )
}
for j := 0 ; j < len ( testCase . result . Objects ) ; j ++ {
if testCase . result . Objects [ j ] . Name != result . Objects [ j ] . Name {
t . Errorf ( "%s: Expected object name to be \"%s\", but found \"%s\" instead" , instanceType , testCase . result . Objects [ j ] . Name , result . Objects [ j ] . Name )
}
}
if len ( testCase . result . Prefixes ) != len ( result . Prefixes ) {
2020-10-28 12:18:35 -04:00
t . Log ( testCase , testCase . result . Prefixes , result . Prefixes )
2020-08-18 15:19:44 -04:00
t . Fatalf ( "%s: Expected number of prefixes in the result to be '%d', but found '%d' prefixes instead" , instanceType , len ( testCase . result . Prefixes ) , len ( result . Prefixes ) )
}
for j := 0 ; j < len ( testCase . result . Prefixes ) ; j ++ {
if testCase . result . Prefixes [ j ] != result . Prefixes [ j ] {
t . Errorf ( "%s: Expected prefix name to be \"%s\", but found \"%s\" instead" , instanceType , testCase . result . Prefixes [ j ] , result . Prefixes [ j ] )
}
}
if testCase . result . IsTruncated != result . IsTruncated {
2020-10-28 12:18:35 -04:00
// Allow an extra continuation token.
if ! result . IsTruncated || len ( result . Objects ) == 0 {
t . Errorf ( "%s: Expected IsTruncated flag to be %v, but instead found it to be %v" , instanceType , testCase . result . IsTruncated , result . IsTruncated )
}
2020-08-18 15:19:44 -04:00
}
if testCase . result . IsTruncated && result . NextMarker == "" {
2020-12-19 12:36:04 -05:00
t . Errorf ( "%s: Expected NextMarker to contain a string since listing is truncated, but instead found it to be empty" , instanceType )
2020-08-18 15:19:44 -04:00
}
if ! testCase . result . IsTruncated && result . NextMarker != "" {
2020-10-28 12:18:35 -04:00
if ! result . IsTruncated || len ( result . Objects ) == 0 {
2020-12-19 12:36:04 -05:00
t . Errorf ( "%s: Expected NextMarker to be empty since listing is not truncated, but instead found `%v`" , instanceType , result . NextMarker )
2020-10-28 12:18:35 -04:00
}
2020-08-18 15:19:44 -04:00
}
2019-05-02 01:06:57 -04:00
}
} )
2016-03-16 20:30:22 -04:00
}
}
2021-11-10 13:41:21 -05:00
// Wrapper for calling ListObjects continuation tests for both Erasure multiple disks and single node setup.
func TestListObjectsContinuation ( t * testing . T ) {
ExecObjectLayerTest ( t , testListObjectsContinuation )
}
// Unit test for ListObjects in general.
func testListObjectsContinuation ( obj ObjectLayer , instanceType string , t1 TestErrHandler ) {
t , _ := t1 . ( * testing . T )
testBuckets := [ ] string {
// This bucket is used for testing ListObject operations.
"test-bucket-list-object-continuation-1" ,
"test-bucket-list-object-continuation-2" ,
}
for _ , bucket := range testBuckets {
2022-12-23 10:46:00 -05:00
err := obj . MakeBucket ( context . Background ( ) , bucket , MakeBucketOptions { } )
2021-11-10 13:41:21 -05:00
if err != nil {
t . Fatalf ( "%s : %s" , instanceType , err . Error ( ) )
}
}
var err error
testObjects := [ ] struct {
parentBucket string
name string
content string
meta map [ string ] string
} {
{ testBuckets [ 0 ] , "a/1.txt" , "contentstring" , nil } ,
{ testBuckets [ 0 ] , "a-1.txt" , "contentstring" , nil } ,
{ testBuckets [ 0 ] , "a.txt" , "contentstring" , nil } ,
{ testBuckets [ 0 ] , "apache2-doc/1.txt" , "contentstring" , nil } ,
{ testBuckets [ 0 ] , "apache2/1.txt" , "contentstring" , nil } ,
{ testBuckets [ 0 ] , "apache2/-sub/2.txt" , "contentstring" , nil } ,
{ testBuckets [ 1 ] , "azerty/1.txt" , "contentstring" , nil } ,
{ testBuckets [ 1 ] , "apache2-doc/1.txt" , "contentstring" , nil } ,
{ testBuckets [ 1 ] , "apache2/1.txt" , "contentstring" , nil } ,
}
for _ , object := range testObjects {
md5Bytes := md5 . Sum ( [ ] byte ( object . content ) )
_ , err = obj . PutObject ( context . Background ( ) , object . parentBucket , object . name , mustGetPutObjReader ( t , bytes . NewBufferString ( object . content ) ,
int64 ( len ( object . content ) ) , hex . EncodeToString ( md5Bytes [ : ] ) , "" ) , ObjectOptions { UserDefined : object . meta } )
if err != nil {
t . Fatalf ( "%s : %s" , instanceType , err . Error ( ) )
}
}
2022-09-09 11:13:06 -04:00
// Formulating the result data set to be expected from ListObjects call inside the tests,
2021-11-10 13:41:21 -05:00
// This will be used in testCases and used for asserting the correctness of ListObjects output in the tests.
resultCases := [ ] ListObjectsInfo {
{
Objects : [ ] ObjectInfo {
{ Name : "a-1.txt" } ,
{ Name : "a.txt" } ,
{ Name : "a/1.txt" } ,
{ Name : "apache2-doc/1.txt" } ,
{ Name : "apache2/-sub/2.txt" } ,
{ Name : "apache2/1.txt" } ,
} ,
} ,
{
Objects : [ ] ObjectInfo {
{ Name : "apache2-doc/1.txt" } ,
{ Name : "apache2/1.txt" } ,
} ,
} ,
{
Prefixes : [ ] string { "apache2-doc/" , "apache2/" , "azerty/" } ,
} ,
}
testCases := [ ] struct {
// Inputs to ListObjects.
bucketName string
prefix string
delimiter string
page int
// Expected output of ListObjects.
result ListObjectsInfo
} {
{ testBuckets [ 0 ] , "" , "" , 1 , resultCases [ 0 ] } ,
{ testBuckets [ 0 ] , "a" , "" , 1 , resultCases [ 0 ] } ,
{ testBuckets [ 1 ] , "apache" , "" , 1 , resultCases [ 1 ] } ,
{ testBuckets [ 1 ] , "" , "/" , 1 , resultCases [ 2 ] } ,
}
for i , testCase := range testCases {
testCase := testCase
t . Run ( fmt . Sprintf ( "%s-Test%d" , instanceType , i + 1 ) , func ( t * testing . T ) {
var foundObjects [ ] ObjectInfo
var foundPrefixes [ ] string
2022-01-02 12:15:06 -05:00
marker := ""
2021-11-10 13:41:21 -05:00
for {
result , err := obj . ListObjects ( context . Background ( ) , testCase . bucketName ,
testCase . prefix , marker , testCase . delimiter , testCase . page )
if err != nil {
t . Fatalf ( "Test %d: %s: Expected to pass, but failed with: <ERROR> %s" , i + 1 , instanceType , err . Error ( ) )
}
foundObjects = append ( foundObjects , result . Objects ... )
foundPrefixes = append ( foundPrefixes , result . Prefixes ... )
if ! result . IsTruncated {
break
}
marker = result . NextMarker
if len ( result . Objects ) > 0 {
// Discard marker, so it cannot resume listing.
marker = result . Objects [ len ( result . Objects ) - 1 ] . Name
}
}
if len ( testCase . result . Objects ) != len ( foundObjects ) {
t . Logf ( "want: %v" , objInfoNames ( testCase . result . Objects ) )
t . Logf ( "got: %v" , objInfoNames ( foundObjects ) )
t . Errorf ( "Test %d: %s: Expected number of objects in the result to be '%d', but found '%d' objects instead" ,
i + 1 , instanceType , len ( testCase . result . Objects ) , len ( foundObjects ) )
}
for j := 0 ; j < len ( testCase . result . Objects ) ; j ++ {
if j >= len ( foundObjects ) {
t . Errorf ( "Test %d: %s: Expected object name to be \"%s\", but not nothing instead" , i + 1 , instanceType , testCase . result . Objects [ j ] . Name )
continue
}
if testCase . result . Objects [ j ] . Name != foundObjects [ j ] . Name {
t . Errorf ( "Test %d: %s: Expected object name to be \"%s\", but found \"%s\" instead" , i + 1 , instanceType , testCase . result . Objects [ j ] . Name , foundObjects [ j ] . Name )
}
}
if len ( testCase . result . Prefixes ) != len ( foundPrefixes ) {
t . Logf ( "want: %v" , testCase . result . Prefixes )
t . Logf ( "got: %v" , foundPrefixes )
t . Errorf ( "Test %d: %s: Expected number of prefixes in the result to be '%d', but found '%d' prefixes instead" ,
i + 1 , instanceType , len ( testCase . result . Prefixes ) , len ( foundPrefixes ) )
}
for j := 0 ; j < len ( testCase . result . Prefixes ) ; j ++ {
if j >= len ( foundPrefixes ) {
t . Errorf ( "Test %d: %s: Expected prefix name to be \"%s\", but found no result" , i + 1 , instanceType , testCase . result . Prefixes [ j ] )
continue
}
if testCase . result . Prefixes [ j ] != foundPrefixes [ j ] {
t . Errorf ( "Test %d: %s: Expected prefix name to be \"%s\", but found \"%s\" instead" , i + 1 , instanceType , testCase . result . Prefixes [ j ] , foundPrefixes [ j ] )
}
}
} )
}
}
2016-10-31 16:57:30 -04:00
// Initialize FS backend for the benchmark.
2016-10-05 15:48:07 -04:00
func initFSObjectsB ( disk string , t * testing . B ) ( obj ObjectLayer ) {
2023-07-03 12:47:40 -04:00
obj , _ , err := initObjectLayer ( context . Background ( ) , mustGetPoolEndpoints ( 0 , disk ) )
2016-10-05 15:48:07 -04:00
if err != nil {
2022-05-30 13:58:37 -04:00
t . Fatal ( err )
2016-10-05 15:48:07 -04:00
}
2021-10-08 14:37:19 -04:00
newTestConfig ( globalMinioDefaultRegion , obj )
2022-10-14 06:08:40 -04:00
initAllSubsystems ( GlobalContext )
2016-10-05 15:48:07 -04:00
return obj
}
2016-10-31 16:57:30 -04:00
// BenchmarkListObjects - Run ListObject Repeatedly and benchmark.
2016-03-16 20:30:22 -04:00
func BenchmarkListObjects ( b * testing . B ) {
2016-04-08 13:37:38 -04:00
// Make a temporary directory to use as the obj.
2022-07-25 15:37:26 -04:00
directory := b . TempDir ( )
2016-03-16 20:30:22 -04:00
2016-04-08 13:37:38 -04:00
// Create the obj.
2016-10-05 15:48:07 -04:00
obj := initFSObjectsB ( directory , b )
2016-03-16 20:30:22 -04:00
2016-10-31 16:57:30 -04:00
bucket := "ls-benchmark-bucket"
2016-03-16 20:30:22 -04:00
// Create a bucket.
2022-12-23 10:46:00 -05:00
err := obj . MakeBucket ( context . Background ( ) , bucket , MakeBucketOptions { } )
2016-03-16 20:30:22 -04:00
if err != nil {
b . Fatal ( err )
}
2016-10-31 16:57:30 -04:00
// Insert objects to be listed and benchmarked later.
2016-03-16 20:30:22 -04:00
for i := 0 ; i < 20000 ; i ++ {
key := "obj" + strconv . Itoa ( i )
2019-02-09 00:31:06 -05:00
_ , err = obj . PutObject ( context . Background ( ) , bucket , key , mustGetPutObjReader ( b , bytes . NewBufferString ( key ) , int64 ( len ( key ) ) , "" , "" ) , ObjectOptions { } )
2016-03-16 20:30:22 -04:00
if err != nil {
b . Fatal ( err )
}
}
b . ResetTimer ( )
// List the buckets over and over and over.
for i := 0 ; i < b . N ; i ++ {
2018-03-15 16:27:16 -04:00
_ , err = obj . ListObjects ( context . Background ( ) , bucket , "" , "obj9000" , "" , - 1 )
2016-03-16 20:30:22 -04:00
if err != nil {
b . Fatal ( err )
}
}
}