github.com/qiniu/go-sdk/v7@v7.27.0/storagev2/uploader/multi_parts_uploader_test.go (about) 1 //go:build unit 2 // +build unit 3 4 package uploader 5 6 import ( 7 "bytes" 8 "context" 9 "encoding/base64" 10 "encoding/json" 11 "fmt" 12 "hash/crc32" 13 "io" 14 "math/rand" 15 "net/http" 16 "net/http/httptest" 17 "os" 18 "strconv" 19 "strings" 20 "sync/atomic" 21 "testing" 22 "time" 23 24 "github.com/gorilla/mux" 25 internal_io "github.com/qiniu/go-sdk/v7/internal/io" 26 "github.com/qiniu/go-sdk/v7/storagev2/apis" 27 "github.com/qiniu/go-sdk/v7/storagev2/credentials" 28 "github.com/qiniu/go-sdk/v7/storagev2/http_client" 29 "github.com/qiniu/go-sdk/v7/storagev2/region" 30 resumablerecorder "github.com/qiniu/go-sdk/v7/storagev2/uploader/resumable_recorder" 31 ) 32 33 func TestMultiPartsUploader(t *testing.T) { 34 tmpFile, err := os.CreateTemp("", "multi-parts-uploader-test-*") 35 if err != nil { 36 t.Fatal(err) 37 } 38 defer os.Remove(tmpFile.Name()) 39 defer tmpFile.Close() 40 41 r := rand.New(rand.NewSource(time.Now().UnixNano())) 42 if _, err = io.CopyN(tmpFile, r, 5*1024*1024); err != nil { 43 t.Fatal(err) 44 } 45 if _, err = tmpFile.Seek(0, io.SeekStart); err != nil { 46 t.Fatal(err) 47 } 48 49 var server *httptest.Server 50 serveMux := mux.NewRouter() 51 serveMux.HandleFunc("/mkblk/4194304", func(w http.ResponseWriter, r *http.Request) { 52 if !strings.HasPrefix(r.Header.Get("Authorization"), "UpToken testak:") { 53 t.Fatalf("unexpected authorization") 54 } 55 actualBody, err := internal_io.ReadAll(r.Body) 56 if err != nil { 57 t.Fatal(err) 58 } 59 expectedBody, err := internal_io.ReadAll(io.NewSectionReader(tmpFile, 0, 4*1024*1024)) 60 if err != nil { 61 t.Fatal(err) 62 } 63 if !bytes.Equal(actualBody, expectedBody) { 64 t.Fatalf("unexpected body") 65 } 66 jsonBody, err := json.Marshal(&apis.ResumableUploadV1MakeBlockResponse{ 67 Ctx: "testctx1", 68 Checksum: "testchecksum1", 69 Crc32: int64(crc32.ChecksumIEEE(actualBody)), 70 Host: server.URL, 71 ExpiredAt: time.Now().Add(1 * time.Hour).Unix(), 72 }) 73 if err != nil { 74 t.Fatal(err) 75 } 76 w.Header().Add("X-ReqId", "fakereqid") 77 w.Write(jsonBody) 78 }).Methods(http.MethodPost) 79 serveMux.HandleFunc("/mkblk/1048576", func(w http.ResponseWriter, r *http.Request) { 80 if !strings.HasPrefix(r.Header.Get("Authorization"), "UpToken testak:") { 81 t.Fatalf("unexpected authorization") 82 } 83 actualBody, err := internal_io.ReadAll(r.Body) 84 if err != nil { 85 t.Fatal(err) 86 } 87 expectedBody, err := internal_io.ReadAll(io.NewSectionReader(tmpFile, 4*1024*1024, 1*1024*1024)) 88 if err != nil { 89 t.Fatal(err) 90 } 91 if !bytes.Equal(actualBody, expectedBody) { 92 t.Fatalf("unexpected body") 93 } 94 jsonBody, err := json.Marshal(&apis.ResumableUploadV1MakeBlockResponse{ 95 Ctx: "testctx2", 96 Checksum: "testchecksum2", 97 Crc32: int64(crc32.ChecksumIEEE(actualBody)), 98 Host: server.URL, 99 ExpiredAt: time.Now().Add(1 * time.Hour).Unix(), 100 }) 101 if err != nil { 102 t.Fatal(err) 103 } 104 w.Header().Add("X-ReqId", "fakereqid") 105 w.Write(jsonBody) 106 }).Methods(http.MethodPost) 107 serveMux.PathPrefix("/mkfile/").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 108 if !strings.HasPrefix(r.Header.Get("Authorization"), "UpToken testak:") { 109 t.Fatalf("unexpected authorization") 110 } 111 components := strings.Split(strings.TrimPrefix(r.URL.Path, "/mkfile/"), "/") 112 if components[0] != strconv.FormatInt(5*1024*1024, 10) { 113 t.Fatalf("unexpected fileSize") 114 } 115 components = components[1:] 116 for len(components) > 0 { 117 switch components[0] { 118 case "key": 119 value := components[1] 120 valueBytes, err := base64.URLEncoding.DecodeString(value) 121 if err != nil { 122 t.Fatal(err) 123 } 124 if string(valueBytes) != "testkey" { 125 t.Fatalf("unexpected key") 126 } 127 case "fname": 128 value := components[1] 129 valueBytes, err := base64.URLEncoding.DecodeString(value) 130 if err != nil { 131 t.Fatal(err) 132 } 133 if string(valueBytes) != "testfilename" { 134 t.Fatalf("unexpected fname") 135 } 136 case "mimeType": 137 value := components[1] 138 valueBytes, err := base64.URLEncoding.DecodeString(value) 139 if err != nil { 140 t.Fatal(err) 141 } 142 if string(valueBytes) != "application/json" { 143 t.Fatalf("unexpected mimeType") 144 } 145 case "x-qn-meta-a": 146 value := components[1] 147 valueBytes, err := base64.URLEncoding.DecodeString(value) 148 if err != nil { 149 t.Fatal(err) 150 } 151 if string(valueBytes) != "b" { 152 t.Fatalf("unexpected x-qn-meta-a") 153 } 154 case "x-qn-meta-c": 155 value := components[1] 156 valueBytes, err := base64.URLEncoding.DecodeString(value) 157 if err != nil { 158 t.Fatal(err) 159 } 160 if string(valueBytes) != "d" { 161 t.Fatalf("unexpected x-qn-meta-c") 162 } 163 case "x:a": 164 value := components[1] 165 valueBytes, err := base64.URLEncoding.DecodeString(value) 166 if err != nil { 167 t.Fatal(err) 168 } 169 if string(valueBytes) != "b" { 170 t.Fatalf("unexpected x:a") 171 } 172 case "x:c": 173 value := components[1] 174 valueBytes, err := base64.URLEncoding.DecodeString(value) 175 if err != nil { 176 t.Fatal(err) 177 } 178 if string(valueBytes) != "d" { 179 t.Fatalf("unexpected x:c") 180 } 181 default: 182 t.Fatalf("unexpected component key: %s", components[0]) 183 } 184 components = components[2:] 185 } 186 actualBody, err := internal_io.ReadAll(r.Body) 187 if err != nil { 188 t.Fatal(err) 189 } 190 if string(actualBody) != "testctx1,testctx2" { 191 t.Fatalf("unexpected body") 192 } 193 w.Header().Add("X-ReqId", "fakereqid") 194 w.Write([]byte(`{"ok":true}`)) 195 }).Methods(http.MethodPost) 196 server = httptest.NewServer(serveMux) 197 defer server.Close() 198 199 multiPartsUploader := newMultiPartsUploader(newConcurrentMultiPartsUploaderScheduler( 200 NewMultiPartsUploaderV1(&MultiPartsUploaderOptions{ 201 Options: http_client.Options{ 202 Regions: ®ion.Region{Up: region.Endpoints{Preferred: []string{server.URL}}}, 203 Credentials: credentials.NewCredentials("testak", "testsk"), 204 }, 205 }), &concurrentMultiPartsUploaderSchedulerOptions{PartSize: 1 << 22, Concurrency: 2}, 206 )) 207 208 var ( 209 key = "testkey" 210 returnValue struct { 211 Ok bool `json:"ok"` 212 } 213 lastUploaded uint64 214 ) 215 if err = multiPartsUploader.UploadFile(context.Background(), tmpFile.Name(), &ObjectOptions{ 216 BucketName: "testbucket", 217 ObjectName: &key, 218 FileName: "testfilename", 219 ContentType: "application/json", 220 Metadata: map[string]string{"a": "b", "c": "d"}, 221 CustomVars: map[string]string{"a": "b", "c": "d"}, 222 OnUploadingProgress: func(progress *UploadingProgress) { 223 if progress.TotalSize != 5*1024*1024 { 224 t.Fatalf("unexpected file size") 225 } else if progress.Uploaded > progress.TotalSize { 226 t.Fatalf("unexpected uploaded") 227 } else if lu := atomic.SwapUint64(&lastUploaded, progress.Uploaded); lu > progress.Uploaded || lu > progress.TotalSize { 228 t.Fatalf("unexpected uploaded") 229 } 230 }, 231 }, &returnValue); err != nil { 232 t.Fatal(err) 233 } else if !returnValue.Ok { 234 t.Fatalf("unexpected response body") 235 } 236 } 237 238 func TestMultiPartsUploaderResuming(t *testing.T) { 239 tmpDir, err := os.MkdirTemp("", "multi-parts-uploader-test-*") 240 if err != nil { 241 t.Fatal(err) 242 } 243 defer os.RemoveAll(tmpDir) 244 245 tmpFile, err := os.CreateTemp("", "multi-parts-uploader-test-*") 246 if err != nil { 247 t.Fatal(err) 248 } 249 defer os.Remove(tmpFile.Name()) 250 defer tmpFile.Close() 251 252 r := rand.New(rand.NewSource(time.Now().UnixNano())) 253 if _, err = io.CopyN(tmpFile, r, 5*1024*1024); err != nil { 254 t.Fatal(err) 255 } 256 if _, err = tmpFile.Seek(0, io.SeekStart); err != nil { 257 t.Fatal(err) 258 } 259 260 var server *httptest.Server 261 serveMux := mux.NewRouter() 262 serveMux.PathPrefix("/mkfile/").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 263 if !strings.HasPrefix(r.Header.Get("Authorization"), "UpToken testak:") { 264 t.Fatalf("unexpected authorization") 265 } 266 components := strings.Split(strings.TrimPrefix(r.URL.Path, "/mkfile/"), "/") 267 if components[0] != strconv.FormatInt(5*1024*1024, 10) { 268 t.Fatalf("unexpected fileSize") 269 } 270 components = components[1:] 271 for len(components) > 0 { 272 switch components[0] { 273 case "key": 274 value := components[1] 275 valueBytes, err := base64.URLEncoding.DecodeString(value) 276 if err != nil { 277 t.Fatal(err) 278 } 279 if string(valueBytes) != "testkey" { 280 t.Fatalf("unexpected key") 281 } 282 case "fname": 283 value := components[1] 284 valueBytes, err := base64.URLEncoding.DecodeString(value) 285 if err != nil { 286 t.Fatal(err) 287 } 288 if string(valueBytes) != "testfilename" { 289 t.Fatalf("unexpected fname") 290 } 291 case "mimeType": 292 value := components[1] 293 valueBytes, err := base64.URLEncoding.DecodeString(value) 294 if err != nil { 295 t.Fatal(err) 296 } 297 if string(valueBytes) != "application/json" { 298 t.Fatalf("unexpected mimeType") 299 } 300 case "x-qn-meta-a": 301 value := components[1] 302 valueBytes, err := base64.URLEncoding.DecodeString(value) 303 if err != nil { 304 t.Fatal(err) 305 } 306 if string(valueBytes) != "b" { 307 t.Fatalf("unexpected x-qn-meta-a") 308 } 309 case "x-qn-meta-c": 310 value := components[1] 311 valueBytes, err := base64.URLEncoding.DecodeString(value) 312 if err != nil { 313 t.Fatal(err) 314 } 315 if string(valueBytes) != "d" { 316 t.Fatalf("unexpected x-qn-meta-c") 317 } 318 case "x:a": 319 value := components[1] 320 valueBytes, err := base64.URLEncoding.DecodeString(value) 321 if err != nil { 322 t.Fatal(err) 323 } 324 if string(valueBytes) != "b" { 325 t.Fatalf("unexpected x:a") 326 } 327 case "x:c": 328 value := components[1] 329 valueBytes, err := base64.URLEncoding.DecodeString(value) 330 if err != nil { 331 t.Fatal(err) 332 } 333 if string(valueBytes) != "d" { 334 t.Fatalf("unexpected x:c") 335 } 336 default: 337 t.Fatalf("unexpected component key: %s", components[0]) 338 } 339 components = components[2:] 340 } 341 actualBody, err := internal_io.ReadAll(r.Body) 342 if err != nil { 343 t.Fatal(err) 344 } 345 if string(actualBody) != "testctx1,testctx2" { 346 t.Fatalf("unexpected body") 347 } 348 w.Header().Add("X-ReqId", "fakereqid") 349 w.Write([]byte(`{"ok":true}`)) 350 }).Methods(http.MethodPost) 351 server = httptest.NewServer(serveMux) 352 defer server.Close() 353 354 tmpFileStat, err := tmpFile.Stat() 355 if err != nil { 356 t.Fatal(err) 357 } 358 tmpFileSourceID := fmt.Sprintf("%d:%d:%s", tmpFileStat.Size(), tmpFileStat.ModTime().UnixNano(), tmpFile.Name()) 359 360 resumableRecorder := resumablerecorder.NewJsonFileSystemResumableRecorder(tmpDir) 361 medium := resumableRecorder.OpenForCreatingNew(&resumablerecorder.ResumableRecorderOpenArgs{ 362 AccessKey: "testak", 363 BucketName: "testbucket", 364 ObjectName: "testkey", 365 SourceID: tmpFileSourceID, 366 PartSize: 4 * 1024 * 1024, 367 TotalSize: 5 * 1024 * 1024, 368 UpEndpoints: region.Endpoints{Preferred: []string{server.URL}}, 369 }) 370 if err = medium.Write(&resumablerecorder.ResumableRecord{ 371 PartID: "testctx1", 372 Offset: 0, 373 PartSize: 4 * 1024 * 1024, 374 PartNumber: 1, 375 ExpiredAt: time.Now().Add(1 * time.Hour), 376 }); err != nil { 377 t.Fatal(err) 378 } 379 if err = medium.Write(&resumablerecorder.ResumableRecord{ 380 PartID: "testctx2", 381 Offset: 4 * 1024 * 1024, 382 PartSize: 1 * 1024 * 1024, 383 PartNumber: 2, 384 ExpiredAt: time.Now().Add(1 * time.Hour), 385 }); err != nil { 386 t.Fatal(err) 387 } 388 if err = medium.Close(); err != nil { 389 t.Fatal(err) 390 } 391 392 multiPartsUploader := newMultiPartsUploader(newConcurrentMultiPartsUploaderScheduler( 393 NewMultiPartsUploaderV1(&MultiPartsUploaderOptions{ 394 ResumableRecorder: resumableRecorder, 395 Options: http_client.Options{ 396 Regions: ®ion.Region{Up: region.Endpoints{Preferred: []string{server.URL}}}, 397 Credentials: credentials.NewCredentials("testak", "testsk"), 398 }, 399 }), &concurrentMultiPartsUploaderSchedulerOptions{PartSize: 1 << 22, Concurrency: 2}, 400 )) 401 402 var ( 403 key = "testkey" 404 returnValue struct { 405 Ok bool `json:"ok"` 406 } 407 lastUploaded uint64 408 ) 409 if err = multiPartsUploader.UploadFile(context.Background(), tmpFile.Name(), &ObjectOptions{ 410 BucketName: "testbucket", 411 ObjectName: &key, 412 FileName: "testfilename", 413 ContentType: "application/json", 414 Metadata: map[string]string{"a": "b", "c": "d"}, 415 CustomVars: map[string]string{"a": "b", "c": "d"}, 416 OnUploadingProgress: func(progress *UploadingProgress) { 417 if progress.TotalSize != 5*1024*1024 { 418 t.Fatalf("unexpected file size") 419 } else if progress.Uploaded > progress.TotalSize { 420 t.Fatalf("unexpected uploaded") 421 } else if lu := atomic.SwapUint64(&lastUploaded, progress.Uploaded); lu > progress.Uploaded || lu > progress.TotalSize { 422 t.Fatalf("unexpected uploaded") 423 } 424 }, 425 }, &returnValue); err != nil { 426 t.Fatal(err) 427 } else if !returnValue.Ok { 428 t.Fatalf("unexpected response body") 429 } 430 } 431 432 func TestMultiPartsUploaderRetry(t *testing.T) { 433 tmpFile, err := os.CreateTemp("", "multi-parts-uploader-test-*") 434 if err != nil { 435 t.Fatal(err) 436 } 437 defer os.Remove(tmpFile.Name()) 438 defer tmpFile.Close() 439 440 r := rand.New(rand.NewSource(time.Now().UnixNano())) 441 if _, err = io.CopyN(tmpFile, r, 5*1024*1024); err != nil { 442 t.Fatal(err) 443 } 444 if _, err = tmpFile.Seek(0, io.SeekStart); err != nil { 445 t.Fatal(err) 446 } 447 448 var handlerCalled_1, handlerCalled_2, handlerCalled_3 uint64 449 serveMux_1 := mux.NewRouter() 450 serveMux_1.HandleFunc("/mkblk/{blockSize}", func(w http.ResponseWriter, r *http.Request) { 451 atomic.AddUint64(&handlerCalled_1, 1) 452 w.Header().Add("X-ReqId", "fakereqid") 453 w.WriteHeader(599) 454 }).Methods(http.MethodPost) 455 server_1 := httptest.NewServer(serveMux_1) 456 defer server_1.Close() 457 458 serveMux_2 := mux.NewRouter() 459 serveMux_2.HandleFunc("/mkblk/{blockSize}", func(w http.ResponseWriter, r *http.Request) { 460 atomic.AddUint64(&handlerCalled_2, 1) 461 w.WriteHeader(200) 462 }).Methods(http.MethodPost) 463 server_2 := httptest.NewServer(serveMux_2) 464 defer server_2.Close() 465 466 var server_3 *httptest.Server 467 serveMux_3 := mux.NewRouter() 468 serveMux_3.HandleFunc("/mkblk/4194304", func(w http.ResponseWriter, r *http.Request) { 469 atomic.AddUint64(&handlerCalled_3, 1) 470 if !strings.HasPrefix(r.Header.Get("Authorization"), "UpToken testak:") { 471 t.Fatalf("unexpected authorization") 472 } 473 actualBody, err := internal_io.ReadAll(r.Body) 474 if err != nil { 475 t.Fatal(err) 476 } 477 expectedBody, err := internal_io.ReadAll(io.NewSectionReader(tmpFile, 0, 4*1024*1024)) 478 if err != nil { 479 t.Fatal(err) 480 } 481 if !bytes.Equal(actualBody, expectedBody) { 482 t.Fatalf("unexpected body") 483 } 484 jsonBody, err := json.Marshal(&apis.ResumableUploadV1MakeBlockResponse{ 485 Ctx: "testctx1", 486 Checksum: "testchecksum1", 487 Crc32: int64(crc32.ChecksumIEEE(actualBody)), 488 Host: server_3.URL, 489 ExpiredAt: time.Now().Add(1 * time.Hour).Unix(), 490 }) 491 if err != nil { 492 t.Fatal(err) 493 } 494 w.Header().Add("X-ReqId", "fakereqid") 495 w.Write(jsonBody) 496 }).Methods(http.MethodPost) 497 serveMux_3.HandleFunc("/mkblk/1048576", func(w http.ResponseWriter, r *http.Request) { 498 atomic.AddUint64(&handlerCalled_3, 1) 499 if !strings.HasPrefix(r.Header.Get("Authorization"), "UpToken testak:") { 500 t.Fatalf("unexpected authorization") 501 } 502 actualBody, err := internal_io.ReadAll(r.Body) 503 if err != nil { 504 t.Fatal(err) 505 } 506 expectedBody, err := internal_io.ReadAll(io.NewSectionReader(tmpFile, 4*1024*1024, 1*1024*1024)) 507 if err != nil { 508 t.Fatal(err) 509 } 510 if !bytes.Equal(actualBody, expectedBody) { 511 t.Fatalf("unexpected body") 512 } 513 jsonBody, err := json.Marshal(&apis.ResumableUploadV1MakeBlockResponse{ 514 Ctx: "testctx2", 515 Checksum: "testchecksum2", 516 Crc32: int64(crc32.ChecksumIEEE(actualBody)), 517 Host: server_3.URL, 518 ExpiredAt: time.Now().Add(1 * time.Hour).Unix(), 519 }) 520 if err != nil { 521 t.Fatal(err) 522 } 523 w.Header().Add("X-ReqId", "fakereqid") 524 w.Write(jsonBody) 525 }).Methods(http.MethodPost) 526 serveMux_3.PathPrefix("/mkfile/").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 527 atomic.AddUint64(&handlerCalled_3, 1) 528 if !strings.HasPrefix(r.Header.Get("Authorization"), "UpToken testak:") { 529 t.Fatalf("unexpected authorization") 530 } 531 components := strings.Split(strings.TrimPrefix(r.URL.Path, "/mkfile/"), "/") 532 if components[0] != strconv.FormatInt(5*1024*1024, 10) { 533 t.Fatalf("unexpected fileSize") 534 } 535 components = components[1:] 536 for len(components) > 0 { 537 switch components[0] { 538 case "key": 539 value := components[1] 540 valueBytes, err := base64.URLEncoding.DecodeString(value) 541 if err != nil { 542 t.Fatal(err) 543 } 544 if string(valueBytes) != "testkey" { 545 t.Fatalf("unexpected key") 546 } 547 case "fname": 548 value := components[1] 549 valueBytes, err := base64.URLEncoding.DecodeString(value) 550 if err != nil { 551 t.Fatal(err) 552 } 553 if string(valueBytes) != "testfilename" { 554 t.Fatalf("unexpected fname") 555 } 556 case "mimeType": 557 value := components[1] 558 valueBytes, err := base64.URLEncoding.DecodeString(value) 559 if err != nil { 560 t.Fatal(err) 561 } 562 if string(valueBytes) != "application/json" { 563 t.Fatalf("unexpected mimeType") 564 } 565 case "x-qn-meta-a": 566 value := components[1] 567 valueBytes, err := base64.URLEncoding.DecodeString(value) 568 if err != nil { 569 t.Fatal(err) 570 } 571 if string(valueBytes) != "b" { 572 t.Fatalf("unexpected x-qn-meta-a") 573 } 574 case "x-qn-meta-c": 575 value := components[1] 576 valueBytes, err := base64.URLEncoding.DecodeString(value) 577 if err != nil { 578 t.Fatal(err) 579 } 580 if string(valueBytes) != "d" { 581 t.Fatalf("unexpected x-qn-meta-c") 582 } 583 case "x:a": 584 value := components[1] 585 valueBytes, err := base64.URLEncoding.DecodeString(value) 586 if err != nil { 587 t.Fatal(err) 588 } 589 if string(valueBytes) != "b" { 590 t.Fatalf("unexpected x:a") 591 } 592 case "x:c": 593 value := components[1] 594 valueBytes, err := base64.URLEncoding.DecodeString(value) 595 if err != nil { 596 t.Fatal(err) 597 } 598 if string(valueBytes) != "d" { 599 t.Fatalf("unexpected x:c") 600 } 601 default: 602 t.Fatalf("unexpected component key: %s", components[0]) 603 } 604 components = components[2:] 605 } 606 actualBody, err := internal_io.ReadAll(r.Body) 607 if err != nil { 608 t.Fatal(err) 609 } 610 if string(actualBody) != "testctx1,testctx2" { 611 t.Fatalf("unexpected body") 612 } 613 w.Header().Add("X-ReqId", "fakereqid") 614 w.Write([]byte(`{"ok":true}`)) 615 }).Methods(http.MethodPost) 616 server_3 = httptest.NewServer(serveMux_3) 617 defer server_3.Close() 618 619 multiPartsUploader := newMultiPartsUploader(newSerialMultiPartsUploaderScheduler( 620 NewMultiPartsUploaderV1(&MultiPartsUploaderOptions{ 621 Options: http_client.Options{ 622 Regions: regions{[]*region.Region{ 623 {Up: region.Endpoints{Preferred: []string{server_1.URL}}}, 624 {Up: region.Endpoints{Preferred: []string{server_2.URL}}}, 625 {Up: region.Endpoints{Preferred: []string{server_3.URL}}}, 626 }}, 627 Credentials: credentials.NewCredentials("testak", "testsk"), 628 }, 629 }), &serialMultiPartsUploaderSchedulerOptions{PartSize: 1 << 22}, 630 )) 631 632 var ( 633 key = "testkey" 634 returnValue struct { 635 Ok bool `json:"ok"` 636 } 637 lastUploaded uint64 638 ) 639 if err = multiPartsUploader.UploadFile(context.Background(), tmpFile.Name(), &ObjectOptions{ 640 BucketName: "testbucket", 641 ObjectName: &key, 642 FileName: "testfilename", 643 ContentType: "application/json", 644 Metadata: map[string]string{"a": "b", "c": "d"}, 645 CustomVars: map[string]string{"a": "b", "c": "d"}, 646 OnUploadingProgress: func(progress *UploadingProgress) { 647 if progress.TotalSize != 5*1024*1024 { 648 t.Fatalf("unexpected file size") 649 } 650 atomic.StoreUint64(&lastUploaded, progress.Uploaded) 651 }, 652 }, &returnValue); err != nil { 653 t.Fatal(err) 654 } else if !returnValue.Ok { 655 t.Fatalf("unexpected response body") 656 } 657 658 if fileSize := atomic.LoadUint64(&lastUploaded); fileSize != 5*1024*1024 { 659 t.Fatalf("unexpected file size: %d", fileSize) 660 } 661 if count := atomic.LoadUint64(&handlerCalled_1); count != 4 { 662 t.Fatalf("unexpected handler call count: %d", count) 663 } 664 if count := atomic.LoadUint64(&handlerCalled_2); count != 4 { 665 t.Fatalf("unexpected handler call count: %d", count) 666 } 667 if count := atomic.LoadUint64(&handlerCalled_3); count != 3 { 668 t.Fatalf("unexpected handler call count: %d", count) 669 } 670 }