github.com/minio/minio-go/v6@v6.0.57/core_test.go (about)

     1  /*
     2   * MinIO Go Library for Amazon S3 Compatible Cloud Storage
     3   * Copyright 2017 MinIO, Inc.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  
    18  package minio
    19  
    20  import (
    21  	"bytes"
    22  	"io"
    23  	"log"
    24  	"net/http"
    25  	"os"
    26  	"strconv"
    27  	"testing"
    28  	"time"
    29  
    30  	"math/rand"
    31  )
    32  
    33  const (
    34  	serverEndpoint = "SERVER_ENDPOINT"
    35  	accessKey      = "ACCESS_KEY"
    36  	secretKey      = "SECRET_KEY"
    37  	enableSecurity = "ENABLE_HTTPS"
    38  )
    39  
    40  const letterBytes = "abcdefghijklmnopqrstuvwxyz01234569"
    41  const (
    42  	letterIdxBits = 6                    // 6 bits to represent a letter index
    43  	letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
    44  	letterIdxMax  = 63 / letterIdxBits   // # of letter indices fitting in 63 bits
    45  )
    46  
    47  // randString generates random names and prepends them with a known prefix.
    48  func randString(n int, src rand.Source, prefix string) string {
    49  	b := make([]byte, n)
    50  	// A rand.Int63() generates 63 random bits, enough for letterIdxMax letters!
    51  	for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
    52  		if remain == 0 {
    53  			cache, remain = src.Int63(), letterIdxMax
    54  		}
    55  		if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
    56  			b[i] = letterBytes[idx]
    57  			i--
    58  		}
    59  		cache >>= letterIdxBits
    60  		remain--
    61  	}
    62  	return prefix + string(b[0:30-len(prefix)])
    63  }
    64  
    65  // Tests for Core GetObject() function.
    66  func TestGetObjectCore(t *testing.T) {
    67  	if testing.Short() {
    68  		t.Skip("skipping functional tests for the short runs")
    69  	}
    70  
    71  	// Seed random based on current time.
    72  	rand.Seed(time.Now().Unix())
    73  
    74  	// Instantiate new minio core client object.
    75  	c, err := NewCore(
    76  		os.Getenv(serverEndpoint),
    77  		os.Getenv(accessKey),
    78  		os.Getenv(secretKey),
    79  		mustParseBool(os.Getenv(enableSecurity)),
    80  	)
    81  	if err != nil {
    82  		t.Fatal("Error:", err)
    83  	}
    84  
    85  	// Enable tracing, write to stderr.
    86  	// c.TraceOn(os.Stderr)
    87  
    88  	// Set user agent.
    89  	c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
    90  
    91  	// Generate a new random bucket name.
    92  	bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
    93  
    94  	// Make a new bucket.
    95  	err = c.MakeBucket(bucketName, "us-east-1")
    96  	if err != nil {
    97  		t.Fatal("Error:", err, bucketName)
    98  	}
    99  
   100  	// Generate data more than 32K
   101  	buf := bytes.Repeat([]byte("3"), rand.Intn(1<<20)+32*1024)
   102  
   103  	// Save the data
   104  	objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
   105  	n, err := c.Client.PutObject(bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), PutObjectOptions{
   106  		ContentType: "binary/octet-stream",
   107  	})
   108  	if err != nil {
   109  		t.Fatal("Error:", err, bucketName, objectName)
   110  	}
   111  
   112  	if n != int64(len(buf)) {
   113  		t.Fatalf("Error: number of bytes does not match, want %v, got %v\n", len(buf), n)
   114  	}
   115  
   116  	offset := int64(2048)
   117  
   118  	// read directly
   119  	buf1 := make([]byte, 512)
   120  	buf2 := make([]byte, 512)
   121  	buf3 := make([]byte, n)
   122  	buf4 := make([]byte, 1)
   123  
   124  	opts := GetObjectOptions{}
   125  	opts.SetRange(offset, offset+int64(len(buf1))-1)
   126  	reader, objectInfo, _, err := c.GetObject(bucketName, objectName, opts)
   127  	if err != nil {
   128  		t.Fatal(err)
   129  	}
   130  	m, err := io.ReadFull(reader, buf1)
   131  	reader.Close()
   132  	if err != nil {
   133  		t.Fatal(err)
   134  	}
   135  
   136  	if objectInfo.Size != int64(m) {
   137  		t.Fatalf("Error: GetObject read shorter bytes before reaching EOF, want %v, got %v\n", objectInfo.Size, m)
   138  	}
   139  	if !bytes.Equal(buf1, buf[offset:offset+512]) {
   140  		t.Fatal("Error: Incorrect read between two GetObject from same offset.")
   141  	}
   142  	offset += 512
   143  
   144  	opts.SetRange(offset, offset+int64(len(buf2))-1)
   145  	reader, objectInfo, _, err = c.GetObject(bucketName, objectName, opts)
   146  	if err != nil {
   147  		t.Fatal(err)
   148  	}
   149  
   150  	m, err = io.ReadFull(reader, buf2)
   151  	reader.Close()
   152  	if err != nil {
   153  		t.Fatal(err)
   154  	}
   155  
   156  	if objectInfo.Size != int64(m) {
   157  		t.Fatalf("Error: GetObject read shorter bytes before reaching EOF, want %v, got %v\n", objectInfo.Size, m)
   158  	}
   159  	if !bytes.Equal(buf2, buf[offset:offset+512]) {
   160  		t.Fatal("Error: Incorrect read between two GetObject from same offset.")
   161  	}
   162  
   163  	opts.SetRange(0, int64(len(buf3)))
   164  	reader, objectInfo, _, err = c.GetObject(bucketName, objectName, opts)
   165  	if err != nil {
   166  		t.Fatal(err)
   167  	}
   168  
   169  	m, err = io.ReadFull(reader, buf3)
   170  	if err != nil {
   171  		reader.Close()
   172  		t.Fatal(err)
   173  	}
   174  	reader.Close()
   175  
   176  	if objectInfo.Size != int64(m) {
   177  		t.Fatalf("Error: GetObject read shorter bytes before reaching EOF, want %v, got %v\n", objectInfo.Size, m)
   178  	}
   179  	if !bytes.Equal(buf3, buf) {
   180  		t.Fatal("Error: Incorrect data read in GetObject, than what was previously upoaded.")
   181  	}
   182  
   183  	opts = GetObjectOptions{}
   184  	opts.SetMatchETag("etag")
   185  	_, _, _, err = c.GetObject(bucketName, objectName, opts)
   186  	if err == nil {
   187  		t.Fatal("Unexpected GetObject should fail with mismatching etags")
   188  	}
   189  	if errResp := ToErrorResponse(err); errResp.Code != "PreconditionFailed" {
   190  		t.Fatalf("Expected \"PreconditionFailed\" as code, got %s instead", errResp.Code)
   191  	}
   192  
   193  	opts = GetObjectOptions{}
   194  	opts.SetMatchETagExcept("etag")
   195  	reader, objectInfo, _, err = c.GetObject(bucketName, objectName, opts)
   196  	if err != nil {
   197  		t.Fatal(err)
   198  	}
   199  
   200  	m, err = io.ReadFull(reader, buf3)
   201  	reader.Close()
   202  	if err != nil {
   203  		t.Fatal(err)
   204  	}
   205  
   206  	if objectInfo.Size != int64(m) {
   207  		t.Fatalf("Error: GetObject read shorter bytes before reaching EOF, want %v, got %v\n", objectInfo.Size, m)
   208  	}
   209  	if !bytes.Equal(buf3, buf) {
   210  		t.Fatal("Error: Incorrect data read in GetObject, than what was previously upoaded.")
   211  	}
   212  
   213  	opts = GetObjectOptions{}
   214  	opts.SetRange(0, 0)
   215  	reader, objectInfo, _, err = c.GetObject(bucketName, objectName, opts)
   216  	if err != nil {
   217  		t.Fatal(err)
   218  	}
   219  
   220  	m, err = io.ReadFull(reader, buf4)
   221  	reader.Close()
   222  	if err != nil {
   223  		t.Fatal(err)
   224  	}
   225  
   226  	if objectInfo.Size != int64(m) {
   227  		t.Fatalf("Error: GetObject read shorter bytes before reaching EOF, want %v, got %v\n", objectInfo.Size, m)
   228  	}
   229  
   230  	opts = GetObjectOptions{}
   231  	opts.SetRange(offset, offset+int64(len(buf2))-1)
   232  	contentLength := len(buf2)
   233  	var header http.Header
   234  	_, _, header, err = c.GetObject(bucketName, objectName, opts)
   235  	if err != nil {
   236  		t.Fatal(err)
   237  	}
   238  
   239  	contentLengthValue, err := strconv.Atoi(header.Get("Content-Length"))
   240  	if err != nil {
   241  		t.Fatal("Error: ", err)
   242  	}
   243  	if contentLength != contentLengthValue {
   244  		t.Fatalf("Error: Content Length in response header %v, not equal to set content length %v\n", contentLengthValue, contentLength)
   245  	}
   246  
   247  	err = c.RemoveObject(bucketName, objectName)
   248  	if err != nil {
   249  		t.Fatal("Error: ", err)
   250  	}
   251  	err = c.RemoveBucket(bucketName)
   252  	if err != nil {
   253  		t.Fatal("Error:", err)
   254  	}
   255  }
   256  
   257  // Tests GetObject to return Content-Encoding properly set
   258  // and overrides any auto decoding.
   259  func TestGetObjectContentEncoding(t *testing.T) {
   260  	if testing.Short() {
   261  		t.Skip("skipping functional tests for the short runs")
   262  	}
   263  
   264  	// Seed random based on current time.
   265  	rand.Seed(time.Now().Unix())
   266  
   267  	// Instantiate new minio core client object.
   268  	c, err := NewCore(
   269  		os.Getenv(serverEndpoint),
   270  		os.Getenv(accessKey),
   271  		os.Getenv(secretKey),
   272  		mustParseBool(os.Getenv(enableSecurity)),
   273  	)
   274  	if err != nil {
   275  		t.Fatal("Error:", err)
   276  	}
   277  
   278  	// Enable tracing, write to stderr.
   279  	// c.TraceOn(os.Stderr)
   280  
   281  	// Set user agent.
   282  	c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
   283  
   284  	// Generate a new random bucket name.
   285  	bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
   286  
   287  	// Make a new bucket.
   288  	err = c.MakeBucket(bucketName, "us-east-1")
   289  	if err != nil {
   290  		t.Fatal("Error:", err, bucketName)
   291  	}
   292  
   293  	// Generate data more than 32K
   294  	buf := bytes.Repeat([]byte("3"), rand.Intn(1<<20)+32*1024)
   295  
   296  	// Save the data
   297  	objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
   298  	n, err := c.Client.PutObject(bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), PutObjectOptions{
   299  		ContentEncoding: "gzip",
   300  	})
   301  	if err != nil {
   302  		t.Fatal("Error:", err, bucketName, objectName)
   303  	}
   304  
   305  	if n != int64(len(buf)) {
   306  		t.Fatalf("Error: number of bytes does not match, want %v, got %v\n", len(buf), n)
   307  	}
   308  
   309  	rwc, objInfo, _, err := c.GetObject(bucketName, objectName, GetObjectOptions{})
   310  	if err != nil {
   311  		t.Fatalf("Error: %v", err)
   312  	}
   313  	rwc.Close()
   314  	if objInfo.Size <= 0 {
   315  		t.Fatalf("Unexpected size of the object %v, expected %v", objInfo.Size, n)
   316  	}
   317  	value, ok := objInfo.Metadata["Content-Encoding"]
   318  	if !ok {
   319  		t.Fatalf("Expected Content-Encoding metadata to be set.")
   320  	}
   321  	if value[0] != "gzip" {
   322  		t.Fatalf("Unexpected content-encoding found, want gzip, got %v", value)
   323  	}
   324  
   325  	err = c.RemoveObject(bucketName, objectName)
   326  	if err != nil {
   327  		t.Fatal("Error: ", err)
   328  	}
   329  	err = c.RemoveBucket(bucketName)
   330  	if err != nil {
   331  		t.Fatal("Error:", err)
   332  	}
   333  }
   334  
   335  // Tests get bucket policy core API.
   336  func TestGetBucketPolicy(t *testing.T) {
   337  	if testing.Short() {
   338  		t.Skip("skipping functional tests for short runs")
   339  	}
   340  
   341  	// Seed random based on current time.
   342  	rand.Seed(time.Now().Unix())
   343  
   344  	// Instantiate new minio client object.
   345  	c, err := NewCore(
   346  		os.Getenv(serverEndpoint),
   347  		os.Getenv(accessKey),
   348  		os.Getenv(secretKey),
   349  		mustParseBool(os.Getenv(enableSecurity)),
   350  	)
   351  	if err != nil {
   352  		t.Fatal("Error:", err)
   353  	}
   354  
   355  	// Enable to debug
   356  	// c.TraceOn(os.Stderr)
   357  
   358  	// Set user agent.
   359  	c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
   360  
   361  	// Generate a new random bucket name.
   362  	bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
   363  
   364  	// Make a new bucket.
   365  	err = c.MakeBucket(bucketName, "us-east-1")
   366  	if err != nil {
   367  		t.Fatal("Error:", err, bucketName)
   368  	}
   369  
   370  	// Verify if bucket exits and you have access.
   371  	var exists bool
   372  	exists, err = c.BucketExists(bucketName)
   373  	if err != nil {
   374  		t.Fatal("Error:", err, bucketName)
   375  	}
   376  	if !exists {
   377  		t.Fatal("Error: could not find ", bucketName)
   378  	}
   379  
   380  	// Asserting the default bucket policy.
   381  	bucketPolicy, err := c.GetBucketPolicy(bucketName)
   382  	if err != nil {
   383  		errResp := ToErrorResponse(err)
   384  		if errResp.Code != "NoSuchBucketPolicy" {
   385  			t.Error("Error:", err, bucketName)
   386  		}
   387  	}
   388  	if bucketPolicy != "" {
   389  		t.Errorf("Bucket policy expected %#v, got %#v", "", bucketPolicy)
   390  	}
   391  
   392  	err = c.RemoveBucket(bucketName)
   393  	if err != nil {
   394  		t.Fatal("Error:", err)
   395  	}
   396  }
   397  
   398  // Tests Core CopyObject API implementation.
   399  func TestCoreCopyObject(t *testing.T) {
   400  	if testing.Short() {
   401  		t.Skip("skipping functional tests for short runs")
   402  	}
   403  
   404  	// Seed random based on current time.
   405  	rand.Seed(time.Now().Unix())
   406  
   407  	// Instantiate new minio client object.
   408  	c, err := NewCore(
   409  		os.Getenv(serverEndpoint),
   410  		os.Getenv(accessKey),
   411  		os.Getenv(secretKey),
   412  		mustParseBool(os.Getenv(enableSecurity)),
   413  	)
   414  	if err != nil {
   415  		t.Fatal("Error:", err)
   416  	}
   417  
   418  	// Enable tracing, write to stderr.
   419  	// c.TraceOn(os.Stderr)
   420  
   421  	// Set user agent.
   422  	c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
   423  
   424  	// Generate a new random bucket name.
   425  	bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
   426  
   427  	// Make a new bucket.
   428  	err = c.MakeBucket(bucketName, "us-east-1")
   429  	if err != nil {
   430  		t.Fatal("Error:", err, bucketName)
   431  	}
   432  
   433  	buf := bytes.Repeat([]byte("a"), 32*1024)
   434  
   435  	// Save the data
   436  	objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
   437  
   438  	putopts := PutObjectOptions{
   439  		UserMetadata: map[string]string{
   440  			"Content-Type": "binary/octet-stream",
   441  		},
   442  	}
   443  	objInfo, err := c.PutObject(bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), "", "", putopts)
   444  	if err != nil {
   445  		t.Fatal("Error:", err, bucketName, objectName)
   446  	}
   447  
   448  	if objInfo.Size != int64(len(buf)) {
   449  		t.Fatalf("Error: number of bytes does not match, want %v, got %v\n", len(buf), objInfo.Size)
   450  	}
   451  
   452  	destBucketName := bucketName
   453  	destObjectName := objectName + "-dest"
   454  
   455  	cobjInfo, err := c.CopyObject(bucketName, objectName, destBucketName, destObjectName, map[string]string{
   456  		"X-Amz-Metadata-Directive": "REPLACE",
   457  		"Content-Type":             "application/javascript",
   458  	})
   459  	if err != nil {
   460  		t.Fatal("Error:", err, bucketName, objectName, destBucketName, destObjectName)
   461  	}
   462  	if cobjInfo.ETag != objInfo.ETag {
   463  		t.Fatalf("Error: expected etag to be same as source object %s, but found different etag %s", objInfo.ETag, cobjInfo.ETag)
   464  	}
   465  
   466  	// Attempt to read from destBucketName and object name.
   467  	r, err := c.Client.GetObject(destBucketName, destObjectName, GetObjectOptions{})
   468  	if err != nil {
   469  		t.Fatal("Error:", err, bucketName, objectName)
   470  	}
   471  
   472  	st, err := r.Stat()
   473  	if err != nil {
   474  		t.Fatal("Error:", err, bucketName, objectName)
   475  	}
   476  
   477  	if st.Size != int64(len(buf)) {
   478  		t.Fatalf("Error: number of bytes in stat does not match, want %v, got %v\n",
   479  			len(buf), st.Size)
   480  	}
   481  
   482  	if st.ContentType != "application/javascript" {
   483  		t.Fatalf("Error: Content types don't match, expected: application/javascript, found: %+v\n", st.ContentType)
   484  	}
   485  
   486  	if st.ETag != objInfo.ETag {
   487  		t.Fatalf("Error: expected etag to be same as source object %s, but found different etag :%s", objInfo.ETag, st.ETag)
   488  	}
   489  
   490  	if err := r.Close(); err != nil {
   491  		t.Fatal("Error:", err)
   492  	}
   493  
   494  	if err := r.Close(); err == nil {
   495  		t.Fatal("Error: object is already closed, should return error")
   496  	}
   497  
   498  	err = c.RemoveObject(bucketName, objectName)
   499  	if err != nil {
   500  		t.Fatal("Error: ", err)
   501  	}
   502  
   503  	err = c.RemoveObject(destBucketName, destObjectName)
   504  	if err != nil {
   505  		t.Fatal("Error: ", err)
   506  	}
   507  
   508  	err = c.RemoveBucket(bucketName)
   509  	if err != nil {
   510  		t.Fatal("Error:", err)
   511  	}
   512  
   513  	// Do not need to remove destBucketName its same as bucketName.
   514  }
   515  
   516  // Test Core CopyObjectPart implementation
   517  func TestCoreCopyObjectPart(t *testing.T) {
   518  	if testing.Short() {
   519  		t.Skip("skipping functional tests for short runs")
   520  	}
   521  
   522  	// Seed random based on current time.
   523  	rand.Seed(time.Now().Unix())
   524  
   525  	// Instantiate new minio client object.
   526  	c, err := NewCore(
   527  		os.Getenv(serverEndpoint),
   528  		os.Getenv(accessKey),
   529  		os.Getenv(secretKey),
   530  		mustParseBool(os.Getenv(enableSecurity)),
   531  	)
   532  	if err != nil {
   533  		t.Fatal("Error:", err)
   534  	}
   535  
   536  	// Enable tracing, write to stderr.
   537  	// c.TraceOn(os.Stderr)
   538  
   539  	// Set user agent.
   540  	c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
   541  
   542  	// Generate a new random bucket name.
   543  	bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
   544  
   545  	// Make a new bucket.
   546  	err = c.MakeBucket(bucketName, "us-east-1")
   547  	if err != nil {
   548  		t.Fatal("Error:", err, bucketName)
   549  	}
   550  
   551  	// Make a buffer with 5MB of data
   552  	buf := bytes.Repeat([]byte("abcde"), 1024*1024)
   553  	metadata := map[string]string{
   554  		"Content-Type": "binary/octet-stream",
   555  	}
   556  	putopts := PutObjectOptions{
   557  		UserMetadata: metadata,
   558  	}
   559  	// Save the data
   560  	objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
   561  	objInfo, err := c.PutObject(bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), "", "", putopts)
   562  	if err != nil {
   563  		t.Fatal("Error:", err, bucketName, objectName)
   564  	}
   565  
   566  	if objInfo.Size != int64(len(buf)) {
   567  		t.Fatalf("Error: number of bytes does not match, want %v, got %v\n", len(buf), objInfo.Size)
   568  	}
   569  
   570  	destBucketName := bucketName
   571  	destObjectName := objectName + "-dest"
   572  
   573  	uploadID, err := c.NewMultipartUpload(destBucketName, destObjectName, PutObjectOptions{})
   574  	if err != nil {
   575  		t.Fatal("Error:", err, bucketName, objectName)
   576  	}
   577  
   578  	// Content of the destination object will be two copies of
   579  	// `objectName` concatenated, followed by first byte of
   580  	// `objectName`.
   581  
   582  	// First of three parts
   583  	fstPart, err := c.CopyObjectPart(bucketName, objectName, destBucketName, destObjectName, uploadID, 1, 0, -1, nil)
   584  	if err != nil {
   585  		t.Fatal("Error:", err, destBucketName, destObjectName)
   586  	}
   587  
   588  	// Second of three parts
   589  	sndPart, err := c.CopyObjectPart(bucketName, objectName, destBucketName, destObjectName, uploadID, 2, 0, -1, nil)
   590  	if err != nil {
   591  		t.Fatal("Error:", err, destBucketName, destObjectName)
   592  	}
   593  
   594  	// Last of three parts
   595  	lstPart, err := c.CopyObjectPart(bucketName, objectName, destBucketName, destObjectName, uploadID, 3, 0, 1, nil)
   596  	if err != nil {
   597  		t.Fatal("Error:", err, destBucketName, destObjectName)
   598  	}
   599  
   600  	// Complete the multipart upload
   601  	_, err = c.CompleteMultipartUpload(destBucketName, destObjectName, uploadID, []CompletePart{fstPart, sndPart, lstPart})
   602  	if err != nil {
   603  		t.Fatal("Error:", err, destBucketName, destObjectName)
   604  	}
   605  
   606  	// Stat the object and check its length matches
   607  	objInfo, err = c.StatObject(destBucketName, destObjectName, StatObjectOptions{})
   608  	if err != nil {
   609  		t.Fatal("Error:", err, destBucketName, destObjectName)
   610  	}
   611  
   612  	if objInfo.Size != (5*1024*1024)*2+1 {
   613  		t.Fatal("Destination object has incorrect size!")
   614  	}
   615  
   616  	// Now we read the data back
   617  	getOpts := GetObjectOptions{}
   618  	getOpts.SetRange(0, 5*1024*1024-1)
   619  	r, _, _, err := c.GetObject(destBucketName, destObjectName, getOpts)
   620  	if err != nil {
   621  		t.Fatal("Error:", err, destBucketName, destObjectName)
   622  	}
   623  	getBuf := make([]byte, 5*1024*1024)
   624  	_, err = io.ReadFull(r, getBuf)
   625  	if err != nil {
   626  		t.Fatal("Error:", err, destBucketName, destObjectName)
   627  	}
   628  	if !bytes.Equal(getBuf, buf) {
   629  		t.Fatal("Got unexpected data in first 5MB")
   630  	}
   631  
   632  	getOpts.SetRange(5*1024*1024, 0)
   633  	r, _, _, err = c.GetObject(destBucketName, destObjectName, getOpts)
   634  	if err != nil {
   635  		t.Fatal("Error:", err, destBucketName, destObjectName)
   636  	}
   637  	getBuf = make([]byte, 5*1024*1024+1)
   638  	_, err = io.ReadFull(r, getBuf)
   639  	if err != nil {
   640  		t.Fatal("Error:", err, destBucketName, destObjectName)
   641  	}
   642  	if !bytes.Equal(getBuf[:5*1024*1024], buf) {
   643  		t.Fatal("Got unexpected data in second 5MB")
   644  	}
   645  	if getBuf[5*1024*1024] != buf[0] {
   646  		t.Fatal("Got unexpected data in last byte of copied object!")
   647  	}
   648  
   649  	if err := c.RemoveObject(destBucketName, destObjectName); err != nil {
   650  		t.Fatal("Error: ", err)
   651  	}
   652  
   653  	if err := c.RemoveObject(bucketName, objectName); err != nil {
   654  		t.Fatal("Error: ", err)
   655  	}
   656  
   657  	if err := c.RemoveBucket(bucketName); err != nil {
   658  		t.Fatal("Error: ", err)
   659  	}
   660  
   661  	// Do not need to remove destBucketName its same as bucketName.
   662  }
   663  
   664  // Test Core PutObject.
   665  func TestCorePutObject(t *testing.T) {
   666  	if testing.Short() {
   667  		t.Skip("skipping functional tests for short runs")
   668  	}
   669  
   670  	// Seed random based on current time.
   671  	rand.Seed(time.Now().Unix())
   672  
   673  	// Instantiate new minio client object.
   674  	c, err := NewCore(
   675  		os.Getenv(serverEndpoint),
   676  		os.Getenv(accessKey),
   677  		os.Getenv(secretKey),
   678  		mustParseBool(os.Getenv(enableSecurity)),
   679  	)
   680  	if err != nil {
   681  		t.Error("Error:", err)
   682  		return
   683  	}
   684  
   685  	// Enable tracing, write to stderr.
   686  	// c.TraceOn(os.Stderr)
   687  
   688  	// Set user agent.
   689  	c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
   690  
   691  	// Generate a new random bucket name.
   692  	bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
   693  
   694  	// Make a new bucket.
   695  	err = c.MakeBucket(bucketName, "us-east-1")
   696  	if err != nil {
   697  		t.Fatal("Error:", err, bucketName)
   698  	}
   699  
   700  	buf := bytes.Repeat([]byte("a"), 32*1024)
   701  
   702  	// Save the data
   703  	objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
   704  	// Object content type
   705  	objectContentType := "binary/octet-stream"
   706  	metadata := make(map[string]string)
   707  	metadata["Content-Type"] = objectContentType
   708  	putopts := PutObjectOptions{
   709  		UserMetadata: metadata,
   710  	}
   711  	objInfo, err := c.PutObject(bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), "1B2M2Y8AsgTpgAmY7PhCfg==", "", putopts)
   712  	if err == nil {
   713  		t.Fatal("Error expected: error, got: nil(success)")
   714  	}
   715  
   716  	objInfo, err = c.PutObject(bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), "", "", putopts)
   717  	if err != nil {
   718  		t.Fatal("Error:", err, bucketName, objectName)
   719  	}
   720  
   721  	if objInfo.Size != int64(len(buf)) {
   722  		t.Fatalf("Error: number of bytes does not match, want %v, got %v\n", len(buf), objInfo.Size)
   723  	}
   724  
   725  	// Read the data back
   726  	r, err := c.Client.GetObject(bucketName, objectName, GetObjectOptions{})
   727  	if err != nil {
   728  		t.Fatal("Error:", err, bucketName, objectName)
   729  	}
   730  
   731  	st, err := r.Stat()
   732  	if err != nil {
   733  		t.Fatal("Error:", err, bucketName, objectName)
   734  	}
   735  
   736  	if st.Size != int64(len(buf)) {
   737  		t.Fatalf("Error: number of bytes in stat does not match, want %v, got %v\n",
   738  			len(buf), st.Size)
   739  	}
   740  
   741  	if st.ContentType != objectContentType {
   742  		t.Fatalf("Error: Content types don't match, expected: %+v, found: %+v\n", objectContentType, st.ContentType)
   743  	}
   744  
   745  	if err := r.Close(); err != nil {
   746  		t.Fatal("Error:", err)
   747  	}
   748  
   749  	if err := r.Close(); err == nil {
   750  		t.Fatal("Error: object is already closed, should return error")
   751  	}
   752  
   753  	err = c.RemoveObject(bucketName, objectName)
   754  	if err != nil {
   755  		t.Fatal("Error: ", err)
   756  	}
   757  
   758  	err = c.RemoveBucket(bucketName)
   759  	if err != nil {
   760  		t.Fatal("Error:", err)
   761  	}
   762  }
   763  
   764  func TestCoreGetObjectMetadata(t *testing.T) {
   765  	if testing.Short() {
   766  		t.Skip("skipping functional tests for the short runs")
   767  	}
   768  
   769  	core, err := NewCore(
   770  		os.Getenv(serverEndpoint),
   771  		os.Getenv(accessKey),
   772  		os.Getenv(secretKey),
   773  		mustParseBool(os.Getenv(enableSecurity)))
   774  	if err != nil {
   775  		log.Fatalln(err)
   776  	}
   777  
   778  	// Generate a new random bucket name.
   779  	bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
   780  
   781  	// Make a new bucket.
   782  	err = core.MakeBucket(bucketName, "us-east-1")
   783  	if err != nil {
   784  		t.Fatal("Error:", err, bucketName)
   785  	}
   786  
   787  	metadata := map[string]string{
   788  		"X-Amz-Meta-Key-1": "Val-1",
   789  	}
   790  	putopts := PutObjectOptions{
   791  		UserMetadata: metadata,
   792  	}
   793  
   794  	_, err = core.PutObject(bucketName, "my-objectname",
   795  		bytes.NewReader([]byte("hello")), 5, "", "", putopts)
   796  	if err != nil {
   797  		log.Fatalln(err)
   798  	}
   799  
   800  	reader, objInfo, _, err := core.GetObject(bucketName, "my-objectname", GetObjectOptions{})
   801  	if err != nil {
   802  		log.Fatalln(err)
   803  	}
   804  	defer reader.Close()
   805  
   806  	if objInfo.Metadata.Get("X-Amz-Meta-Key-1") != "Val-1" {
   807  		log.Fatalln("Expected metadata to be available but wasn't")
   808  	}
   809  
   810  	err = core.RemoveObject(bucketName, "my-objectname")
   811  	if err != nil {
   812  		t.Fatal("Error: ", err)
   813  	}
   814  	err = core.RemoveBucket(bucketName)
   815  	if err != nil {
   816  		t.Fatal("Error:", err)
   817  	}
   818  }