github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/object_api_suite_test.go (about)

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