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 }