github.com/0chain/gosdk@v1.17.11/zboxcore/sdk/allocation_test.go (about) 1 package sdk 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/hex" 7 "encoding/json" 8 "io" 9 "io/fs" 10 "io/ioutil" 11 "log" 12 "net/http" 13 "os" 14 "strconv" 15 "strings" 16 "sync" 17 "testing" 18 "time" 19 20 "github.com/0chain/gosdk/dev/blobber" 21 "github.com/0chain/gosdk/dev/blobber/model" 22 "github.com/0chain/gosdk/zboxcore/encryption" 23 "golang.org/x/crypto/sha3" 24 25 "github.com/0chain/errors" 26 "github.com/0chain/gosdk/core/common" 27 "github.com/0chain/gosdk/core/sys" 28 29 "github.com/0chain/gosdk/core/zcncrypto" 30 "github.com/0chain/gosdk/zboxcore/blockchain" 31 zclient "github.com/0chain/gosdk/zboxcore/client" 32 "github.com/0chain/gosdk/zboxcore/fileref" 33 "github.com/0chain/gosdk/zboxcore/mocks" 34 "github.com/0chain/gosdk/zboxcore/zboxutil" 35 "github.com/stretchr/testify/mock" 36 "github.com/stretchr/testify/require" 37 ) 38 39 const ( 40 tokenUnit = 10000000000.0 41 mockAllocationId = "mock allocation id" 42 mockAllocationTxId = "mock transaction id" 43 mockClientId = "mock client id" 44 mockClientKey = "mock client key" 45 mockBlobberId = "mock blobber id" 46 mockBlobberUrl = "mockBlobberUrl" 47 mockLookupHash = "mock lookup hash" 48 mockAllocationRoot = "mock allocation root" 49 mockFileRefName = "mock file ref name" 50 numBlobbers = 4 51 ) 52 53 func setupMockGetFileMetaResponse( 54 t *testing.T, mockClient *mocks.HttpClient, funcName string, 55 testCaseName string, a *Allocation, httpMethod string, 56 statusCode int, body []byte) { 57 58 for i := 0; i < numBlobbers; i++ { 59 url := funcName + testCaseName + mockBlobberUrl + strconv.Itoa(i) + zboxutil.FILE_META_ENDPOINT 60 mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 61 log.Println(req.URL.String(), url) 62 return req.Method == httpMethod && 63 strings.HasPrefix(req.URL.String(), url) 64 })).Return(&http.Response{ 65 StatusCode: statusCode, 66 Body: ioutil.NopCloser(bytes.NewReader(body)), 67 }, nil).Once() 68 } 69 } 70 71 func setupMockHttpResponse( 72 t *testing.T, mockClient *mocks.HttpClient, funcName string, 73 testCaseName string, a *Allocation, httpMethod string, 74 statusCode int, body []byte) { 75 76 for i := 0; i < numBlobbers; i++ { 77 mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 78 return req.Method == httpMethod && strings.Contains(req.URL.String(), "list=true") 79 })).Return(&http.Response{ 80 StatusCode: statusCode, 81 Body: io.NopCloser(bytes.NewReader(body)), 82 }, nil).Once() 83 } 84 85 for i := 0; i < numBlobbers; i++ { 86 url := funcName + testCaseName + mockBlobberUrl + strconv.Itoa(i) 87 mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 88 return req.Method == httpMethod && 89 strings.Contains(req.URL.String(), url) 90 })).Return(&http.Response{ 91 StatusCode: statusCode, 92 Body: io.NopCloser(bytes.NewReader(body)), 93 }, nil).Once() 94 } 95 } 96 97 func setupMockCommitRequest(a *Allocation) { 98 commitChan = make(map[string]chan *CommitRequest) 99 for _, blobber := range a.Blobbers { 100 if _, ok := commitChan[blobber.ID]; !ok { 101 commitChan[blobber.ID] = make(chan *CommitRequest, 1) 102 blobberChan := commitChan[blobber.ID] 103 go func(c <-chan *CommitRequest, blID string) { 104 for { 105 cm := <-c 106 if cm != nil { 107 cm.result = &CommitResult{ 108 Success: true, 109 } 110 if cm.wg != nil { 111 cm.wg.Done() 112 } 113 } 114 } 115 }(blobberChan, blobber.ID) 116 } 117 } 118 } 119 120 func setupMockWriteLockRequest(a *Allocation, mockClient *mocks.HttpClient) { 121 122 for _, blobber := range a.Blobbers { 123 url := blobber.Baseurl + zboxutil.WM_LOCK_ENDPOINT 124 url = strings.TrimRight(url, "/") 125 mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 126 return strings.Contains(req.URL.String(), url) 127 })).Return(&http.Response{ 128 StatusCode: http.StatusOK, 129 Body: func() io.ReadCloser { 130 resp := &WMLockResult{ 131 Status: WMLockStatusOK, 132 } 133 respBuf, _ := json.Marshal(resp) 134 return io.NopCloser(bytes.NewReader(respBuf)) 135 }(), 136 }, nil) 137 } 138 } 139 140 func setupMockFile(t *testing.T, path string) (teardown func(t *testing.T)) { 141 _, err := os.Create(path) 142 require.Nil(t, err) 143 err = ioutil.WriteFile(path, []byte("mockActualHash"), os.ModePerm) 144 require.Nil(t, err) 145 return func(t *testing.T) { 146 os.Remove(path) 147 } 148 } 149 150 func setupMockRollback(a *Allocation, mockClient *mocks.HttpClient) { 151 152 for _, blobber := range a.Blobbers { 153 url := blobber.Baseurl + zboxutil.LATEST_WRITE_MARKER_ENDPOINT 154 url = strings.TrimRight(url, "/") 155 mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 156 return strings.Contains(req.URL.String(), url) 157 })).Return(&http.Response{ 158 StatusCode: http.StatusOK, 159 Body: func() io.ReadCloser { 160 s := `{"latest_write_marker":null,"prev_write_marker":null}` 161 return ioutil.NopCloser(bytes.NewReader([]byte(s))) 162 }(), 163 }, nil) 164 165 newUrl := blobber.Baseurl + zboxutil.ROLLBACK_ENDPOINT 166 newUrl = strings.TrimRight(newUrl, "/") 167 mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 168 return strings.Contains(req.URL.String(), newUrl) 169 })).Return(&http.Response{ 170 StatusCode: http.StatusOK, 171 Body: ioutil.NopCloser(bytes.NewReader(nil)), 172 }, nil) 173 } 174 175 } 176 177 func setupMockFileAndReferencePathResult(t *testing.T, allocationID, name string) (teardown func(t *testing.T)) { 178 var buf = []byte("mockActualHash") 179 h := sha3.New256() 180 f, _ := os.Create(name) 181 w := io.MultiWriter(h, f) 182 //nolint: errcheck 183 w.Write(buf) 184 185 cancel := blobber.MockReferencePathResult(allocationID, &model.Ref{ 186 AllocationID: allocationID, 187 Type: model.DIRECTORY, 188 Name: "/", 189 Path: "/", 190 PathLevel: 1, 191 ParentPath: "", 192 Children: []*model.Ref{ 193 { 194 AllocationID: allocationID, 195 Name: name, 196 Type: model.FILE, 197 Path: "/" + name, 198 ActualFileSize: int64(len(buf)), 199 ActualFileHash: hex.EncodeToString(h.Sum(nil)), 200 ChunkSize: CHUNK_SIZE, 201 PathLevel: 2, 202 ParentPath: "/", 203 }, 204 }, 205 }) 206 207 return func(t *testing.T) { 208 cancel() 209 os.Remove(name) 210 } 211 } 212 213 func TestGetMinMaxWriteReadSuccess(t *testing.T) { 214 var ssc = newTestAllocation() 215 ssc.DataShards = 5 216 ssc.ParityShards = 4 217 218 ssc.initialized = true 219 sdkInitialized = true 220 require.NotNil(t, ssc.BlobberDetails) 221 222 t.Run("Success minR, minW", func(t *testing.T) { 223 minW, minR, err := ssc.GetMinWriteRead() 224 require.NoError(t, err) 225 require.Equal(t, 0.01, minW) 226 require.Equal(t, 0.01, minR) 227 }) 228 229 t.Run("Success maxR, maxW", func(t *testing.T) { 230 maxW, maxR, err := ssc.GetMaxWriteRead() 231 require.NoError(t, err) 232 require.Equal(t, 0.01, maxW) 233 require.Equal(t, 0.01, maxR) 234 }) 235 236 t.Run("Error / No Blobbers", func(t *testing.T) { 237 var ( 238 ssc = newTestAllocationEmptyBlobbers() 239 err error 240 ) 241 ssc.initialized = true 242 _, _, err = ssc.GetMinWriteRead() 243 require.Error(t, err) 244 }) 245 246 t.Run("Error / Empty Blobbers", func(t *testing.T) { 247 var err error 248 ssc.initialized = false 249 _, _, err = ssc.GetMinWriteRead() 250 require.Error(t, err) 251 }) 252 253 t.Run("Error / Not Initialized", func(t *testing.T) { 254 var err error 255 ssc.initialized = false 256 _, _, err = ssc.GetMinWriteRead() 257 require.Error(t, err) 258 }) 259 } 260 261 func TestGetMaxMinStorageCostSuccess(t *testing.T) { 262 var ssc = newTestAllocation() 263 ssc.DataShards = 4 264 ssc.ParityShards = 2 265 266 ssc.initialized = true 267 sdkInitialized = true 268 269 t.Run("Storage cost", func(t *testing.T) { 270 cost, err := ssc.GetMaxStorageCost(100 * GB) 271 require.NoError(t, err) 272 require.Equal(t, 1.5, cost) 273 }) 274 } 275 276 func newTestAllocationEmptyBlobbers() (ssc *Allocation) { 277 ssc = new(Allocation) 278 ssc.Expiration = 0 279 ssc.ID = "ID" 280 ssc.BlobberDetails = make([]*BlobberAllocation, 0) 281 return ssc 282 } 283 284 func newTestAllocation() (ssc *Allocation) { 285 ssc = new(Allocation) 286 ssc.Expiration = 0 287 ssc.ID = "ID" 288 ssc.BlobberDetails = newBlobbersDetails() 289 return ssc 290 } 291 292 func newBlobbersDetails() (blobbers []*BlobberAllocation) { 293 blobberDetails := make([]*BlobberAllocation, 0) 294 295 for i := 1; i <= 1; i++ { 296 var balloc BlobberAllocation 297 balloc.Size = 1000 298 299 balloc.Terms = Terms{ReadPrice: common.Balance(100000000), WritePrice: common.Balance(100000000)} 300 blobberDetails = append(blobberDetails, &balloc) 301 } 302 303 return blobberDetails 304 } 305 306 type MockFile struct { 307 os.FileInfo 308 } 309 310 func (m MockFile) Size() int64 { return 10 } 311 312 func TestPriceRange_IsValid(t *testing.T) { 313 type fields struct { 314 Min uint64 315 Max uint64 316 } 317 tests := []struct { 318 name string 319 fields fields 320 want bool 321 }{ 322 { 323 "Test_Valid_InRange", 324 fields{ 325 Min: 0, 326 Max: 50, 327 }, 328 true, 329 }, 330 { 331 "Test_Valid_At_Once_Value", 332 fields{ 333 Min: 10, 334 Max: 10, 335 }, 336 true, 337 }, 338 { 339 "Test_Invalid_InRange", 340 fields{ 341 Min: 10, 342 Max: 5, 343 }, 344 false, 345 }, 346 } 347 for _, tt := range tests { 348 t.Run(tt.name, func(t *testing.T) { 349 pr := &PriceRange{ 350 Min: tt.fields.Min, 351 Max: tt.fields.Max, 352 } 353 got := pr.IsValid() 354 require := require.New(t) 355 var check = require.False 356 if tt.want { 357 check = require.True 358 } 359 check(got) 360 }) 361 } 362 } 363 364 func TestAllocation_InitAllocation(t *testing.T) { 365 a := Allocation{ 366 FileOptions: 63, 367 } 368 a.InitAllocation() 369 require.New(t).NotZero(a) 370 } 371 372 func TestAllocation_dispatchWork(t *testing.T) { 373 a := Allocation{DataShards: 2, ParityShards: 2, downloadChan: make(chan *DownloadRequest), repairChan: make(chan *RepairRequest)} 374 t.Run("Test_Cover_Context_Canceled", func(t *testing.T) { 375 ctx, cancelFn := context.WithCancel(context.Background()) 376 go a.dispatchWork(ctx) 377 cancelFn() 378 }) 379 t.Run("Test_Cover_Download_Request", func(t *testing.T) { 380 ctx, ctxCncl := context.WithCancel(context.Background()) 381 go a.dispatchWork(context.Background()) 382 a.downloadChan <- &DownloadRequest{ctx: ctx, ctxCncl: ctxCncl} 383 }) 384 t.Run("Test_Cover_Repair_Request", func(t *testing.T) { 385 go a.dispatchWork(context.Background()) 386 a.repairChan <- &RepairRequest{listDir: &ListResult{}} 387 }) 388 } 389 390 func TestAllocation_GetStats(t *testing.T) { 391 stats := &AllocationStats{} 392 a := &Allocation{ 393 Stats: stats, 394 } 395 got := a.GetStats() 396 require.New(t).Same(stats, got) 397 } 398 399 func TestAllocation_GetBlobberStats(t *testing.T) { 400 var mockClient = mocks.HttpClient{} 401 zboxutil.Client = &mockClient 402 403 client := zclient.GetClient() 404 client.Wallet = &zcncrypto.Wallet{ 405 ClientID: mockClientId, 406 ClientKey: mockClientKey, 407 } 408 409 tests := []struct { 410 name string 411 setup func(*testing.T, string) 412 }{ 413 { 414 name: "Test_Success", 415 setup: func(t *testing.T, testName string) { 416 mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 417 return strings.HasPrefix(req.URL.Path, "TestAllocation_GetBlobberStats"+testName) 418 })).Return(&http.Response{ 419 Body: func() io.ReadCloser { 420 jsonFR, err := json.Marshal(&BlobberAllocationStats{ 421 ID: mockAllocationId, 422 Tx: mockAllocationTxId, 423 }) 424 require.NoError(t, err) 425 return ioutil.NopCloser(bytes.NewReader([]byte(jsonFR))) 426 }(), 427 StatusCode: http.StatusOK, 428 }, nil) 429 }, 430 }, 431 } 432 for _, tt := range tests { 433 t.Run(tt.name, func(t *testing.T) { 434 require := require.New(t) 435 tt.setup(t, tt.name) 436 a := &Allocation{ 437 ID: mockAllocationId, 438 Tx: mockAllocationTxId, 439 } 440 a.Blobbers = append(a.Blobbers, &blockchain.StorageNode{ 441 ID: tt.name + mockBlobberId, 442 Baseurl: "TestAllocation_GetBlobberStats" + tt.name + mockBlobberUrl, 443 }) 444 got := a.GetBlobberStats() 445 require.NotEmptyf(got, "Error no blobber stats result found") 446 447 expected := make(map[string]*BlobberAllocationStats, 1) 448 expected["TestAllocation_GetBlobberStats"+tt.name+mockBlobberUrl] = &BlobberAllocationStats{ 449 ID: mockAllocationId, 450 Tx: mockAllocationTxId, 451 BlobberID: tt.name + mockBlobberId, 452 BlobberURL: "TestAllocation_GetBlobberStats" + tt.name + mockBlobberUrl, 453 } 454 455 for key, val := range expected { 456 require.NotNilf(got[key], "Error result map must be contain key %v", key) 457 require.EqualValues(val, got[key]) 458 } 459 }) 460 } 461 } 462 463 func TestAllocation_isInitialized(t *testing.T) { 464 tests := []struct { 465 name string 466 sdkInitialized, allocationInitialized, want bool 467 }{ 468 { 469 "Test_Initialized", 470 true, true, true, 471 }, 472 { 473 "Test_SDK_Uninitialized", 474 false, true, false, 475 }, 476 { 477 "Test_Allocation_Uninitialized", 478 true, false, false, 479 }, 480 { 481 "Test_Both_SDK_And_Allocation_Uninitialized", 482 false, false, false, 483 }, 484 } 485 for _, tt := range tests { 486 t.Run(tt.name, func(t *testing.T) { 487 originalSDKInitialized := sdkInitialized 488 defer func() { sdkInitialized = originalSDKInitialized }() 489 sdkInitialized = tt.sdkInitialized 490 a := &Allocation{initialized: tt.allocationInitialized} 491 got := a.isInitialized() 492 require := require.New(t) 493 if tt.want { 494 require.True(got, `Error a.isInitialized() should returns "true"", but got "false"`) 495 return 496 } 497 require.False(got, `Error a.isInitialized() should returns "false"", but got "true"`) 498 }) 499 } 500 } 501 502 // Uncomment tests later on after critical issues are fixed 503 // func TestAllocation_CreateDir(t *testing.T) { 504 // const mockLocalPath = "/test" 505 // require := require.New(t) 506 // if teardown := setupMockFile(t, mockLocalPath); teardown != nil { 507 // defer teardown(t) 508 // } 509 // a := &Allocation{ 510 // ParityShards: 2, 511 // DataShards: 2, 512 // } 513 // setupMockAllocation(t, a) 514 515 // var mockClient = mocks.HttpClient{} 516 // zboxutil.Client = &mockClient 517 518 // client := zclient.GetClient() 519 // client.Wallet = &zcncrypto.Wallet{ 520 // ClientID: mockClientId, 521 // ClientKey: mockClientKey, 522 // } 523 524 // mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 525 // return strings.HasPrefix(req.URL.Path, "TestAllocation_CreateDir") 526 // })).Return(&http.Response{ 527 // StatusCode: http.StatusOK, 528 // Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), 529 // }, nil) 530 531 // for i := 0; i < numBlobbers; i++ { 532 // a.Blobbers = append(a.Blobbers, &blockchain.StorageNode{ 533 // ID: mockBlobberId + strconv.Itoa(i), 534 // Baseurl: "TestAllocation_CreateDir" + mockBlobberUrl + strconv.Itoa(i), 535 // }) 536 // } 537 // err := a.CreateDir(mockLocalPath) 538 // require.NoErrorf(err, "Unexpected error %v", err) 539 // } 540 541 func TestAllocation_RepairRequired(t *testing.T) { 542 const ( 543 mockActualHash = "4041e3eeb170751544a47af4e4f9d374e76cee1d" 544 ) 545 546 var mockClient = mocks.HttpClient{} 547 zboxutil.Client = &mockClient 548 549 client := zclient.GetClient() 550 client.Wallet = &zcncrypto.Wallet{ 551 ClientID: mockClientId, 552 ClientKey: mockClientKey, 553 } 554 555 tests := []struct { 556 name string 557 setup func(*testing.T, string, *Allocation) (teardown func(*testing.T)) 558 remotePath string 559 wantFound uint64 560 wantFileRef *fileref.FileRef 561 wantMatchesConsensus, wantErr bool 562 errMsg string 563 }{ 564 { 565 name: "Test_Not_Repair_Required_Success", 566 setup: func(t *testing.T, testCaseName string, a *Allocation) (teardown func(t *testing.T)) { 567 for i := 0; i < numBlobbers; i++ { 568 url := "TestAllocation_RepairRequired" + testCaseName + mockBlobberUrl + strconv.Itoa(i) 569 a.Blobbers = append(a.Blobbers, &blockchain.StorageNode{ 570 Baseurl: url, 571 }) 572 mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 573 return strings.HasPrefix(req.URL.Path, url) 574 })).Return(&http.Response{ 575 StatusCode: http.StatusOK, 576 Body: func() io.ReadCloser { 577 respString := `{"file_meta_hash":"` + mockActualHash + `"}` 578 return ioutil.NopCloser(bytes.NewReader([]byte(respString))) 579 }(), 580 }, nil) 581 } 582 return nil 583 }, 584 remotePath: "/x.txt", 585 wantFound: 0xf, 586 wantMatchesConsensus: false, 587 wantErr: false, 588 }, 589 { 590 name: "Test_Uninitialized_Failed", 591 setup: func(t *testing.T, testCaseName string, a *Allocation) (teardown func(t *testing.T)) { 592 a.initialized = false 593 return func(t *testing.T) { a.initialized = true } 594 }, 595 remotePath: "/", 596 wantFound: 0, 597 wantMatchesConsensus: false, 598 wantErr: true, 599 errMsg: "sdk_not_initialized: Please call InitStorageSDK Init and use GetAllocation to get the allocation object", 600 }, 601 { 602 name: "Test_Repair_Required_Success", 603 setup: func(t *testing.T, testCaseName string, a *Allocation) (teardown func(t *testing.T)) { 604 for i := 0; i < numBlobbers; i++ { 605 var hash string 606 if i < numBlobbers-1 { 607 hash = mockActualHash 608 } 609 url := "TestAllocation_RepairRequired" + testCaseName + mockBlobberUrl + strconv.Itoa(i) 610 a.Blobbers = append(a.Blobbers, &blockchain.StorageNode{ 611 Baseurl: url, 612 }) 613 mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 614 return strings.HasPrefix(req.URL.Path, url) 615 })).Return(&http.Response{ 616 StatusCode: http.StatusOK, 617 Body: func(hash string) io.ReadCloser { 618 respString := `{"file_meta_hash":"` + hash + `"}` 619 return ioutil.NopCloser(bytes.NewReader([]byte(respString))) 620 }(hash), 621 }, nil) 622 } 623 return nil 624 }, 625 remotePath: "/", 626 wantFound: 0x7, 627 wantMatchesConsensus: true, 628 wantErr: false, 629 }, 630 { 631 name: "Test_Remote_File_Not_Found_Failed", 632 setup: func(t *testing.T, testCaseName string, a *Allocation) (teardown func(t *testing.T)) { 633 for i := 0; i < numBlobbers; i++ { 634 url := "TestAllocation_RepairRequired" + testCaseName + mockBlobberUrl + strconv.Itoa(i) 635 a.Blobbers = append(a.Blobbers, &blockchain.StorageNode{ 636 Baseurl: url, 637 }) 638 mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 639 return strings.HasPrefix(req.URL.Path, url) 640 })).Return(&http.Response{ 641 StatusCode: http.StatusBadRequest, 642 Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), 643 }, nil) 644 } 645 return nil 646 }, 647 remotePath: "/x.txt", 648 wantFound: 0x0, 649 wantMatchesConsensus: false, 650 wantErr: true, 651 errMsg: "File not found for the given remotepath", 652 }, 653 } 654 for _, tt := range tests { 655 t.Run(tt.name, func(t *testing.T) { 656 require := require.New(t) 657 a := &Allocation{ 658 DataShards: 2, 659 ParityShards: 2, 660 FileOptions: 63, 661 } 662 a.InitAllocation() 663 sdkInitialized = true 664 if tt.setup != nil { 665 if teardown := tt.setup(t, tt.name, a); teardown != nil { 666 defer teardown(t) 667 } 668 } 669 found, _, matchesConsensus, fileRef, err := a.RepairRequired(tt.remotePath) 670 require.Equal(zboxutil.NewUint128(tt.wantFound), found, "found value must be same") 671 if tt.wantMatchesConsensus { 672 require.True(tt.wantMatchesConsensus, matchesConsensus) 673 } else { 674 require.False(tt.wantMatchesConsensus, matchesConsensus) 675 } 676 require.EqualValues(tt.wantErr, err != nil) 677 if err != nil { 678 require.EqualValues(tt.errMsg, errors.Top(err)) 679 return 680 } 681 require.EqualValues(mockActualHash, fileRef.FileMetaHash) 682 require.NoErrorf(err, "Unexpected error %v", err) 683 }) 684 } 685 } 686 687 func TestAllocation_DownloadFileToFileHandler(t *testing.T) { 688 const ( 689 mockActualHash = "mockActualHash" 690 mockRemoteFilePath = "1.txt" 691 ) 692 693 var mockFile = &sys.MemFile{Name: "mockFile", Mode: fs.ModePerm, ModTime: time.Now()} 694 var mockClient = mocks.HttpClient{} 695 zboxutil.Client = &mockClient 696 697 client := zclient.GetClient() 698 client.Wallet = &zcncrypto.Wallet{ 699 ClientID: mockClientId, 700 ClientKey: mockClientKey, 701 } 702 703 type parameters struct { 704 fileHandler sys.File 705 remotePath string 706 statusCallback StatusCallback 707 } 708 tests := []struct { 709 name string 710 parameters parameters 711 setup func(*testing.T, string, parameters, *Allocation) (teardown func(*testing.T)) 712 wantErr bool 713 errMsg string 714 }{ 715 { 716 name: "Test_Download_File_Success", 717 parameters: parameters{ 718 fileHandler: mockFile, 719 remotePath: mockRemoteFilePath, 720 statusCallback: nil, 721 }, 722 setup: func(t *testing.T, testCaseName string, p parameters, a *Allocation) (teardown func(t *testing.T)) { 723 for i := 0; i < numBlobbers; i++ { 724 url := "TestAllocation_DownloadToFileHandler" + mockBlobberUrl + strconv.Itoa(i) 725 mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 726 return strings.HasPrefix(req.URL.Path, url) 727 })).Return(&http.Response{ 728 StatusCode: http.StatusOK, 729 Body: func() io.ReadCloser { 730 jsonFR, err := json.Marshal(&fileref.FileRef{ 731 ActualFileHash: mockActualHash, 732 }) 733 require.NoError(t, err) 734 return ioutil.NopCloser(bytes.NewReader([]byte(jsonFR))) 735 }(), 736 }, nil) 737 } 738 return nil 739 }, 740 }, 741 } 742 for _, tt := range tests { 743 t.Run(tt.name, func(t *testing.T) { 744 require := require.New(t) 745 a := &Allocation{} 746 setupMockAllocation(t, a) 747 for i := 0; i < numBlobbers; i++ { 748 a.Blobbers = append(a.Blobbers, &blockchain.StorageNode{ 749 ID: tt.name + strconv.Itoa(i), 750 Baseurl: "TestAllocation_DownloadToFileHandler" + mockBlobberUrl + strconv.Itoa(i), 751 }) 752 } 753 if m := tt.setup; m != nil { 754 if teardown := m(t, tt.name, tt.parameters, a); teardown != nil { 755 defer teardown(t) 756 } 757 } 758 759 err := a.DownloadFileToFileHandler(tt.parameters.fileHandler, tt.parameters.remotePath, true, tt.parameters.statusCallback, false) 760 require.EqualValues(tt.wantErr, err != nil) 761 if err != nil { 762 require.EqualValues(tt.errMsg, errors.Top(err)) 763 return 764 } 765 require.NoErrorf(err, "Unexpected error: %v", err) 766 }) 767 } 768 } 769 770 func TestAllocation_DownloadFile(t *testing.T) { 771 const ( 772 mockActualHash = "mockActualHash" 773 mockLocalPath = "DownloadFile" 774 mockRemoteFilePath = "1.txt" 775 ) 776 var mockClient = mocks.HttpClient{} 777 zboxutil.Client = &mockClient 778 779 client := zclient.GetClient() 780 client.Wallet = &zcncrypto.Wallet{ 781 ClientID: mockClientId, 782 ClientKey: mockClientKey, 783 } 784 785 require := require.New(t) 786 a := &Allocation{ 787 ParityShards: 2, 788 DataShards: 2, 789 } 790 setupMockAllocation(t, a) 791 792 for i := 0; i < numBlobbers; i++ { 793 a.Blobbers = append(a.Blobbers, &blockchain.StorageNode{ 794 ID: mockBlobberId + strconv.Itoa(i), 795 Baseurl: mockBlobberUrl + strconv.Itoa(i), 796 }) 797 } 798 for i := 0; i < numBlobbers; i++ { 799 url := mockBlobberUrl + strconv.Itoa(i) 800 mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 801 return strings.HasPrefix(req.URL.Path, url) 802 })).Return(&http.Response{ 803 StatusCode: http.StatusOK, 804 Body: func() io.ReadCloser { 805 jsonFR, err := json.Marshal(&fileref.FileRef{ 806 ActualFileHash: mockActualHash, 807 }) 808 require.NoError(err) 809 return ioutil.NopCloser(bytes.NewReader([]byte(jsonFR))) 810 }(), 811 }, nil) 812 } 813 814 defer os.RemoveAll(mockLocalPath) //nolint: errcheck 815 err := a.DownloadFile(mockLocalPath, mockRemoteFilePath, false, nil, false) 816 require.NoErrorf(err, "Unexpected error %v", err) 817 } 818 819 func TestAllocation_DownloadFileByBlock(t *testing.T) { 820 const ( 821 mockLocalPath = "DownloadFileByBlock" 822 mockRemoteFilePath = "1.txt" 823 ) 824 var mockClient = mocks.HttpClient{} 825 zboxutil.Client = &mockClient 826 827 client := zclient.GetClient() 828 client.Wallet = &zcncrypto.Wallet{ 829 ClientID: mockClientId, 830 ClientKey: mockClientKey, 831 } 832 833 require := require.New(t) 834 a := &Allocation{ 835 ParityShards: 2, 836 DataShards: 2, 837 } 838 setupMockAllocation(t, a) 839 840 for i := 0; i < numBlobbers; i++ { 841 a.Blobbers = append(a.Blobbers, &blockchain.StorageNode{ 842 ID: mockBlobberId + strconv.Itoa(i), 843 Baseurl: mockBlobberUrl + strconv.Itoa(i), 844 }) 845 } 846 defer os.RemoveAll(mockLocalPath) //nolint: errcheck 847 err := a.DownloadFileByBlock(mockLocalPath, mockRemoteFilePath, 1, 0, numBlockDownloads, true, nil, false) 848 require.NoErrorf(err, "Unexpected error %v", err) 849 } 850 851 func TestAllocation_DownloadThumbnail(t *testing.T) { 852 const ( 853 mockLocalPath = "DownloadThumbnail" 854 mockRemoteFilePath = "1.txt" 855 ) 856 require := require.New(t) 857 a := &Allocation{} 858 setupMockAllocation(t, a) 859 860 for i := 0; i < numBlobbers; i++ { 861 a.Blobbers = append(a.Blobbers, &blockchain.StorageNode{ 862 ID: strconv.Itoa(i), 863 Baseurl: "TestAllocation_DownloadThumbnail" + mockBlobberUrl + strconv.Itoa(i), 864 }) 865 } 866 867 defer os.RemoveAll(mockLocalPath) //nolint: errcheck 868 err := a.DownloadThumbnail(mockLocalPath, mockRemoteFilePath, true, nil, false) 869 require.NoErrorf(err, "Unexpected error %v", err) 870 } 871 872 func TestAllocation_downloadFile(t *testing.T) { 873 const ( 874 mockActualHash = "mockActualHash" 875 mockLocalPath = "alloc" 876 mockRemoteFilePath = "1.txt" 877 ) 878 879 var mockClient = mocks.HttpClient{} 880 zboxutil.Client = &mockClient 881 882 client := zclient.GetClient() 883 client.Wallet = &zcncrypto.Wallet{ 884 ClientID: mockClientId, 885 ClientKey: mockClientKey, 886 } 887 888 type parameters struct { 889 localPath, remotePath, contentMode string 890 startBlock, endBlock int64 891 numBlocks int 892 statusCallback StatusCallback 893 } 894 tests := []struct { 895 name string 896 parameters parameters 897 setup func(*testing.T, string, parameters, *Allocation) (teardown func(*testing.T)) 898 wantErr bool 899 errMsg string 900 }{ 901 { 902 name: "Test_Uninitialized_Failed", 903 parameters: parameters{ 904 mockLocalPath, mockRemoteFilePath, 905 DOWNLOAD_CONTENT_FULL, 906 1, 0, 907 numBlockDownloads, 908 nil, 909 }, 910 setup: func(t *testing.T, testCaseName string, p parameters, a *Allocation) (teardown func(t *testing.T)) { 911 a.initialized = false 912 return func(t *testing.T) { a.initialized = true } 913 }, 914 wantErr: true, 915 errMsg: "sdk_not_initialized: Please call InitStorageSDK Init and use GetAllocation to get the allocation object", 916 }, 917 { 918 name: "Test_No_Blobber_Failed", 919 parameters: parameters{ 920 localPath: mockLocalPath, 921 remotePath: mockRemoteFilePath, 922 contentMode: DOWNLOAD_CONTENT_FULL, 923 startBlock: 1, 924 endBlock: 0, 925 numBlocks: numBlockDownloads, 926 statusCallback: nil, 927 }, 928 setup: func(t *testing.T, testCaseName string, p parameters, a *Allocation) (teardown func(t *testing.T)) { 929 blobbers := a.Blobbers 930 a.Blobbers = []*blockchain.StorageNode{} 931 return func(t *testing.T) { 932 a.Blobbers = blobbers 933 } 934 }, 935 wantErr: true, 936 errMsg: "No Blobbers set in this allocation", 937 }, 938 { 939 name: "Test_Download_File_Success", 940 parameters: parameters{ 941 localPath: mockLocalPath, 942 remotePath: mockRemoteFilePath, 943 contentMode: DOWNLOAD_CONTENT_FULL, 944 startBlock: 1, 945 endBlock: 0, 946 numBlocks: numBlockDownloads, 947 statusCallback: nil, 948 }, 949 setup: func(t *testing.T, testCaseName string, p parameters, a *Allocation) (teardown func(t *testing.T)) { 950 for i := 0; i < numBlobbers; i++ { 951 url := "TestAllocation_downloadFile" + mockBlobberUrl + strconv.Itoa(i) 952 mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 953 return strings.HasPrefix(req.URL.Path, url) 954 })).Return(&http.Response{ 955 StatusCode: http.StatusOK, 956 Body: func() io.ReadCloser { 957 jsonFR, err := json.Marshal(&fileref.FileRef{ 958 ActualFileHash: mockActualHash, 959 }) 960 require.NoError(t, err) 961 return ioutil.NopCloser(bytes.NewReader([]byte(jsonFR))) 962 }(), 963 }, nil) 964 } 965 return func(t *testing.T) { 966 os.Remove("alloc/1.txt") //nolint: errcheck 967 } 968 }, 969 }, 970 } 971 for _, tt := range tests { 972 t.Run(tt.name, func(t *testing.T) { 973 require := require.New(t) 974 a := &Allocation{} 975 a.downloadChan = make(chan *DownloadRequest, 10) 976 a.repairChan = make(chan *RepairRequest, 1) 977 a.ctx, a.ctxCancelF = context.WithCancel(context.Background()) 978 a.downloadProgressMap = make(map[string]*DownloadRequest) 979 a.mutex = &sync.Mutex{} 980 a.initialized = true 981 sdkInitialized = true 982 setupMockAllocation(t, a) 983 for i := 0; i < numBlobbers; i++ { 984 a.Blobbers = append(a.Blobbers, &blockchain.StorageNode{ 985 ID: tt.name + strconv.Itoa(i), 986 Baseurl: "TestAllocation_downloadFile" + tt.name + mockBlobberUrl + strconv.Itoa(i), 987 }) 988 } 989 if m := tt.setup; m != nil { 990 if teardown := m(t, tt.name, tt.parameters, a); teardown != nil { 991 defer teardown(t) 992 } 993 } 994 995 f, localFilePath, _, err := a.prepareAndOpenLocalFile(tt.parameters.localPath, tt.parameters.remotePath) 996 defer func() { 997 f.Close() //nolint: errcheck 998 os.Remove(localFilePath) //nolint: errcheck 999 }() 1000 1001 if err == nil { 1002 err = a.addAndGenerateDownloadRequest( 1003 f, tt.parameters.remotePath, tt.parameters.contentMode, 1004 tt.parameters.startBlock, tt.parameters.endBlock, tt.parameters.numBlocks, 1005 true, tt.parameters.statusCallback, false, localFilePath) 1006 } 1007 require.EqualValues(tt.wantErr, err != nil) 1008 if err != nil { 1009 require.EqualValues(tt.errMsg, errors.Top(err)) 1010 return 1011 } 1012 require.NoErrorf(err, "Unexpected error: %v", err) 1013 }) 1014 } 1015 } 1016 1017 func TestAllocation_GetRefs(t *testing.T) { 1018 1019 var mockClient = mocks.HttpClient{} 1020 zboxutil.Client = &mockClient 1021 1022 client := zclient.GetClient() 1023 client.Wallet = &zcncrypto.Wallet{ 1024 ClientID: mockClientId, 1025 ClientKey: mockClientKey, 1026 } 1027 functionName := "TestAllocation_GetRefs" 1028 t.Run("Test_Get_Refs_Returns_Slice_Of_Length_0_When_File_Not_Present", func(t *testing.T) { 1029 a := &Allocation{ 1030 DataShards: 2, 1031 ParityShards: 2, 1032 } 1033 testCaseName := "Test_Get_Refs_Returns_Slice_Of_Length_0_When_File_Not_Present" 1034 a.InitAllocation() 1035 sdkInitialized = true 1036 for i := 0; i < numBlobbers; i++ { 1037 a.Blobbers = append(a.Blobbers, &blockchain.StorageNode{ 1038 ID: testCaseName + mockBlobberId + strconv.Itoa(i), 1039 Baseurl: functionName + testCaseName + mockBlobberUrl + strconv.Itoa(i), 1040 }) 1041 } 1042 for i := 0; i < numBlobbers; i++ { 1043 body, err := json.Marshal(map[string]string{ 1044 "code": "invalid_path", 1045 "error": "invalid_path: ", 1046 }) 1047 require.NoError(t, err) 1048 setupMockHttpResponse(t, &mockClient, functionName, testCaseName, a, http.MethodGet, http.StatusBadRequest, body) 1049 } 1050 path := "/any_random_path.txt" 1051 otr, err := a.GetRefs(path, "", "", "", "f", "regular", 0, 5) 1052 require.NoError(t, err) 1053 require.Equal(t, true, len(otr.Refs) == 0) 1054 1055 }) 1056 } 1057 1058 func TestAllocation_GetFileMeta(t *testing.T) { 1059 const ( 1060 mockType = "f" 1061 mockActualHash = "mockActualHash" 1062 ) 1063 1064 var mockClient = mocks.HttpClient{} 1065 zboxutil.Client = &mockClient 1066 1067 client := zclient.GetClient() 1068 client.Wallet = &zcncrypto.Wallet{ 1069 ClientID: mockClientId, 1070 ClientKey: mockClientKey, 1071 } 1072 1073 type parameters struct { 1074 path string 1075 } 1076 tests := []struct { 1077 name string 1078 parameters parameters 1079 setup func(*testing.T, string, *Allocation) (teardown func(*testing.T)) 1080 wantErr bool 1081 errMsg string 1082 }{ 1083 { 1084 name: "Test_Uninitialized_Failed", 1085 parameters: parameters{}, 1086 setup: func(t *testing.T, testCaseName string, a *Allocation) (teardown func(t *testing.T)) { 1087 a.initialized = false 1088 return func(t *testing.T) { 1089 a.initialized = true 1090 } 1091 }, 1092 wantErr: true, 1093 errMsg: "sdk_not_initialized: Please call InitStorageSDK Init and use GetAllocation to get the allocation object", 1094 }, 1095 { 1096 name: "Test_Error_Getting_File_Meta_Data_From_Blobbers_Failed", 1097 parameters: parameters{ 1098 path: "/1.txt", 1099 }, 1100 setup: func(t *testing.T, testCaseName string, a *Allocation) (teardown func(t *testing.T)) { 1101 body, err := json.Marshal(&fileref.FileRef{ 1102 ActualFileHash: mockActualHash, 1103 }) 1104 require.NoError(t, err) 1105 setupMockHttpResponse(t, &mockClient, "TestAllocation_GetFileMeta", testCaseName, a, http.MethodPost, http.StatusBadRequest, body) 1106 return nil 1107 }, 1108 wantErr: true, 1109 errMsg: "file_meta_error: Error getting the file meta data from blobbers", 1110 }, 1111 { 1112 name: "Test_Success", 1113 parameters: parameters{ 1114 path: "/1.txt", 1115 }, 1116 setup: func(t *testing.T, testCaseName string, a *Allocation) (teardown func(t *testing.T)) { 1117 body, err := json.Marshal(&fileref.FileRef{ 1118 ActualFileHash: mockActualHash, 1119 }) 1120 require.NoError(t, err) 1121 setupMockHttpResponse(t, &mockClient, "TestAllocation_GetFileMeta", testCaseName, a, http.MethodPost, http.StatusOK, body) 1122 return nil 1123 }, 1124 }, 1125 } 1126 for _, tt := range tests { 1127 t.Run(tt.name, func(t *testing.T) { 1128 require := require.New(t) 1129 a := &Allocation{ 1130 DataShards: 2, 1131 ParityShards: 2, 1132 FileOptions: 63, 1133 } 1134 a.InitAllocation() 1135 sdkInitialized = true 1136 for i := 0; i < numBlobbers; i++ { 1137 a.Blobbers = append(a.Blobbers, &blockchain.StorageNode{ 1138 ID: tt.name + mockBlobberId + strconv.Itoa(i), 1139 Baseurl: "TestAllocation_GetFileMeta" + tt.name + mockBlobberUrl + strconv.Itoa(i), 1140 }) 1141 } 1142 if tt.setup != nil { 1143 if teardown := tt.setup(t, tt.name, a); teardown != nil { 1144 defer teardown(t) 1145 } 1146 } 1147 got, err := a.GetFileMeta(tt.parameters.path) 1148 require.EqualValues(tt.wantErr, err != nil) 1149 if err != nil { 1150 require.EqualValues(tt.errMsg, errors.Top(err)) 1151 return 1152 } 1153 require.NoErrorf(err, "unexpected error: %v", err) 1154 expectedResult := &ConsolidatedFileMeta{ 1155 Hash: mockActualHash, 1156 } 1157 require.EqualValues(expectedResult, got) 1158 }) 1159 } 1160 } 1161 1162 func TestAllocation_GetAuthTicketForShare(t *testing.T) { 1163 const mockValidationRoot = "mock validation root" 1164 const numberBlobbers = 10 1165 1166 var mockClient = mocks.HttpClient{} 1167 httpResponse := http.Response{ 1168 StatusCode: http.StatusOK, 1169 Body: func() io.ReadCloser { 1170 jsonFR, err := json.Marshal(fileref.FileRef{ 1171 Ref: fileref.Ref{ 1172 Name: mockFileRefName, 1173 }, 1174 ValidationRoot: mockValidationRoot, 1175 }) 1176 require.NoError(t, err) 1177 return ioutil.NopCloser(bytes.NewReader([]byte(jsonFR))) 1178 }(), 1179 } 1180 zboxutil.Client = &mockClient 1181 for i := 0; i < numBlobbers; i++ { 1182 mockClient.On("Do", mock.Anything).Return(&httpResponse, nil) 1183 } 1184 1185 client := zclient.GetClient() 1186 client.Wallet = &zcncrypto.Wallet{ 1187 ClientID: mockClientId, 1188 ClientKey: mockClientKey, 1189 } 1190 require := require.New(t) 1191 a := &Allocation{DataShards: 1, ParityShards: 1, FileOptions: 63} 1192 a.InitAllocation() 1193 for i := 0; i < numberBlobbers; i++ { 1194 a.Blobbers = append(a.Blobbers, &blockchain.StorageNode{}) 1195 } 1196 sdkInitialized = true 1197 at, err := a.GetAuthTicketForShare("/1.txt", "1.txt", fileref.FILE, "") 1198 require.NotEmptyf(at, "unexpected empty auth ticket") 1199 require.NoErrorf(err, "unexpected error: %v", err) 1200 } 1201 1202 func TestAllocation_GetAuthTicket(t *testing.T) { 1203 var testTitle = "TestAllocation_GetAuthTicket" 1204 type parameters struct { 1205 path string 1206 filename string 1207 referenceType string 1208 refereeClientID string 1209 refereeEncryptionPublicKey string 1210 } 1211 tests := []struct { 1212 name string 1213 parameters parameters 1214 setup func(*testing.T, string, *Allocation, *mocks.HttpClient) (teardown func(*testing.T)) 1215 wantErr bool 1216 errMsg string 1217 }{ 1218 { 1219 name: "Test_Uninitialized_Failed", 1220 setup: func(t *testing.T, testCaseName string, a *Allocation, mockClient *mocks.HttpClient) (teardown func(t *testing.T)) { 1221 a.initialized = false 1222 httpResponse := http.Response{ 1223 StatusCode: http.StatusOK, 1224 Body: func() io.ReadCloser { 1225 jsonFR, err := json.Marshal(fileref.FileRef{ 1226 Ref: fileref.Ref{ 1227 Name: mockFileRefName, 1228 }, 1229 ValidationRoot: "mock validation root", 1230 }) 1231 require.NoError(t, err) 1232 return ioutil.NopCloser(bytes.NewReader([]byte(jsonFR))) 1233 }(), 1234 } 1235 for i := 0; i < numBlobbers; i++ { 1236 mockClient.On("Do", mock.Anything).Return(&httpResponse, nil) 1237 } 1238 return func(t *testing.T) { 1239 a.initialized = true 1240 } 1241 }, 1242 wantErr: true, 1243 errMsg: "sdk_not_initialized: Please call InitStorageSDK Init and use GetAllocation to get the allocation object", 1244 }, 1245 { 1246 name: "Test_Success_File_Type_Directory", 1247 parameters: parameters{ 1248 path: "/", 1249 filename: "1.txt", 1250 referenceType: fileref.DIRECTORY, 1251 refereeClientID: mockClientId, 1252 }, 1253 setup: func(t *testing.T, testCaseName string, a *Allocation, mockClient *mocks.HttpClient) (teardown func(t *testing.T)) { 1254 httpResponse := &http.Response{ 1255 StatusCode: http.StatusOK, 1256 Body: func() io.ReadCloser { 1257 jsonFR, err := json.Marshal(fileref.FileRef{ 1258 Ref: fileref.Ref{ 1259 Name: mockFileRefName, 1260 }, 1261 ValidationRoot: "mock validation root", 1262 }) 1263 require.NoError(t, err) 1264 return ioutil.NopCloser(bytes.NewReader([]byte(jsonFR))) 1265 }(), 1266 } 1267 for i := 0; i < numBlobbers; i++ { 1268 mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 1269 return strings.HasPrefix(req.URL.Path, testTitle+testCaseName) 1270 })).Return(httpResponse, nil) 1271 } 1272 return nil 1273 }, 1274 }, 1275 { 1276 name: "Test_Success_With_Referee_Encryption_Public_Key", 1277 parameters: parameters{ 1278 path: "/1.txt", 1279 filename: "1.txt", 1280 referenceType: fileref.FILE, 1281 refereeClientID: mockClientId, 1282 refereeEncryptionPublicKey: func() string { 1283 client_mnemonic := "travel twenty hen negative fresh sentence hen flat swift embody increase juice eternal satisfy want vessel matter honey video begin dutch trigger romance assault" 1284 client_encscheme := encryption.NewEncryptionScheme() 1285 _, err := client_encscheme.Initialize(client_mnemonic) 1286 require.Nil(t, err) 1287 client_encscheme.InitForEncryption("filetype:audio") 1288 client_enc_pub_key, err := client_encscheme.GetPublicKey() 1289 require.NoError(t, err) 1290 return client_enc_pub_key 1291 }(), 1292 }, 1293 setup: func(t *testing.T, testCaseName string, a *Allocation, mockClient *mocks.HttpClient) (teardown func(t *testing.T)) { 1294 // mock GetFileMeta for private sharing validation 1295 fileMeta, err := json.Marshal(&fileref.FileRef{ 1296 EncryptedKey: "EncryptedKey", 1297 }) 1298 require.NoError(t, err) 1299 setupMockHttpResponse(t, mockClient, "TestAllocation_GetAuthTicket", testCaseName, a, http.MethodPost, http.StatusOK, fileMeta) 1300 1301 body, err := json.Marshal(&fileref.ReferencePath{ 1302 Meta: map[string]interface{}{ 1303 "type": "f", 1304 }, 1305 }) 1306 httpResponse := &http.Response{ 1307 StatusCode: http.StatusOK, 1308 Body: func() io.ReadCloser { 1309 jsonFR, err := json.Marshal(fileref.FileRef{ 1310 Ref: fileref.Ref{ 1311 Name: mockFileRefName, 1312 }, 1313 ValidationRoot: "mock validation root", 1314 }) 1315 require.NoError(t, err) 1316 return ioutil.NopCloser(bytes.NewReader([]byte(jsonFR))) 1317 }(), 1318 } 1319 for i := 0; i < numBlobbers; i++ { 1320 mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 1321 return strings.HasPrefix(req.URL.Path, testTitle+testCaseName) 1322 })).Return(httpResponse, nil) 1323 } 1324 require.NoError(t, err) 1325 setupMockHttpResponse(t, mockClient, "TestAllocation_GetAuthTicket", testCaseName, a, http.MethodPost, http.StatusOK, body) 1326 return nil 1327 }, 1328 }, 1329 { 1330 name: "Test_Success_With_No_Referee_Encryption_Public_Key", 1331 parameters: parameters{ 1332 path: "/1.txt", 1333 filename: "1.txt", 1334 referenceType: fileref.FILE, 1335 refereeClientID: mockClientId, 1336 refereeEncryptionPublicKey: "", 1337 }, 1338 setup: func(t *testing.T, testCaseName string, a *Allocation, mockClient *mocks.HttpClient) (teardown func(t *testing.T)) { 1339 1340 // mock GetFileMeta for private sharing validation 1341 fileMeta, err := json.Marshal(&fileref.FileRef{ 1342 EncryptedKey: "EncryptedKey", 1343 }) 1344 require.NoError(t, err) 1345 setupMockHttpResponse(t, mockClient, "TestAllocation_GetAuthTicket", testCaseName, a, http.MethodPost, http.StatusOK, fileMeta) 1346 1347 httpResponse := &http.Response{ 1348 StatusCode: http.StatusOK, 1349 Body: func() io.ReadCloser { 1350 jsonFR, err := json.Marshal(fileref.FileRef{ 1351 Ref: fileref.Ref{ 1352 Name: mockFileRefName, 1353 }, 1354 ValidationRoot: "mock validation root", 1355 }) 1356 require.NoError(t, err) 1357 return ioutil.NopCloser(bytes.NewReader([]byte(jsonFR))) 1358 }(), 1359 } 1360 for i := 0; i < numBlobbers; i++ { 1361 mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 1362 return strings.HasPrefix(req.URL.Path, testTitle+testCaseName) 1363 })).Return(httpResponse, nil) 1364 } 1365 return nil 1366 }, 1367 }, 1368 { 1369 name: "Test_Invalid_Path_Failed", 1370 parameters: parameters{ 1371 filename: "1.txt", 1372 }, 1373 wantErr: true, 1374 errMsg: "invalid_path: Invalid path for the list", 1375 }, 1376 { 1377 name: "Test_Remote_Path_Not_Absolute_Failed", 1378 parameters: parameters{ 1379 path: "x", 1380 }, 1381 wantErr: true, 1382 errMsg: "invalid_path: Path should be valid and absolute", 1383 }, 1384 } 1385 1386 for _, tt := range tests { 1387 t.Run(tt.name, func(t *testing.T) { 1388 var mockClient = mocks.HttpClient{} 1389 zboxutil.Client = &mockClient 1390 1391 client := zclient.GetClient() 1392 client.Wallet = &zcncrypto.Wallet{ 1393 ClientID: mockClientId, 1394 ClientKey: mockClientKey, 1395 } 1396 1397 require := require.New(t) 1398 a := &Allocation{ 1399 DataShards: 1, 1400 ParityShards: 1, 1401 FileOptions: 63, 1402 } 1403 a.InitAllocation() 1404 sdkInitialized = true 1405 1406 for i := 0; i < numBlobbers; i++ { 1407 a.Blobbers = append(a.Blobbers, &blockchain.StorageNode{ 1408 ID: tt.name + mockBlobberId + strconv.Itoa(i), 1409 Baseurl: "TestAllocation_GetAuthTicket" + tt.name + mockBlobberUrl + strconv.Itoa(i), 1410 }) 1411 } 1412 1413 zboxutil.Client = &mockClient 1414 1415 if tt.setup != nil { 1416 if teardown := tt.setup(t, tt.name, a, &mockClient); teardown != nil { 1417 defer teardown(t) 1418 } 1419 } 1420 at, err := a.GetAuthTicket(tt.parameters.path, tt.parameters.filename, tt.parameters.referenceType, tt.parameters.refereeClientID, tt.parameters.refereeEncryptionPublicKey, 0, nil) 1421 require.EqualValues(tt.wantErr, err != nil, errors.Top(err)) 1422 if err != nil { 1423 require.EqualValues(tt.errMsg, errors.Top(err)) 1424 return 1425 } 1426 require.NoErrorf(err, "unexpected error: %v", err) 1427 require.NotEmptyf(at, "unexpected empty auth ticket") 1428 }) 1429 } 1430 } 1431 1432 func TestAllocation_CancelDownload(t *testing.T) { 1433 const remotePath = "/1.txt" 1434 type parameters struct { 1435 remotepath string 1436 } 1437 tests := []struct { 1438 name string 1439 parameters parameters 1440 setup func(*testing.T, *Allocation) (teardown func(*testing.T)) 1441 wantErr bool 1442 errMsg string 1443 }{ 1444 { 1445 name: "Test_Failed", 1446 wantErr: true, 1447 errMsg: "local_path_not_found: Invalid path. No upload in progress for the path", 1448 }, 1449 { 1450 name: "Test_Success", 1451 parameters: parameters{ 1452 remotepath: remotePath, 1453 }, 1454 setup: func(t *testing.T, a *Allocation) (teardown func(t *testing.T)) { 1455 req := &DownloadRequest{} 1456 req.ctx, req.ctxCncl = context.WithCancel(context.TODO()) 1457 a.downloadProgressMap[remotePath] = req 1458 return nil 1459 }, 1460 }, 1461 } 1462 for _, tt := range tests { 1463 t.Run(tt.name, func(t *testing.T) { 1464 require := require.New(t) 1465 a := &Allocation{FileOptions: 63} 1466 a.InitAllocation() 1467 sdkInitialized = true 1468 if tt.setup != nil { 1469 if teardown := tt.setup(t, a); teardown != nil { 1470 defer teardown(t) 1471 } 1472 } 1473 err := a.CancelDownload(tt.parameters.remotepath) 1474 if tt.wantErr { 1475 require.Error(err, "expected error != nil") 1476 return 1477 } 1478 require.NoErrorf(err, "unexpected error: %v", err) 1479 }) 1480 } 1481 } 1482 1483 func TestAllocation_ListDirFromAuthTicket(t *testing.T) { 1484 const ( 1485 mockLookupHash = "mock lookup hash" 1486 mockType = "d" 1487 ) 1488 1489 authTicket := getMockAuthTicket(t) 1490 1491 type parameters struct { 1492 authTicket string 1493 lookupHash string 1494 expectedResult *ListResult 1495 } 1496 tests := []struct { 1497 name string 1498 parameters parameters 1499 setup func(*testing.T, string, *Allocation, *mocks.HttpClient) (teardown func(t *testing.T)) 1500 wantErr bool 1501 errMsg string 1502 }{ 1503 { 1504 name: "Test_Uninitialized_Failed", 1505 setup: func(t *testing.T, testCaseName string, a *Allocation, mockClient *mocks.HttpClient) (teardown func(t *testing.T)) { 1506 a.initialized = false 1507 return func(t *testing.T) { 1508 a.initialized = true 1509 } 1510 }, 1511 wantErr: true, 1512 errMsg: "invalid_path: Invalid path for the list", 1513 }, 1514 { 1515 name: "Test_Cannot_Decode_Auth_Ticket_Failed", 1516 parameters: parameters{ 1517 authTicket: "some wrong auth ticket to decode", 1518 }, 1519 setup: nil, 1520 wantErr: true, 1521 errMsg: "invalid_path: Invalid path for the list" + 1522 "", 1523 }, 1524 { 1525 name: "Test_Cannot_Unmarshal_Auth_Ticket_Failed", 1526 parameters: parameters{ 1527 authTicket: "c29tZSB3cm9uZyBhdXRoIHRpY2tldCB0byBtYXJzaGFs", 1528 }, 1529 setup: nil, 1530 wantErr: true, 1531 errMsg: "invalid_path: Invalid path for the list", 1532 }, 1533 { 1534 name: "Test_Wrong_Auth_Ticket_File_Path_Hash_Or_Lookup_Hash_Failed", 1535 parameters: parameters{ 1536 authTicket: authTicket, 1537 lookupHash: "", 1538 }, 1539 setup: nil, 1540 wantErr: true, 1541 errMsg: "invalid_path: Invalid path for the list", 1542 }, 1543 { 1544 name: "Test_Error_Get_List_File_From_Blobbers_Failed", 1545 parameters: parameters{ 1546 authTicket: authTicket, 1547 lookupHash: mockLookupHash, 1548 expectedResult: &ListResult{}, 1549 }, 1550 setup: func(t *testing.T, testCaseName string, a *Allocation, mockClient *mocks.HttpClient) (teardown func(t *testing.T)) { 1551 for i := 0; i < numBlobbers; i++ { 1552 a.Blobbers = append(a.Blobbers, &blockchain.StorageNode{ 1553 Baseurl: "TestAllocation_ListDirFromAuthTicket" + testCaseName + mockBlobberUrl + strconv.Itoa(i), 1554 }) 1555 } 1556 1557 setupMockHttpResponse(t, mockClient, "TestAllocation_ListDirFromAuthTicket", testCaseName, a, http.MethodGet, http.StatusBadRequest, []byte("")) 1558 return func(t *testing.T) { 1559 a.Blobbers = nil 1560 } 1561 }, 1562 wantErr: true, 1563 errMsg: "error from server list response: ", 1564 }, 1565 { 1566 name: "Test_Success", 1567 parameters: parameters{ 1568 authTicket: authTicket, 1569 lookupHash: mockLookupHash, 1570 expectedResult: &ListResult{ 1571 Type: mockType, 1572 Size: 0, 1573 }, 1574 }, 1575 setup: func(t *testing.T, testCaseName string, a *Allocation, mockClient *mocks.HttpClient) (teardown func(t *testing.T)) { 1576 for i := 0; i < numBlobbers; i++ { 1577 a.Blobbers = append(a.Blobbers, &blockchain.StorageNode{ 1578 ID: testCaseName + mockBlobberId + strconv.Itoa(i), 1579 Baseurl: "TestAllocation_ListDirFromAuthTicket" + testCaseName + mockBlobberUrl + strconv.Itoa(i), 1580 }) 1581 } 1582 body, err := json.Marshal(&fileref.ListResult{ 1583 AllocationRoot: mockAllocationRoot, 1584 Meta: map[string]interface{}{ 1585 "type": mockType, 1586 }, 1587 }) 1588 require.NoError(t, err) 1589 setupMockHttpResponse(t, mockClient, "TestAllocation_ListDirFromAuthTicket", testCaseName, a, http.MethodGet, http.StatusOK, body) 1590 return nil 1591 }, 1592 }, 1593 } 1594 for _, tt := range tests { 1595 t.Run(tt.name, func(t *testing.T) { 1596 require := require.New(t) 1597 1598 var mockClient = mocks.HttpClient{} 1599 zboxutil.Client = &mockClient 1600 1601 client := zclient.GetClient() 1602 client.Wallet = &zcncrypto.Wallet{ 1603 ClientID: mockClientId, 1604 ClientKey: mockClientKey, 1605 } 1606 a := &Allocation{ 1607 ID: mockAllocationId, 1608 Tx: mockAllocationTxId, 1609 FileOptions: 63, 1610 DataShards: 2, 1611 ParityShards: 2, 1612 } 1613 if tt.parameters.expectedResult != nil { 1614 tt.parameters.expectedResult.deleteMask = zboxutil.NewUint128(1).Lsh(uint64(a.DataShards + a.ParityShards)).Sub64(1) 1615 } 1616 if tt.setup != nil { 1617 if teardown := tt.setup(t, tt.name, a, &mockClient); teardown != nil { 1618 defer teardown(t) 1619 } 1620 } 1621 1622 setupMockGetFileInfoResponse(t, &mockClient) 1623 a.InitAllocation() 1624 sdkInitialized = true 1625 if len(a.Blobbers) == 0 { 1626 for i := 0; i < numBlobbers; i++ { 1627 a.Blobbers = append(a.Blobbers, &blockchain.StorageNode{}) 1628 } 1629 } 1630 1631 got, err := a.ListDirFromAuthTicket(authTicket, tt.parameters.lookupHash) 1632 require.EqualValues(tt.wantErr, err != nil) 1633 if err != nil { 1634 require.EqualValues(tt.errMsg, errors.Top(err)) 1635 return 1636 } 1637 require.NoErrorf(err, "unexpected error: %v", err) 1638 require.EqualValues(tt.parameters.expectedResult, got) 1639 }) 1640 } 1641 } 1642 1643 func TestAllocation_downloadFromAuthTicket(t *testing.T) { 1644 const ( 1645 mockLookupHash = "mock lookup hash" 1646 mockLocalPath = "alloc" 1647 mockRemoteFileName = "1.txt" 1648 mockType = "f" 1649 mockActualHash = "mockActualHash" 1650 ) 1651 1652 var mockClient = mocks.HttpClient{} 1653 zboxutil.Client = &mockClient 1654 1655 client := zclient.GetClient() 1656 client.Wallet = &zcncrypto.Wallet{ 1657 ClientID: mockClientId, 1658 ClientKey: mockClientKey, 1659 } 1660 1661 a := &Allocation{ 1662 ID: mockAllocationId, 1663 Tx: mockAllocationTxId, 1664 DataShards: 2, 1665 ParityShards: 2, 1666 } 1667 setupMockAllocation(t, a) 1668 setupMockGetFileInfoResponse(t, &mockClient) 1669 authTicket := getMockAuthTicket(t) 1670 1671 type parameters struct { 1672 localPath string 1673 authTicket string 1674 lookupHash string 1675 startBlock int64 1676 endBlock int64 1677 numBlocks int 1678 remoteFilename string 1679 contentMode string 1680 statusCallback StatusCallback 1681 } 1682 tests := []struct { 1683 name string 1684 parameters parameters 1685 setup func(*testing.T, string, parameters) (teardown func(*testing.T)) 1686 blockDownloadResponseMock func(blobber *blockchain.StorageNode, wg *sync.WaitGroup) 1687 wantErr bool 1688 errMsg string 1689 }{ 1690 { 1691 name: "Test_Uninitialized_Failed", 1692 setup: func(t *testing.T, testCaseName string, p parameters) (teardown func(t *testing.T)) { 1693 a.initialized = false 1694 return func(t *testing.T) { 1695 a.initialized = true 1696 } 1697 }, 1698 wantErr: true, 1699 errMsg: "sdk_not_initialized: Please call InitStorageSDK Init and use GetAllocation to get the allocation object", 1700 }, 1701 { 1702 name: "Test_Cannot_Decode_Auth_Ticket_Failed", 1703 parameters: parameters{ 1704 localPath: mockLocalPath, 1705 authTicket: "some wrong auth ticket to decode", 1706 remoteFilename: mockRemoteFileName, 1707 }, 1708 wantErr: true, 1709 errMsg: "auth_ticket_decode_error: Error decoding the auth ticket.illegal base64 data at input byte 4", 1710 }, 1711 { 1712 name: "Test_Cannot_Unmarshal_Auth_Ticket_Failed", 1713 parameters: parameters{ 1714 localPath: mockLocalPath, 1715 authTicket: "c29tZSB3cm9uZyBhdXRoIHRpY2tldCB0byBtYXJzaGFs", 1716 remoteFilename: mockRemoteFileName, 1717 }, 1718 wantErr: true, 1719 errMsg: "auth_ticket_decode_error: Error unmarshaling the auth ticket.invalid character 's' looking for beginning of value", 1720 }, 1721 { 1722 name: "Test_Not_Enough_Minimum_Blobbers_Failed", 1723 parameters: parameters{ 1724 localPath: mockLocalPath, 1725 authTicket: authTicket, 1726 remoteFilename: mockRemoteFileName, 1727 }, 1728 setup: func(t *testing.T, testCaseName string, p parameters) (teardown func(t *testing.T)) { 1729 blobbers := a.Blobbers 1730 a.Blobbers = []*blockchain.StorageNode{} 1731 return func(t *testing.T) { 1732 a.Blobbers = blobbers 1733 } 1734 }, 1735 wantErr: true, 1736 errMsg: "No Blobbers set in this allocation", 1737 }, 1738 { 1739 name: "Test_Download_File_Success", 1740 parameters: parameters{ 1741 localPath: mockLocalPath, 1742 remoteFilename: mockRemoteFileName, 1743 authTicket: authTicket, 1744 contentMode: DOWNLOAD_CONTENT_FULL, 1745 startBlock: 1, 1746 endBlock: 0, 1747 numBlocks: numBlockDownloads, 1748 lookupHash: mockLookupHash}, 1749 setup: func(t *testing.T, testCaseName string, p parameters) (teardown func(t *testing.T)) { 1750 for i := 0; i < numBlobbers; i++ { 1751 a.Blobbers = append(a.Blobbers, &blockchain.StorageNode{ 1752 ID: testCaseName + mockBlobberId + strconv.Itoa(i), 1753 Baseurl: "TestAllocation_downloadFromAuthTicket" + testCaseName + mockBlobberUrl + strconv.Itoa(i), 1754 }) 1755 } 1756 body, err := json.Marshal(&fileref.FileRef{ 1757 Ref: fileref.Ref{ 1758 Name: mockFileRefName, 1759 }, 1760 }) 1761 require.NoError(t, err) 1762 setupMockHttpResponse(t, &mockClient, "TestAllocation_downloadFromAuthTicket", testCaseName, a, http.MethodPost, http.StatusOK, body) 1763 return nil 1764 }, 1765 }, 1766 } 1767 for _, tt := range tests { 1768 t.Run(tt.name, func(t *testing.T) { 1769 require := require.New(t) 1770 if tt.setup != nil { 1771 if teardown := tt.setup(t, tt.name, tt.parameters); teardown != nil { 1772 defer teardown(t) 1773 } 1774 } 1775 1776 f, localFilePath, _, err := a.prepareAndOpenLocalFile(tt.parameters.localPath, tt.parameters.remoteFilename) 1777 defer func() { 1778 f.Close() //nolint: errcheck 1779 os.RemoveAll(mockLocalPath) //nolint: errcheck 1780 }() 1781 1782 if err == nil { 1783 err = a.downloadFromAuthTicket( 1784 f, tt.parameters.authTicket, tt.parameters.lookupHash, 1785 tt.parameters.startBlock, tt.parameters.endBlock, tt.parameters.numBlocks, 1786 tt.parameters.remoteFilename, tt.parameters.contentMode, true, tt.parameters.statusCallback, false, localFilePath) 1787 } 1788 1789 require.EqualValues(tt.wantErr, err != nil) 1790 if err != nil { 1791 require.EqualValues(tt.errMsg, errors.Top(err)) 1792 return 1793 } 1794 require.NoErrorf(err, "unexpected error: %v", err) 1795 }) 1796 } 1797 } 1798 1799 func TestAllocation_listDir(t *testing.T) { 1800 const ( 1801 mockPath = "/1.txt" 1802 mockType = "d" 1803 ) 1804 1805 type parameters struct { 1806 path string 1807 expectedResult *ListResult 1808 } 1809 tests := []struct { 1810 name string 1811 parameters parameters 1812 setup func(*testing.T, string, *Allocation, *mocks.HttpClient) (teardown func(*testing.T)) 1813 wantErr bool 1814 errMsg string 1815 }{ 1816 { 1817 name: "Test_Uninitialized_Failed", 1818 setup: func(t *testing.T, testCaseName string, a *Allocation, mockClient *mocks.HttpClient) (teardown func(t *testing.T)) { 1819 a.initialized = false 1820 return func(t *testing.T) { 1821 a.initialized = true 1822 } 1823 }, 1824 wantErr: true, 1825 errMsg: "sdk_not_initialized: Please call InitStorageSDK Init and use GetAllocation to get the allocation object", 1826 }, 1827 { 1828 name: "Test_Invalid_Path_Failed", 1829 wantErr: true, 1830 errMsg: "invalid_path: Invalid path for the list", 1831 }, 1832 { 1833 name: "Test_Invalid_Absolute_Path_Failed", 1834 parameters: parameters{ 1835 path: "1.txt", 1836 }, 1837 wantErr: true, 1838 errMsg: "invalid_path: Path should be valid and absolute", 1839 }, 1840 { 1841 name: "Test_Error_Get_List_File_From_Blobbers_Failed", 1842 parameters: parameters{ 1843 path: mockPath, 1844 expectedResult: &ListResult{}, 1845 }, 1846 setup: func(t *testing.T, testCaseName string, a *Allocation, mockClient *mocks.HttpClient) (teardown func(t *testing.T)) { 1847 setupMockHttpResponse(t, mockClient, "TestAllocation_listDir", testCaseName, a, http.MethodGet, http.StatusBadRequest, []byte("")) 1848 return nil 1849 }, 1850 wantErr: true, 1851 errMsg: "error from server list response: ", 1852 }, 1853 { 1854 name: "Test_Success", 1855 parameters: parameters{ 1856 path: mockPath, 1857 expectedResult: &ListResult{ 1858 Type: mockType, 1859 Size: 0, 1860 }, 1861 }, 1862 setup: func(t *testing.T, testCaseName string, a *Allocation, mockClient *mocks.HttpClient) (teardown func(t *testing.T)) { 1863 body, err := json.Marshal(&fileref.ListResult{ 1864 AllocationRoot: mockAllocationRoot, 1865 Meta: map[string]interface{}{ 1866 "type": mockType, 1867 }, 1868 }) 1869 require.NoError(t, err) 1870 setupMockHttpResponse(t, mockClient, "TestAllocation_listDir", testCaseName, a, http.MethodGet, http.StatusOK, body) 1871 return nil 1872 }, 1873 }, 1874 } 1875 1876 for _, tt := range tests { 1877 t.Run(tt.name, func(t *testing.T) { 1878 var mockClient = mocks.HttpClient{} 1879 zboxutil.Client = &mockClient 1880 1881 client := zclient.GetClient() 1882 client.Wallet = &zcncrypto.Wallet{ 1883 ClientID: mockClientId, 1884 ClientKey: mockClientKey, 1885 } 1886 1887 require := require.New(t) 1888 a := &Allocation{ 1889 ID: mockAllocationId, 1890 Tx: mockAllocationTxId, 1891 FileOptions: 63, 1892 DataShards: 2, 1893 ParityShards: 2, 1894 } 1895 if tt.parameters.expectedResult != nil { 1896 tt.parameters.expectedResult.deleteMask = zboxutil.NewUint128(1).Lsh(uint64(a.DataShards + a.ParityShards)).Sub64(1) 1897 } 1898 a.InitAllocation() 1899 sdkInitialized = true 1900 for i := 0; i < numBlobbers; i++ { 1901 a.Blobbers = append(a.Blobbers, &blockchain.StorageNode{ 1902 ID: tt.name + mockBlobberId + strconv.Itoa(i), 1903 Baseurl: "TestAllocation_listDir" + tt.name + mockBlobberUrl + strconv.Itoa(i), 1904 }) 1905 } 1906 if tt.setup != nil { 1907 if teardown := tt.setup(t, tt.name, a, &mockClient); teardown != nil { 1908 defer teardown(t) 1909 } 1910 } 1911 got, err := a.ListDir(tt.parameters.path) 1912 require.EqualValues(tt.wantErr, err != nil) 1913 if err != nil { 1914 require.EqualValues(tt.errMsg, errors.Top(err)) 1915 return 1916 } 1917 require.NoErrorf(err, "unexpected error: %v", err) 1918 require.EqualValues(tt.parameters.expectedResult, got) 1919 }) 1920 } 1921 } 1922 1923 func TestAllocation_GetFileMetaFromAuthTicket(t *testing.T) { 1924 const ( 1925 mockLookupHash = "mock lookup hash" 1926 mockActualHash = "mockActualHash" 1927 mockType = "f" 1928 ) 1929 1930 var authTicket = getMockAuthTicket(t) 1931 1932 type parameters struct { 1933 authTicket, lookupHash string 1934 } 1935 tests := []struct { 1936 name string 1937 parameters parameters 1938 setup func(*testing.T, string, *Allocation, *mocks.HttpClient) (teardown func(t *testing.T)) 1939 wantErr bool 1940 errMsg string 1941 }{ 1942 { 1943 name: "Test_Uninitialized_Failed", 1944 setup: func(t *testing.T, testCaseName string, a *Allocation, _ *mocks.HttpClient) (teardown func(t *testing.T)) { 1945 a.initialized = false 1946 return func(t *testing.T) { 1947 a.initialized = true 1948 } 1949 }, 1950 wantErr: true, 1951 errMsg: "sdk_not_initialized: Please call InitStorageSDK Init and use GetAllocation to get the allocation object", 1952 }, 1953 { 1954 name: "Test_Cannot_Decode_Auth_Ticket_Failed", 1955 parameters: parameters{ 1956 authTicket: "some wrong auth ticket to decode", 1957 }, 1958 wantErr: true, 1959 errMsg: "auth_ticket_decode_error: Error decoding the auth ticket.illegal base64 data at input byte 4", 1960 }, 1961 { 1962 name: "Test_Cannot_Unmarshal_Auth_Ticket_Failed", 1963 parameters: parameters{ 1964 authTicket: "c29tZSB3cm9uZyBhdXRoIHRpY2tldCB0byBtYXJzaGFs", 1965 }, 1966 wantErr: true, 1967 errMsg: "auth_ticket_decode_error: Error unmarshaling the auth ticket.invalid character 's' looking for beginning of value", 1968 }, 1969 { 1970 name: "Test_Wrong_Auth_Ticket_File_Path_Hash_Or_Lookup_Hash_Failed", 1971 parameters: parameters{ 1972 authTicket: authTicket, 1973 lookupHash: "", 1974 }, 1975 wantErr: true, 1976 errMsg: "invalid_path: Invalid path for the list", 1977 }, 1978 { 1979 name: "Test_Error_Get_File_Meta_From_Blobbers_Failed", 1980 parameters: parameters{ 1981 authTicket: authTicket, 1982 lookupHash: mockLookupHash, 1983 }, 1984 wantErr: true, 1985 errMsg: "file_meta_error: Error getting the file meta data from blobbers", 1986 }, 1987 { 1988 name: "Test_Success", 1989 parameters: parameters{ 1990 authTicket: authTicket, 1991 lookupHash: mockLookupHash, 1992 }, 1993 setup: func(t *testing.T, testCaseName string, a *Allocation, mockClient *mocks.HttpClient) (teardown func(t *testing.T)) { 1994 for i := 0; i < numBlobbers; i++ { 1995 a.Blobbers = append(a.Blobbers, &blockchain.StorageNode{ 1996 ID: testCaseName + mockBlobberId + strconv.Itoa(i), 1997 Baseurl: "TestAllocation_GetFileMetaFromAuthTicket" + testCaseName + mockBlobberUrl + strconv.Itoa(i), 1998 }) 1999 } 2000 body, err := json.Marshal(&fileref.FileRef{ 2001 ActualFileHash: mockActualHash, 2002 }) 2003 require.NoError(t, err) 2004 setupMockHttpResponse(t, mockClient, "TestAllocation_GetFileMetaFromAuthTicket", testCaseName, a, http.MethodPost, http.StatusOK, body) 2005 return nil 2006 }, 2007 }, 2008 } 2009 2010 for _, tt := range tests { 2011 t.Run(tt.name, func(t *testing.T) { 2012 var mockClient = mocks.HttpClient{} 2013 zboxutil.Client = &mockClient 2014 2015 client := zclient.GetClient() 2016 client.Wallet = &zcncrypto.Wallet{ 2017 ClientID: mockClientId, 2018 ClientKey: mockClientKey, 2019 } 2020 2021 a := &Allocation{ 2022 ID: mockAllocationId, 2023 Tx: mockAllocationTxId, 2024 DataShards: 2, 2025 ParityShards: 2, 2026 FileOptions: 63, 2027 } 2028 a.InitAllocation() 2029 sdkInitialized = true 2030 a.initialized = true 2031 2032 require := require.New(t) 2033 if tt.setup != nil { 2034 if teardown := tt.setup(t, tt.name, a, &mockClient); teardown != nil { 2035 defer teardown(t) 2036 } 2037 } 2038 got, err := a.GetFileMetaFromAuthTicket(tt.parameters.authTicket, tt.parameters.lookupHash) 2039 require.EqualValues(tt.wantErr, err != nil) 2040 if err != nil { 2041 require.EqualValues(tt.errMsg, errors.Top(err)) 2042 return 2043 } 2044 require.NoErrorf(err, "unexpected error: %v", err) 2045 expectedResult := &ConsolidatedFileMeta{ 2046 Hash: mockActualHash, 2047 } 2048 require.EqualValues(expectedResult, got) 2049 }) 2050 } 2051 } 2052 2053 func TestAllocation_DownloadToFileHandlerFromAuthTicket(t *testing.T) { 2054 const ( 2055 mockLookupHash = "mock lookup hash" 2056 mockRemoteFilePath = "1.txt" 2057 mockType = "d" 2058 ) 2059 2060 var mockFile = &sys.MemFile{Name: "mockFile", Mode: fs.ModePerm, ModTime: time.Now()} 2061 var mockClient = mocks.HttpClient{} 2062 zboxutil.Client = &mockClient 2063 2064 client := zclient.GetClient() 2065 client.Wallet = &zcncrypto.Wallet{ 2066 ClientID: mockClientId, 2067 ClientKey: mockClientKey, 2068 } 2069 2070 require := require.New(t) 2071 2072 a := &Allocation{} 2073 setupMockAllocation(t, a) 2074 for i := 0; i < numBlobbers; i++ { 2075 a.Blobbers = append(a.Blobbers, &blockchain.StorageNode{ 2076 ID: strconv.Itoa(i), 2077 Baseurl: "TestAllocation_DownloadFromAuthTicket" + mockBlobberUrl + strconv.Itoa(i), 2078 }) 2079 } 2080 2081 var authTicket = getMockAuthTicket(t) 2082 2083 err := a.DownloadFileToFileHandlerFromAuthTicket(mockFile, authTicket, mockLookupHash, 2084 mockRemoteFilePath, false, nil, false) 2085 defer os.Remove("alloc/1.txt") 2086 require.NoErrorf(err, "unexpected error: %v", err) 2087 } 2088 2089 func TestAllocation_DownloadThumbnailFromAuthTicket(t *testing.T) { 2090 const ( 2091 mockLookupHash = "mock lookup hash" 2092 mockLocalPath = "alloc" 2093 mockRemoteFilePath = "1.txt" 2094 mockType = "d" 2095 ) 2096 2097 var mockClient = mocks.HttpClient{} 2098 zboxutil.Client = &mockClient 2099 2100 client := zclient.GetClient() 2101 client.Wallet = &zcncrypto.Wallet{ 2102 ClientID: mockClientId, 2103 ClientKey: mockClientKey, 2104 } 2105 2106 require := require.New(t) 2107 2108 a := &Allocation{} 2109 setupMockAllocation(t, a) 2110 for i := 0; i < numBlobbers; i++ { 2111 a.Blobbers = append(a.Blobbers, &blockchain.StorageNode{ 2112 ID: strconv.Itoa(i), 2113 Baseurl: "TestAllocation_DownloadThumbnailFromAuthTicket" + mockBlobberUrl + strconv.Itoa(i), 2114 }) 2115 } 2116 2117 var authTicket = getMockAuthTicket(t) 2118 2119 body, err := json.Marshal(&fileref.ReferencePath{ 2120 Meta: map[string]interface{}{ 2121 "type": mockType, 2122 }, 2123 }) 2124 require.NoError(err) 2125 setupMockHttpResponse(t, &mockClient, "TestAllocation_DownloadThumbnailFromAuthTicket", "", a, http.MethodGet, http.StatusOK, body) 2126 2127 err = a.DownloadThumbnailFromAuthTicket(mockLocalPath, authTicket, mockLookupHash, mockRemoteFilePath, true, nil, false) 2128 defer os.Remove("alloc/1.txt") 2129 require.NoErrorf(err, "unexpected error: %v", err) 2130 } 2131 2132 func TestAllocation_DownloadFromAuthTicket(t *testing.T) { 2133 const ( 2134 mockLookupHash = "mock lookup hash" 2135 mockLocalPath = "alloc" 2136 mockRemoteFilePath = "1.txt" 2137 mockType = "d" 2138 ) 2139 2140 var mockClient = mocks.HttpClient{} 2141 zboxutil.Client = &mockClient 2142 2143 client := zclient.GetClient() 2144 client.Wallet = &zcncrypto.Wallet{ 2145 ClientID: mockClientId, 2146 ClientKey: mockClientKey, 2147 } 2148 2149 require := require.New(t) 2150 2151 a := &Allocation{} 2152 setupMockAllocation(t, a) 2153 for i := 0; i < numBlobbers; i++ { 2154 a.Blobbers = append(a.Blobbers, &blockchain.StorageNode{ 2155 ID: strconv.Itoa(i), 2156 Baseurl: "TestAllocation_DownloadFromAuthTicket" + mockBlobberUrl + strconv.Itoa(i), 2157 }) 2158 } 2159 2160 var authTicket = getMockAuthTicket(t) 2161 2162 err := a.DownloadFromAuthTicket(mockLocalPath, authTicket, mockLookupHash, mockRemoteFilePath, true, nil, false) 2163 defer os.Remove("alloc/1.txt") 2164 require.NoErrorf(err, "unexpected error: %v", err) 2165 } 2166 2167 func TestAllocation_DownloadFromAuthTicketByBlocks(t *testing.T) { 2168 const ( 2169 mockLookupHash = "mock lookup hash" 2170 mockLocalPath = "alloc" 2171 mockRemoteFilePath = "1.txt" 2172 mockType = "d" 2173 ) 2174 2175 var authTicket = getMockAuthTicket(t) 2176 2177 var mockClient = mocks.HttpClient{} 2178 zboxutil.Client = &mockClient 2179 2180 client := zclient.GetClient() 2181 client.Wallet = &zcncrypto.Wallet{ 2182 ClientID: mockClientId, 2183 ClientKey: mockClientKey, 2184 } 2185 2186 require := require.New(t) 2187 2188 a := &Allocation{} 2189 setupMockAllocation(t, a) 2190 for i := 0; i < numBlobbers; i++ { 2191 a.Blobbers = append(a.Blobbers, &blockchain.StorageNode{ 2192 ID: strconv.Itoa(i), 2193 Baseurl: "TestAllocation_DownloadFromAuthTicketByBlocks" + mockBlobberUrl + strconv.Itoa(i), 2194 }) 2195 } 2196 2197 setupMockHttpResponse(t, &mockClient, "TestAllocation_DownloadFromAuthTicketByBlocks", "", a, http.MethodPost, http.StatusBadRequest, []byte("")) 2198 2199 err := a.DownloadFromAuthTicketByBlocks( 2200 mockLocalPath, authTicket, 1, 0, numBlockDownloads, mockLookupHash, mockRemoteFilePath, true, nil, false) 2201 defer os.Remove("alloc/1.txt") 2202 require.NoErrorf(err, "unexpected error: %v", err) 2203 } 2204 2205 func TestAllocation_StartRepair(t *testing.T) { 2206 const ( 2207 mockLookupHash = "mock lookup hash" 2208 mockLocalPath = "alloc" 2209 mockPathToRepair = "/1.txt" 2210 mockType = "d" 2211 ) 2212 2213 var mockClient = mocks.HttpClient{} 2214 zboxutil.Client = &mockClient 2215 2216 client := zclient.GetClient() 2217 client.Wallet = &zcncrypto.Wallet{ 2218 ClientID: mockClientId, 2219 ClientKey: mockClientKey, 2220 } 2221 2222 type parameters struct { 2223 localPath, pathToRepair string 2224 statusCallback StatusCallback 2225 } 2226 tests := []struct { 2227 name string 2228 parameters parameters 2229 setup func(*testing.T, string, *Allocation) (teardown func(*testing.T)) 2230 wantErr bool 2231 errMsg string 2232 }{ 2233 { 2234 name: "Test_Uninitialized_Failed", 2235 setup: func(t *testing.T, testCaseName string, a *Allocation) (teardown func(t *testing.T)) { 2236 a.initialized = false 2237 return func(t *testing.T) { 2238 a.initialized = true 2239 } 2240 }, 2241 wantErr: true, 2242 errMsg: "sdk_not_initialized: Please call InitStorageSDK Init and use GetAllocation to get the allocation object", 2243 }, 2244 { 2245 name: "Test_Repair_Success", 2246 parameters: parameters{ 2247 localPath: mockLocalPath, 2248 pathToRepair: "/1.txt", 2249 }, 2250 setup: func(t *testing.T, testCaseName string, a *Allocation) (teardown func(t *testing.T)) { 2251 body, err := json.Marshal(&fileref.ListResult{ 2252 AllocationRoot: mockAllocationRoot, 2253 Meta: map[string]interface{}{ 2254 "type": mockType, 2255 }, 2256 }) 2257 require.NoError(t, err) 2258 setupMockHttpResponse(t, &mockClient, "TestAllocation_StartRepair", testCaseName, a, http.MethodGet, http.StatusOK, body) 2259 return nil 2260 }, 2261 }, 2262 } 2263 for _, tt := range tests { 2264 t.Run(tt.name, func(t *testing.T) { 2265 require := require.New(t) 2266 a := &Allocation{ 2267 DataShards: 2, 2268 ParityShards: 2, 2269 } 2270 setupMockAllocation(t, a) 2271 for i := 0; i < numBlobbers; i++ { 2272 a.Blobbers = append(a.Blobbers, &blockchain.StorageNode{ 2273 ID: tt.name + mockBlobberId + strconv.Itoa(i), 2274 Baseurl: "TestAllocation_StartRepair" + tt.name + mockBlobberUrl + strconv.Itoa(i), 2275 }) 2276 } 2277 if tt.setup != nil { 2278 if teardown := tt.setup(t, tt.name, a); teardown != nil { 2279 defer teardown(t) 2280 } 2281 } 2282 err := a.StartRepair(tt.parameters.localPath, tt.parameters.pathToRepair, tt.parameters.statusCallback) 2283 require.EqualValues(tt.wantErr, err != nil) 2284 if err != nil { 2285 require.EqualValues(tt.errMsg, errors.Top(err)) 2286 return 2287 } 2288 require.NoErrorf(err, "unexpected error: %v", err) 2289 }) 2290 } 2291 } 2292 2293 func TestAllocation_CancelRepair(t *testing.T) { 2294 tests := []struct { 2295 name string 2296 setup func(*testing.T, *Allocation) (teardown func(*testing.T)) 2297 wantErr bool 2298 errMsg string 2299 }{ 2300 { 2301 name: "Test_Failed", 2302 wantErr: true, 2303 errMsg: "invalid_cancel_repair_request: No repair in progress for the allocation", 2304 }, 2305 { 2306 name: "Test_Success", 2307 setup: func(t *testing.T, a *Allocation) (teardown func(t *testing.T)) { 2308 a.repairRequestInProgress = &RepairRequest{} 2309 return nil 2310 }, 2311 }, 2312 } 2313 for _, tt := range tests { 2314 t.Run(tt.name, func(t *testing.T) { 2315 require := require.New(t) 2316 a := &Allocation{} 2317 setupMockAllocation(t, a) 2318 if tt.setup != nil { 2319 if teardown := tt.setup(t, a); teardown != nil { 2320 defer teardown(t) 2321 } 2322 } 2323 err := a.CancelRepair() 2324 require.EqualValues(tt.wantErr, err != nil) 2325 if err != nil { 2326 require.EqualValues(tt.errMsg, errors.Top(err)) 2327 return 2328 } 2329 require.NoErrorf(err, "unexpected error: %v", err) 2330 }) 2331 } 2332 } 2333 2334 func setupMockAllocation(t *testing.T, a *Allocation) { 2335 a.downloadChan = make(chan *DownloadRequest, 10) 2336 a.repairChan = make(chan *RepairRequest, 1) 2337 a.ctx, a.ctxCancelF = context.WithCancel(context.Background()) 2338 a.downloadProgressMap = make(map[string]*DownloadRequest) 2339 a.mutex = &sync.Mutex{} 2340 a.FileOptions = uint16(63) // 0011 1111 All allowed 2341 InitCommitWorker(a.Blobbers) 2342 a.initialized = true 2343 if a.DataShards != 0 { 2344 a.fullconsensus, a.consensusThreshold = a.getConsensuses() 2345 } 2346 sdkInitialized = true 2347 2348 go func() { 2349 for { 2350 select { 2351 case <-a.ctx.Done(): 2352 t.Log("Upload cancelled by the parent") 2353 return 2354 case downloadReq := <-a.downloadChan: 2355 remotePathCB := downloadReq.remotefilepath 2356 if remotePathCB == "" { 2357 remotePathCB = downloadReq.remotefilepathhash 2358 } 2359 if downloadReq.completedCallback != nil { 2360 downloadReq.completedCallback(downloadReq.remotefilepath, downloadReq.remotefilepathhash) 2361 } 2362 if downloadReq.statusCallback != nil { 2363 downloadReq.statusCallback.Completed(a.ID, remotePathCB, "1.txt", "application/octet-stream", 3, OpDownload) 2364 } 2365 t.Logf("received a download request for %v\n", downloadReq.remotefilepath) 2366 case repairReq := <-a.repairChan: 2367 if repairReq.completedCallback != nil { 2368 repairReq.completedCallback() 2369 } 2370 if repairReq.wg != nil { 2371 repairReq.wg.Done() 2372 } 2373 t.Logf("received a repair request for %v\n", repairReq.listDir.Path) 2374 } 2375 } 2376 }() 2377 } 2378 2379 func setupMockGetFileInfoResponse(t *testing.T, mockClient *mocks.HttpClient) { 2380 httpResponse := http.Response{ 2381 StatusCode: http.StatusOK, 2382 Body: func() io.ReadCloser { 2383 jsonFR, err := json.Marshal(fileref.FileRef{ 2384 Ref: fileref.Ref{ 2385 Name: mockFileRefName, 2386 }, 2387 ValidationRoot: "mock validation root", 2388 }) 2389 require.NoError(t, err) 2390 return ioutil.NopCloser(bytes.NewReader([]byte(jsonFR))) 2391 }(), 2392 } 2393 for i := 0; i < numBlobbers; i++ { 2394 mockClient.On("Do", mock.Anything).Return(&httpResponse, nil) 2395 } 2396 } 2397 2398 func getMockAuthTicket(t *testing.T) string { 2399 var mockClient = mocks.HttpClient{} 2400 zboxutil.Client = &mockClient 2401 2402 client := zclient.GetClient() 2403 client.Wallet = &zcncrypto.Wallet{ 2404 ClientID: mockClientId, 2405 ClientKey: mockClientKey, 2406 } 2407 a := &Allocation{ 2408 ID: mockAllocationId, 2409 Tx: mockAllocationTxId, 2410 DataShards: 1, 2411 ParityShards: 1, 2412 FileOptions: 63, 2413 } 2414 2415 a.InitAllocation() 2416 sdkInitialized = true 2417 for i := 0; i < numBlobbers; i++ { 2418 a.Blobbers = append(a.Blobbers, &blockchain.StorageNode{ 2419 ID: strconv.Itoa(i), 2420 Baseurl: "TestAllocation_getMockAuthTicket" + mockBlobberUrl + strconv.Itoa(i), 2421 }) 2422 } 2423 2424 jsonFR, err := json.Marshal(fileref.FileRef{ 2425 Ref: fileref.Ref{ 2426 Name: mockFileRefName, 2427 }, 2428 ValidationRoot: "mock validation root", 2429 EncryptedKey: "encrypted key", 2430 }) 2431 require.NoError(t, err) 2432 2433 httpResponse := &http.Response{ 2434 StatusCode: http.StatusOK, 2435 Body: func() io.ReadCloser { 2436 return ioutil.NopCloser(bytes.NewReader(jsonFR)) 2437 }(), 2438 } 2439 2440 setupMockGetFileMetaResponse(t, &mockClient, "TestAllocation_getMockAuthTicket", "", a, http.MethodPost, http.StatusOK, jsonFR) 2441 2442 for i := 0; i < numBlobbers; i++ { 2443 mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 2444 return strings.HasPrefix(req.URL.Path, "TestAllocation_ListDirFromAuthTicket") 2445 })).Return(httpResponse, nil) 2446 2447 mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 2448 return strings.HasPrefix(req.URL.Path, "TestAllocation_getMockAuthTicket") 2449 })).Return(httpResponse, nil) 2450 } 2451 2452 authTicket, err := a.GetAuthTicket("/1.txt", "1.txt", fileref.FILE, mockClientId, "", 0, nil) 2453 require.NoErrorf(t, err, "unexpected get auth ticket error: %v", err) 2454 require.NotEmptyf(t, authTicket, "unexpected empty auth ticket") 2455 return authTicket 2456 }