storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/object_api_suite_test.go (about)

     1  /*
     2   * MinIO Cloud Storage, (C) 2015, 2016, 2017 MinIO, Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package cmd
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"io"
    23  	"math/rand"
    24  	"strconv"
    25  	"testing"
    26  
    27  	"github.com/dustin/go-humanize"
    28  
    29  	"storj.io/minio/pkg/kms"
    30  )
    31  
    32  // Return pointer to testOneByteReadEOF{}
    33  func newTestReaderEOF(data []byte) io.Reader {
    34  	return &testOneByteReadEOF{false, data}
    35  }
    36  
    37  // OneByteReadEOF - implements io.Reader which returns 1 byte along with io.EOF error.
    38  type testOneByteReadEOF struct {
    39  	eof  bool
    40  	data []byte
    41  }
    42  
    43  func (r *testOneByteReadEOF) Read(p []byte) (n int, err error) {
    44  	if r.eof {
    45  		return 0, io.EOF
    46  	}
    47  	n = copy(p, r.data)
    48  	r.eof = true
    49  	return n, io.EOF
    50  }
    51  
    52  // Return pointer to testOneByteReadNoEOF{}
    53  func newTestReaderNoEOF(data []byte) io.Reader {
    54  	return &testOneByteReadNoEOF{false, data}
    55  }
    56  
    57  // testOneByteReadNoEOF - implements io.Reader which returns 1 byte and nil error, but
    58  // returns io.EOF on the next Read().
    59  type testOneByteReadNoEOF struct {
    60  	eof  bool
    61  	data []byte
    62  }
    63  
    64  func (r *testOneByteReadNoEOF) Read(p []byte) (n int, err error) {
    65  	if r.eof {
    66  		return 0, io.EOF
    67  	}
    68  	n = copy(p, r.data)
    69  	r.eof = true
    70  	return n, nil
    71  }
    72  
    73  // Wrapper for calling testMakeBucket for both Erasure and FS.
    74  func TestMakeBucket(t *testing.T) {
    75  	ExecObjectLayerTest(t, testMakeBucket)
    76  }
    77  
    78  // Tests validate bucket creation.
    79  func testMakeBucket(obj ObjectLayer, instanceType string, t TestErrHandler) {
    80  	err := obj.MakeBucketWithLocation(context.Background(), "bucket-unknown", BucketOptions{})
    81  	if err != nil {
    82  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
    83  	}
    84  }
    85  
    86  // Wrapper for calling testMultipartObjectCreation for both Erasure and FS.
    87  func TestMultipartObjectCreation(t *testing.T) {
    88  	ExecExtendedObjectLayerTest(t, testMultipartObjectCreation)
    89  }
    90  
    91  // Tests validate creation of part files during Multipart operation.
    92  func testMultipartObjectCreation(obj ObjectLayer, instanceType string, t TestErrHandler) {
    93  	var opts ObjectOptions
    94  	err := obj.MakeBucketWithLocation(context.Background(), "bucket", BucketOptions{})
    95  	if err != nil {
    96  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
    97  	}
    98  	uploadID, err := obj.NewMultipartUpload(context.Background(), "bucket", "key", opts)
    99  	if err != nil {
   100  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
   101  	}
   102  	// Create a byte array of 5MiB.
   103  	data := bytes.Repeat([]byte("0123456789abcdef"), 5*humanize.MiByte/16)
   104  	completedParts := CompleteMultipartUpload{}
   105  	for i := 1; i <= 10; i++ {
   106  		expectedETaghex := getMD5Hash(data)
   107  
   108  		var calcPartInfo PartInfo
   109  		calcPartInfo, err = obj.PutObjectPart(context.Background(), "bucket", "key", uploadID, i, mustGetPutObjReader(t, bytes.NewBuffer(data), int64(len(data)), expectedETaghex, ""), opts)
   110  		if err != nil {
   111  			t.Errorf("%s: <ERROR> %s", instanceType, err)
   112  		}
   113  		if calcPartInfo.ETag != expectedETaghex {
   114  			t.Errorf("MD5 Mismatch")
   115  		}
   116  		completedParts.Parts = append(completedParts.Parts, CompletePart{
   117  			PartNumber: i,
   118  			ETag:       calcPartInfo.ETag,
   119  		})
   120  	}
   121  	objInfo, err := obj.CompleteMultipartUpload(context.Background(), "bucket", "key", uploadID, completedParts.Parts, ObjectOptions{})
   122  	if err != nil {
   123  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
   124  	}
   125  	if objInfo.ETag != "7d364cb728ce42a74a96d22949beefb2-10" {
   126  		t.Errorf("Md5 mismtch")
   127  	}
   128  }
   129  
   130  // Wrapper for calling testMultipartObjectAbort for both Erasure and FS.
   131  func TestMultipartObjectAbort(t *testing.T) {
   132  	ExecObjectLayerTest(t, testMultipartObjectAbort)
   133  }
   134  
   135  // Tests validate abortion of Multipart operation.
   136  func testMultipartObjectAbort(obj ObjectLayer, instanceType string, t TestErrHandler) {
   137  	var opts ObjectOptions
   138  	err := obj.MakeBucketWithLocation(context.Background(), "bucket", BucketOptions{})
   139  	if err != nil {
   140  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
   141  	}
   142  	uploadID, err := obj.NewMultipartUpload(context.Background(), "bucket", "key", opts)
   143  	if err != nil {
   144  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
   145  	}
   146  
   147  	parts := make(map[int]string)
   148  	metadata := make(map[string]string)
   149  	for i := 1; i <= 10; i++ {
   150  		randomPerm := rand.Perm(10)
   151  		randomString := ""
   152  		for _, num := range randomPerm {
   153  			randomString = randomString + strconv.Itoa(num)
   154  		}
   155  
   156  		expectedETaghex := getMD5Hash([]byte(randomString))
   157  
   158  		metadata["md5"] = expectedETaghex
   159  		var calcPartInfo PartInfo
   160  		calcPartInfo, err = obj.PutObjectPart(context.Background(), "bucket", "key", uploadID, i, mustGetPutObjReader(t, bytes.NewBufferString(randomString), int64(len(randomString)), expectedETaghex, ""), opts)
   161  		if err != nil {
   162  			t.Fatalf("%s: <ERROR> %s", instanceType, err)
   163  		}
   164  		if calcPartInfo.ETag != expectedETaghex {
   165  			t.Errorf("Md5 Mismatch")
   166  		}
   167  		parts[i] = expectedETaghex
   168  	}
   169  	err = obj.AbortMultipartUpload(context.Background(), "bucket", "key", uploadID, ObjectOptions{})
   170  	if err != nil {
   171  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
   172  	}
   173  }
   174  
   175  // Wrapper for calling testMultipleObjectCreation for both Erasure and FS.
   176  func TestMultipleObjectCreation(t *testing.T) {
   177  	ExecExtendedObjectLayerTest(t, testMultipleObjectCreation)
   178  }
   179  
   180  // Tests validate object creation.
   181  func testMultipleObjectCreation(obj ObjectLayer, instanceType string, t TestErrHandler) {
   182  	objects := make(map[string][]byte)
   183  	var opts ObjectOptions
   184  	err := obj.MakeBucketWithLocation(context.Background(), "bucket", BucketOptions{})
   185  	if err != nil {
   186  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
   187  	}
   188  	for i := 0; i < 10; i++ {
   189  		randomPerm := rand.Perm(100)
   190  		randomString := ""
   191  		for _, num := range randomPerm {
   192  			randomString = randomString + strconv.Itoa(num)
   193  		}
   194  
   195  		expectedETaghex := getMD5Hash([]byte(randomString))
   196  
   197  		key := "obj" + strconv.Itoa(i)
   198  		objects[key] = []byte(randomString)
   199  		metadata := make(map[string]string)
   200  		metadata["etag"] = expectedETaghex
   201  		var objInfo ObjectInfo
   202  		objInfo, err = obj.PutObject(context.Background(), "bucket", key, mustGetPutObjReader(t, bytes.NewBufferString(randomString), int64(len(randomString)), metadata["etag"], ""), ObjectOptions{UserDefined: metadata})
   203  		if err != nil {
   204  			t.Fatalf("%s: <ERROR> %s", instanceType, err)
   205  		}
   206  		if objInfo.ETag != expectedETaghex {
   207  			t.Errorf("Md5 Mismatch")
   208  		}
   209  	}
   210  
   211  	for key, value := range objects {
   212  		var byteBuffer bytes.Buffer
   213  		err = GetObject(context.Background(), obj, "bucket", key, 0, int64(len(value)), &byteBuffer, "", opts)
   214  		if err != nil {
   215  			t.Fatalf("%s: <ERROR> %s", instanceType, err)
   216  		}
   217  		if !bytes.Equal(byteBuffer.Bytes(), value) {
   218  			t.Errorf("%s: Mismatch of GetObject data with the expected one.", instanceType)
   219  		}
   220  
   221  		objInfo, err := obj.GetObjectInfo(context.Background(), "bucket", key, opts)
   222  		if err != nil {
   223  			t.Fatalf("%s: <ERROR> %s", instanceType, err)
   224  		}
   225  		if objInfo.Size != int64(len(value)) {
   226  			t.Errorf("%s: Size mismatch of the GetObject data.", instanceType)
   227  		}
   228  
   229  	}
   230  }
   231  
   232  // Wrapper for calling TestPaging for both Erasure and FS.
   233  func TestPaging(t *testing.T) {
   234  	ExecObjectLayerTest(t, testPaging)
   235  }
   236  
   237  // Tests validate creation of objects and the order of listing using various filters for ListObjects operation.
   238  func testPaging(obj ObjectLayer, instanceType string, t TestErrHandler) {
   239  	obj.MakeBucketWithLocation(context.Background(), "bucket", BucketOptions{})
   240  	result, err := obj.ListObjects(context.Background(), "bucket", "", "", "", 0)
   241  	if err != nil {
   242  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
   243  	}
   244  	if len(result.Objects) != 0 {
   245  		t.Errorf("%s: Number of objects in the result different from expected value.", instanceType)
   246  	}
   247  	if result.IsTruncated {
   248  		t.Errorf("%s: Expected IsTruncated to be `false`, but instead found it to be `%v`", instanceType, result.IsTruncated)
   249  	}
   250  
   251  	uploadContent := "The specified multipart upload does not exist. The upload ID might be invalid, or the multipart upload might have been aborted or completed."
   252  	var opts ObjectOptions
   253  	// check before paging occurs.
   254  	for i := 0; i < 5; i++ {
   255  		key := "obj" + strconv.Itoa(i)
   256  		_, err = obj.PutObject(context.Background(), "bucket", key, mustGetPutObjReader(t, bytes.NewBufferString(uploadContent), int64(len(uploadContent)), "", ""), opts)
   257  		if err != nil {
   258  			t.Fatalf("%s: <ERROR> %s", instanceType, err)
   259  		}
   260  
   261  		result, err = obj.ListObjects(context.Background(), "bucket", "", "", "", 5)
   262  		if err != nil {
   263  			t.Fatalf("%s: <ERROR> %s", instanceType, err)
   264  		}
   265  		if len(result.Objects) != i+1 {
   266  			t.Errorf("%s: Expected length of objects to be %d, instead found to be %d", instanceType, len(result.Objects), i+1)
   267  		}
   268  		if result.IsTruncated {
   269  			t.Errorf("%s: Expected IsTruncated to be `false`, but instead found it to be `%v`", instanceType, result.IsTruncated)
   270  		}
   271  	}
   272  
   273  	// check after paging occurs pages work.
   274  	for i := 6; i <= 10; i++ {
   275  		key := "obj" + strconv.Itoa(i)
   276  		_, err = obj.PutObject(context.Background(), "bucket", key, mustGetPutObjReader(t, bytes.NewBufferString(uploadContent), int64(len(uploadContent)), "", ""), opts)
   277  		if err != nil {
   278  			t.Fatalf("%s: <ERROR> %s", instanceType, err)
   279  		}
   280  		result, err = obj.ListObjects(context.Background(), "bucket", "obj", "", "", 5)
   281  		if err != nil {
   282  			t.Fatalf("%s: <ERROR> %s", instanceType, err)
   283  		}
   284  		if len(result.Objects) != 5 {
   285  			t.Errorf("%s: Expected length of objects to be %d, instead found to be %d", instanceType, 5, len(result.Objects))
   286  		}
   287  		if !result.IsTruncated {
   288  			t.Errorf("%s: Expected IsTruncated to be `true`, but instead found it to be `%v`", instanceType, result.IsTruncated)
   289  		}
   290  	}
   291  	// check paging with prefix at end returns less objects.
   292  	{
   293  		_, err = obj.PutObject(context.Background(), "bucket", "newPrefix", mustGetPutObjReader(t, bytes.NewBufferString(uploadContent), int64(len(uploadContent)), "", ""), opts)
   294  		if err != nil {
   295  			t.Fatalf("%s: <ERROR> %s", instanceType, err)
   296  		}
   297  		_, err = obj.PutObject(context.Background(), "bucket", "newPrefix2", mustGetPutObjReader(t, bytes.NewBufferString(uploadContent), int64(len(uploadContent)), "", ""), opts)
   298  		if err != nil {
   299  			t.Fatalf("%s: <ERROR> %s", instanceType, err)
   300  		}
   301  		result, err = obj.ListObjects(context.Background(), "bucket", "new", "", "", 5)
   302  		if err != nil {
   303  			t.Fatalf("%s: <ERROR> %s", instanceType, err)
   304  		}
   305  		if len(result.Objects) != 2 {
   306  			t.Errorf("%s: Expected length of objects to be %d, instead found to be %d", instanceType, 2, len(result.Objects))
   307  		}
   308  	}
   309  
   310  	// check ordering of pages.
   311  	{
   312  		result, err = obj.ListObjects(context.Background(), "bucket", "", "", "", 1000)
   313  		if err != nil {
   314  			t.Fatalf("%s: <ERROR> %s", instanceType, err)
   315  		}
   316  		if result.Objects[0].Name != "newPrefix" {
   317  			t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "newPrefix", result.Objects[0].Name)
   318  		}
   319  		if result.Objects[1].Name != "newPrefix2" {
   320  			t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "newPrefix", result.Objects[1].Name)
   321  		}
   322  		if result.Objects[2].Name != "obj0" {
   323  			t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "newPrefix", result.Objects[2].Name)
   324  		}
   325  		if result.Objects[3].Name != "obj1" {
   326  			t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "newPrefix", result.Objects[3].Name)
   327  		}
   328  		if result.Objects[4].Name != "obj10" {
   329  			t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "newPrefix", result.Objects[4].Name)
   330  		}
   331  	}
   332  
   333  	// check delimited results with delimiter and prefix.
   334  	{
   335  		_, err = obj.PutObject(context.Background(), "bucket", "this/is/delimited", mustGetPutObjReader(t, bytes.NewBufferString(uploadContent), int64(len(uploadContent)), "", ""), opts)
   336  		if err != nil {
   337  			t.Fatalf("%s: <ERROR> %s", instanceType, err)
   338  		}
   339  		_, err = obj.PutObject(context.Background(), "bucket", "this/is/also/a/delimited/file", mustGetPutObjReader(t, bytes.NewBufferString(uploadContent), int64(len(uploadContent)), "", ""), opts)
   340  		if err != nil {
   341  			t.Fatalf("%s: <ERROR> %s", instanceType, err)
   342  		}
   343  		result, err = obj.ListObjects(context.Background(), "bucket", "this/is/", "", SlashSeparator, 10)
   344  		if err != nil {
   345  			t.Fatalf("%s: <ERROR> %s", instanceType, err)
   346  		}
   347  		if len(result.Objects) != 1 {
   348  			t.Errorf("%s: Expected the number of objects in the result to be %d, but instead found %d", instanceType, 1, len(result.Objects))
   349  		}
   350  		if result.Prefixes[0] != "this/is/also/" {
   351  			t.Errorf("%s: Expected prefix to be `%s`, but instead found `%s`", instanceType, "this/is/also/", result.Prefixes[0])
   352  		}
   353  	}
   354  
   355  	// check delimited results with delimiter without prefix.
   356  	{
   357  		result, err = obj.ListObjects(context.Background(), "bucket", "", "", SlashSeparator, 1000)
   358  		if err != nil {
   359  			t.Fatalf("%s: <ERROR> %s", instanceType, err)
   360  		}
   361  
   362  		if result.Objects[0].Name != "newPrefix" {
   363  			t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "newPrefix", result.Objects[0].Name)
   364  		}
   365  		if result.Objects[1].Name != "newPrefix2" {
   366  			t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "newPrefix", result.Objects[1].Name)
   367  		}
   368  		if result.Objects[2].Name != "obj0" {
   369  			t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "newPrefix", result.Objects[2].Name)
   370  		}
   371  		if result.Objects[3].Name != "obj1" {
   372  			t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "newPrefix", result.Objects[3].Name)
   373  		}
   374  		if result.Objects[4].Name != "obj10" {
   375  			t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "newPrefix", result.Objects[4].Name)
   376  		}
   377  		if result.Prefixes[0] != "this/" {
   378  			t.Errorf("%s: Expected the prefix to be `%s`, but instead found `%s`", instanceType, "this/", result.Prefixes[0])
   379  		}
   380  	}
   381  
   382  	// check results with Marker.
   383  	{
   384  
   385  		result, err = obj.ListObjects(context.Background(), "bucket", "", "newPrefix", "", 3)
   386  		if err != nil {
   387  			t.Fatalf("%s: <ERROR> %s", instanceType, err)
   388  		}
   389  		if result.Objects[0].Name != "newPrefix2" {
   390  			t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "newPrefix2", result.Objects[0].Name)
   391  		}
   392  		if result.Objects[1].Name != "obj0" {
   393  			t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "obj0", result.Objects[1].Name)
   394  		}
   395  		if result.Objects[2].Name != "obj1" {
   396  			t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "obj1", result.Objects[2].Name)
   397  		}
   398  	}
   399  	// check ordering of results with prefix.
   400  	{
   401  		result, err = obj.ListObjects(context.Background(), "bucket", "obj", "", "", 1000)
   402  		if err != nil {
   403  			t.Fatalf("%s: <ERROR> %s", instanceType, err)
   404  		}
   405  		if result.Objects[0].Name != "obj0" {
   406  			t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "obj0", result.Objects[0].Name)
   407  		}
   408  		if result.Objects[1].Name != "obj1" {
   409  			t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "obj1", result.Objects[1].Name)
   410  		}
   411  		if result.Objects[2].Name != "obj10" {
   412  			t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "obj10", result.Objects[2].Name)
   413  		}
   414  		if result.Objects[3].Name != "obj2" {
   415  			t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "obj2", result.Objects[3].Name)
   416  		}
   417  		if result.Objects[4].Name != "obj3" {
   418  			t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "obj3", result.Objects[4].Name)
   419  		}
   420  	}
   421  	// check ordering of results with prefix and no paging.
   422  	{
   423  		result, err = obj.ListObjects(context.Background(), "bucket", "new", "", "", 5)
   424  		if err != nil {
   425  			t.Fatalf("%s: <ERROR> %s", instanceType, err)
   426  		}
   427  		if result.Objects[0].Name != "newPrefix" {
   428  			t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "newPrefix", result.Objects[0].Name)
   429  		}
   430  		if result.Objects[1].Name != "newPrefix2" {
   431  			t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "newPrefix2", result.Objects[0].Name)
   432  		}
   433  	}
   434  }
   435  
   436  // Wrapper for calling testObjectOverwriteWorks for both Erasure and FS.
   437  func TestObjectOverwriteWorks(t *testing.T) {
   438  	ExecObjectLayerTest(t, testObjectOverwriteWorks)
   439  }
   440  
   441  // Tests validate overwriting of an existing object.
   442  func testObjectOverwriteWorks(obj ObjectLayer, instanceType string, t TestErrHandler) {
   443  	err := obj.MakeBucketWithLocation(context.Background(), "bucket", BucketOptions{})
   444  	if err != nil {
   445  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
   446  	}
   447  
   448  	var opts ObjectOptions
   449  	uploadContent := "The list of parts was not in ascending order. The parts list must be specified in order by part number."
   450  	length := int64(len(uploadContent))
   451  	_, err = obj.PutObject(context.Background(), "bucket", "object", mustGetPutObjReader(t, bytes.NewBufferString(uploadContent), length, "", ""), opts)
   452  	if err != nil {
   453  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
   454  	}
   455  
   456  	uploadContent = "The specified multipart upload does not exist. The upload ID might be invalid, or the multipart upload might have been aborted or completed."
   457  	length = int64(len(uploadContent))
   458  	_, err = obj.PutObject(context.Background(), "bucket", "object", mustGetPutObjReader(t, bytes.NewBufferString(uploadContent), length, "", ""), opts)
   459  	if err != nil {
   460  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
   461  	}
   462  
   463  	var bytesBuffer bytes.Buffer
   464  	err = GetObject(context.Background(), obj, "bucket", "object", 0, length, &bytesBuffer, "", opts)
   465  	if err != nil {
   466  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
   467  	}
   468  	if bytesBuffer.String() != "The specified multipart upload does not exist. The upload ID might be invalid, or the multipart upload might have been aborted or completed." {
   469  		t.Errorf("%s: Invalid upload ID error mismatch.", instanceType)
   470  	}
   471  }
   472  
   473  // Wrapper for calling testNonExistantBucketOperations for both Erasure and FS.
   474  func TestNonExistantBucketOperations(t *testing.T) {
   475  	ExecObjectLayerTest(t, testNonExistantBucketOperations)
   476  }
   477  
   478  // Tests validate that bucket operation on non-existent bucket fails.
   479  func testNonExistantBucketOperations(obj ObjectLayer, instanceType string, t TestErrHandler) {
   480  	var opts ObjectOptions
   481  	_, err := obj.PutObject(context.Background(), "bucket1", "object", mustGetPutObjReader(t, bytes.NewBufferString("one"), int64(len("one")), "", ""), opts)
   482  	if err == nil {
   483  		t.Fatal("Expected error but found nil")
   484  	}
   485  	if err.Error() != "Bucket not found: bucket1" {
   486  		t.Errorf("%s: Expected the error msg to be `%s`, but instead found `%s`", instanceType, "Bucket not found: bucket1", err.Error())
   487  	}
   488  }
   489  
   490  // Wrapper for calling testBucketRecreateFails for both Erasure and FS.
   491  func TestBucketRecreateFails(t *testing.T) {
   492  	ExecObjectLayerTest(t, testBucketRecreateFails)
   493  }
   494  
   495  // Tests validate that recreation of the bucket fails.
   496  func testBucketRecreateFails(obj ObjectLayer, instanceType string, t TestErrHandler) {
   497  	err := obj.MakeBucketWithLocation(context.Background(), "string", BucketOptions{})
   498  	if err != nil {
   499  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
   500  	}
   501  	err = obj.MakeBucketWithLocation(context.Background(), "string", BucketOptions{})
   502  	if err == nil {
   503  		t.Fatalf("%s: Expected error but found nil.", instanceType)
   504  	}
   505  
   506  	if err.Error() != "Bucket exists: string" {
   507  		t.Errorf("%s: Expected the error message to be `%s`, but instead found `%s`", instanceType, "Bucket exists: string", err.Error())
   508  	}
   509  }
   510  
   511  func enableCompression(t *testing.T, encrypt bool) {
   512  	// Enable compression and exec...
   513  	globalCompressConfigMu.Lock()
   514  	globalCompressConfig.Enabled = true
   515  	globalCompressConfig.MimeTypes = nil
   516  	globalCompressConfig.Extensions = nil
   517  	globalCompressConfig.AllowEncrypted = encrypt
   518  	globalCompressConfigMu.Unlock()
   519  	if encrypt {
   520  		globalAutoEncryption = encrypt
   521  		var err error
   522  		GlobalKMS, err = kms.Parse("my-minio-key:5lF+0pJM0OWwlQrvK2S/I7W9mO4a6rJJI7wzj7v09cw=")
   523  		if err != nil {
   524  			t.Fatal(err)
   525  		}
   526  	}
   527  }
   528  
   529  func enableEncrytion(t *testing.T) {
   530  	// Exec with default settings...
   531  	globalCompressConfigMu.Lock()
   532  	globalCompressConfig.Enabled = false
   533  	globalCompressConfigMu.Unlock()
   534  
   535  	globalAutoEncryption = true
   536  	var err error
   537  	GlobalKMS, err = kms.Parse("my-minio-key:5lF+0pJM0OWwlQrvK2S/I7W9mO4a6rJJI7wzj7v09cw=")
   538  	if err != nil {
   539  		t.Fatal(err)
   540  	}
   541  }
   542  
   543  func resetCompressEncryption() {
   544  	// Reset...
   545  	globalCompressConfigMu.Lock()
   546  	globalCompressConfig.Enabled = false
   547  	globalCompressConfig.AllowEncrypted = false
   548  	globalCompressConfigMu.Unlock()
   549  	globalAutoEncryption = false
   550  	GlobalKMS = nil
   551  }
   552  
   553  func execExtended(t *testing.T, fn func(t *testing.T)) {
   554  	// Exec with default settings...
   555  	resetCompressEncryption()
   556  	t.Run("default", func(t *testing.T) {
   557  		fn(t)
   558  	})
   559  
   560  	if testing.Short() {
   561  		return
   562  	}
   563  
   564  	t.Run("compressed", func(t *testing.T) {
   565  		resetCompressEncryption()
   566  		enableCompression(t, false)
   567  		fn(t)
   568  	})
   569  
   570  	t.Run("encrypted", func(t *testing.T) {
   571  		resetCompressEncryption()
   572  		enableEncrytion(t)
   573  		fn(t)
   574  	})
   575  
   576  	t.Run("compressed+encrypted", func(t *testing.T) {
   577  		resetCompressEncryption()
   578  		enableCompression(t, true)
   579  		fn(t)
   580  	})
   581  }
   582  
   583  // ExecExtendedObjectLayerTest will execute the tests with combinations of encrypted & compressed.
   584  // This can be used to test functionality when reading and writing data.
   585  func ExecExtendedObjectLayerTest(t *testing.T, objTest objTestType) {
   586  	execExtended(t, func(t *testing.T) {
   587  		ExecObjectLayerTest(t, objTest)
   588  	})
   589  }
   590  
   591  // Wrapper for calling testPutObject for both Erasure and FS.
   592  func TestPutObject(t *testing.T) {
   593  	ExecExtendedObjectLayerTest(t, testPutObject)
   594  }
   595  
   596  // Tests validate PutObject without prefix.
   597  func testPutObject(obj ObjectLayer, instanceType string, t TestErrHandler) {
   598  	content := []byte("testcontent")
   599  	length := int64(len(content))
   600  	readerEOF := newTestReaderEOF(content)
   601  	readerNoEOF := newTestReaderNoEOF(content)
   602  	err := obj.MakeBucketWithLocation(context.Background(), "bucket", BucketOptions{})
   603  	if err != nil {
   604  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
   605  	}
   606  
   607  	var bytesBuffer1 bytes.Buffer
   608  	var opts ObjectOptions
   609  	_, err = obj.PutObject(context.Background(), "bucket", "object", mustGetPutObjReader(t, readerEOF, length, "", ""), opts)
   610  	if err != nil {
   611  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
   612  	}
   613  	err = GetObject(context.Background(), obj, "bucket", "object", 0, length, &bytesBuffer1, "", opts)
   614  	if err != nil {
   615  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
   616  	}
   617  	if len(bytesBuffer1.Bytes()) != len(content) {
   618  		t.Errorf("%s: Expected content length to be `%d`, but instead found `%d`", instanceType, len(content), len(bytesBuffer1.Bytes()))
   619  	}
   620  
   621  	var bytesBuffer2 bytes.Buffer
   622  	_, err = obj.PutObject(context.Background(), "bucket", "object", mustGetPutObjReader(t, readerNoEOF, length, "", ""), opts)
   623  	if err != nil {
   624  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
   625  	}
   626  	err = GetObject(context.Background(), obj, "bucket", "object", 0, length, &bytesBuffer2, "", opts)
   627  	if err != nil {
   628  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
   629  	}
   630  	if len(bytesBuffer2.Bytes()) != len(content) {
   631  		t.Errorf("%s: Expected content length to be `%d`, but instead found `%d`", instanceType, len(content), len(bytesBuffer2.Bytes()))
   632  	}
   633  }
   634  
   635  // Wrapper for calling testPutObjectInSubdir for both Erasure and FS.
   636  func TestPutObjectInSubdir(t *testing.T) {
   637  	ExecExtendedObjectLayerTest(t, testPutObjectInSubdir)
   638  }
   639  
   640  // Tests validate PutObject with subdirectory prefix.
   641  func testPutObjectInSubdir(obj ObjectLayer, instanceType string, t TestErrHandler) {
   642  	err := obj.MakeBucketWithLocation(context.Background(), "bucket", BucketOptions{})
   643  	if err != nil {
   644  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
   645  	}
   646  
   647  	var opts ObjectOptions
   648  	uploadContent := `The specified multipart upload does not exist. The upload ID might be invalid, or the multipart
   649   upload might have been aborted or completed.`
   650  	length := int64(len(uploadContent))
   651  	_, err = obj.PutObject(context.Background(), "bucket", "dir1/dir2/object", mustGetPutObjReader(t, bytes.NewBufferString(uploadContent), length, "", ""), opts)
   652  	if err != nil {
   653  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
   654  	}
   655  
   656  	var bytesBuffer bytes.Buffer
   657  	err = GetObject(context.Background(), obj, "bucket", "dir1/dir2/object", 0, length, &bytesBuffer, "", opts)
   658  	if err != nil {
   659  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
   660  	}
   661  	if len(bytesBuffer.Bytes()) != len(uploadContent) {
   662  		t.Errorf("%s: Expected length of downloaded data to be `%d`, but instead found `%d`",
   663  			instanceType, len(uploadContent), len(bytesBuffer.Bytes()))
   664  	}
   665  }
   666  
   667  // Wrapper for calling testListBuckets for both Erasure and FS.
   668  func TestListBuckets(t *testing.T) {
   669  	ExecObjectLayerTest(t, testListBuckets)
   670  }
   671  
   672  // Tests validate ListBuckets.
   673  func testListBuckets(obj ObjectLayer, instanceType string, t TestErrHandler) {
   674  	// test empty list.
   675  	buckets, err := obj.ListBuckets(context.Background())
   676  	if err != nil {
   677  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
   678  	}
   679  	if len(buckets) != 0 {
   680  		t.Errorf("%s: Expected number of bucket to be `%d`, but instead found `%d`", instanceType, 0, len(buckets))
   681  	}
   682  
   683  	// add one and test exists.
   684  	err = obj.MakeBucketWithLocation(context.Background(), "bucket1", BucketOptions{})
   685  	if err != nil {
   686  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
   687  	}
   688  
   689  	buckets, err = obj.ListBuckets(context.Background())
   690  	if err != nil {
   691  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
   692  	}
   693  	if len(buckets) != 1 {
   694  		t.Errorf("%s: Expected number of bucket to be `%d`, but instead found `%d`", instanceType, 1, len(buckets))
   695  	}
   696  
   697  	// add two and test exists.
   698  	err = obj.MakeBucketWithLocation(context.Background(), "bucket2", BucketOptions{})
   699  	if err != nil {
   700  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
   701  	}
   702  
   703  	buckets, err = obj.ListBuckets(context.Background())
   704  	if err != nil {
   705  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
   706  	}
   707  	if len(buckets) != 2 {
   708  		t.Errorf("%s: Expected number of bucket to be `%d`, but instead found `%d`", instanceType, 2, len(buckets))
   709  	}
   710  
   711  	// add three and test exists + prefix.
   712  	err = obj.MakeBucketWithLocation(context.Background(), "bucket22", BucketOptions{})
   713  	if err != nil {
   714  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
   715  	}
   716  
   717  	buckets, err = obj.ListBuckets(context.Background())
   718  	if err != nil {
   719  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
   720  	}
   721  	if len(buckets) != 3 {
   722  		t.Errorf("%s: Expected number of bucket to be `%d`, but instead found `%d`", instanceType, 3, len(buckets))
   723  	}
   724  }
   725  
   726  // Wrapper for calling testListBucketsOrder for both Erasure and FS.
   727  func TestListBucketsOrder(t *testing.T) {
   728  	ExecObjectLayerTest(t, testListBucketsOrder)
   729  }
   730  
   731  // Tests validate the order of result of ListBuckets.
   732  func testListBucketsOrder(obj ObjectLayer, instanceType string, t TestErrHandler) {
   733  	// if implementation contains a map, order of map keys will vary.
   734  	// this ensures they return in the same order each time.
   735  	// add one and test exists.
   736  	err := obj.MakeBucketWithLocation(context.Background(), "bucket1", BucketOptions{})
   737  	if err != nil {
   738  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
   739  	}
   740  	err = obj.MakeBucketWithLocation(context.Background(), "bucket2", BucketOptions{})
   741  	if err != nil {
   742  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
   743  	}
   744  	buckets, err := obj.ListBuckets(context.Background())
   745  	if err != nil {
   746  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
   747  	}
   748  	if len(buckets) != 2 {
   749  		t.Errorf("%s: Expected number of bucket to be `%d`, but instead found `%d`", instanceType, 2, len(buckets))
   750  	}
   751  
   752  	if buckets[0].Name != "bucket1" {
   753  		t.Errorf("%s: Expected bucket name to be `%s`, but instead found `%s`", instanceType, "bucket1", buckets[0].Name)
   754  	}
   755  	if buckets[1].Name != "bucket2" {
   756  		t.Errorf("%s: Expected bucket name to be `%s`, but instead found `%s`", instanceType, "bucket2", buckets[1].Name)
   757  	}
   758  }
   759  
   760  // Wrapper for calling testListObjectsTestsForNonExistantBucket for both Erasure and FS.
   761  func TestListObjectsTestsForNonExistantBucket(t *testing.T) {
   762  	ExecObjectLayerTest(t, testListObjectsTestsForNonExistantBucket)
   763  }
   764  
   765  // Tests validate that ListObjects operation on a non-existent bucket fails as expected.
   766  func testListObjectsTestsForNonExistantBucket(obj ObjectLayer, instanceType string, t TestErrHandler) {
   767  	result, err := obj.ListObjects(context.Background(), "bucket", "", "", "", 1000)
   768  	if err == nil {
   769  		t.Fatalf("%s: Expected error but found nil.", instanceType)
   770  	}
   771  	if len(result.Objects) != 0 {
   772  		t.Fatalf("%s: Expected number of objects in the result to be `%d`, but instead found `%d`", instanceType, 0, len(result.Objects))
   773  	}
   774  	if result.IsTruncated {
   775  		t.Fatalf("%s: Expected IsTruncated to be `false`, but instead found it to be `%v`", instanceType, result.IsTruncated)
   776  	}
   777  	if err.Error() != "Bucket not found: bucket" {
   778  		t.Errorf("%s: Expected the error msg to be `%s`, but instead found `%s`", instanceType, "Bucket not found: bucket", err.Error())
   779  	}
   780  }
   781  
   782  // Wrapper for calling testNonExistantObjectInBucket for both Erasure and FS.
   783  func TestNonExistantObjectInBucket(t *testing.T) {
   784  	ExecObjectLayerTest(t, testNonExistantObjectInBucket)
   785  }
   786  
   787  // Tests validate that GetObject fails on a non-existent bucket as expected.
   788  func testNonExistantObjectInBucket(obj ObjectLayer, instanceType string, t TestErrHandler) {
   789  	err := obj.MakeBucketWithLocation(context.Background(), "bucket", BucketOptions{})
   790  	if err != nil {
   791  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
   792  	}
   793  
   794  	_, err = obj.GetObjectInfo(context.Background(), "bucket", "dir1", ObjectOptions{})
   795  	if err == nil {
   796  		t.Fatalf("%s: Expected error but found nil", instanceType)
   797  	}
   798  	if isErrObjectNotFound(err) {
   799  		if err.Error() != "Object not found: bucket/dir1" {
   800  			t.Errorf("%s: Expected the Error message to be `%s`, but instead found `%s`", instanceType, "Object not found: bucket/dir1", err.Error())
   801  		}
   802  	} else {
   803  		if err.Error() != "fails" {
   804  			t.Errorf("%s: Expected the Error message to be `%s`, but instead found it to be `%s`", instanceType, "fails", err.Error())
   805  		}
   806  	}
   807  }
   808  
   809  // Wrapper for calling testGetDirectoryReturnsObjectNotFound for both Erasure and FS.
   810  func TestGetDirectoryReturnsObjectNotFound(t *testing.T) {
   811  	ExecObjectLayerTest(t, testGetDirectoryReturnsObjectNotFound)
   812  }
   813  
   814  // Tests validate that GetObject on an existing directory fails as expected.
   815  func testGetDirectoryReturnsObjectNotFound(obj ObjectLayer, instanceType string, t TestErrHandler) {
   816  	bucketName := "bucket"
   817  	err := obj.MakeBucketWithLocation(context.Background(), bucketName, BucketOptions{})
   818  	if err != nil {
   819  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
   820  	}
   821  	content := "One or more of the specified parts could not be found. The part might not have been uploaded, or the specified entity tag might not have matched the part's entity tag."
   822  	length := int64(len(content))
   823  	var opts ObjectOptions
   824  	_, err = obj.PutObject(context.Background(), bucketName, "dir1/dir3/object", mustGetPutObjReader(t, bytes.NewBufferString(content), length, "", ""), opts)
   825  
   826  	if err != nil {
   827  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
   828  	}
   829  
   830  	testCases := []struct {
   831  		dir string
   832  		err error
   833  	}{
   834  		{
   835  			dir: "dir1/",
   836  			err: ObjectNotFound{Bucket: bucketName, Object: "dir1/"},
   837  		},
   838  		{
   839  			dir: "dir1/dir3/",
   840  			err: ObjectNotFound{Bucket: bucketName, Object: "dir1/dir3/"},
   841  		},
   842  	}
   843  
   844  	for i, testCase := range testCases {
   845  		_, expectedErr := obj.GetObjectInfo(context.Background(), bucketName, testCase.dir, opts)
   846  		if expectedErr != nil && expectedErr.Error() != testCase.err.Error() {
   847  			t.Errorf("Test %d, %s: Expected error %s, got %s", i+1, instanceType, testCase.err, expectedErr)
   848  		}
   849  	}
   850  }
   851  
   852  // Wrapper for calling testContentType for both Erasure and FS.
   853  func TestContentType(t *testing.T) {
   854  	ExecObjectLayerTest(t, testContentType)
   855  }
   856  
   857  // Test content-type.
   858  func testContentType(obj ObjectLayer, instanceType string, t TestErrHandler) {
   859  	err := obj.MakeBucketWithLocation(context.Background(), "bucket", BucketOptions{})
   860  	if err != nil {
   861  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
   862  	}
   863  	var opts ObjectOptions
   864  	uploadContent := "The specified multipart upload does not exist. The upload ID might be invalid, or the multipart upload might have been aborted or completed."
   865  	// Test empty.
   866  	_, err = obj.PutObject(context.Background(), "bucket", "minio.png", mustGetPutObjReader(t, bytes.NewBufferString(uploadContent), int64(len(uploadContent)), "", ""), opts)
   867  	if err != nil {
   868  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
   869  	}
   870  	objInfo, err := obj.GetObjectInfo(context.Background(), "bucket", "minio.png", opts)
   871  	if err != nil {
   872  		t.Fatalf("%s: <ERROR> %s", instanceType, err)
   873  	}
   874  
   875  	if objInfo.ContentType != "image/png" {
   876  		t.Errorf("%s: Expected Content type to be `%s`, but instead found `%s`", instanceType, "image/png", objInfo.ContentType)
   877  	}
   878  }