github.com/0chain/gosdk@v1.17.11/zboxcore/sdk/allocation_file_test.go (about) 1 package sdk 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/json" 7 "io" 8 "io/ioutil" 9 "net/http" 10 "os" 11 "strconv" 12 "strings" 13 "sync" 14 "testing" 15 "time" 16 17 "github.com/0chain/errors" 18 "github.com/0chain/gosdk/core/common" 19 "github.com/0chain/gosdk/core/pathutil" 20 "github.com/0chain/gosdk/core/resty" 21 "github.com/0chain/gosdk/core/zcncrypto" 22 "github.com/hitenjain14/fasthttp" 23 24 "github.com/0chain/gosdk/zboxcore/blockchain" 25 "github.com/0chain/gosdk/zboxcore/client" 26 zclient "github.com/0chain/gosdk/zboxcore/client" 27 "github.com/0chain/gosdk/zboxcore/fileref" 28 "github.com/0chain/gosdk/zboxcore/mocks" 29 "github.com/0chain/gosdk/zboxcore/zboxutil" 30 "github.com/stretchr/testify/mock" 31 "github.com/stretchr/testify/require" 32 ) 33 34 func setupHttpResponses( 35 t *testing.T, mockClient *mocks.HttpClient, fastMock *mocks.FastClient, allocID string, 36 refsInput, fileMetaInput []byte, hashes []string, 37 numBlobbers, numCorrect int, isUpdate bool) { 38 39 walletJSON := `{"client_id":"00d2d56d0d573329fe61b8252a4b1715f93fac15176e5d90c413bc92a42e498b","client_key":"000b47144eb0366c3039bca10bc6df3ac289d8823de14ffc08cfdfe83f03e4079ab94bdc3932e7e9bc053f38834c7da63ce6f9c6e540d93cf0c52ba4149f2280","keys":[{"public_key":"000b47144eb0366c3039bca10bc6df3ac289d8823de14ffc08cfdfe83f03e4079ab94bdc3932e7e9bc053f38834c7da63ce6f9c6e540d93cf0c52ba4149f2280","private_key":"77a7faf0dcc1865a475963fee7ce71ca6dc6a20198209eb75d9fc1dc9df41f0f"}],"mnemonics":"mistake alone lumber swamp tape device flight oppose room combine useful typical deal lion device hope glad once million pudding artist brush sing vicious","version":"1.0","date_created":"2024-03-11T20:06:33+05:30","nonce":0}` 40 client.PopulateClient(walletJSON, "bls0chain") //nolint:errcheck 41 42 for i := 0; i < numBlobbers; i++ { 43 metaBlobberBase := t.Name() + "/" + mockBlobberUrl + strconv.Itoa(i) + zboxutil.FILE_META_ENDPOINT 44 refsBlobberBase := t.Name() + "/" + mockBlobberUrl + strconv.Itoa(i) + zboxutil.REFS_ENDPOINT 45 uploadBlobberBase := t.Name() + "/" + mockBlobberUrl + strconv.Itoa(i) + zboxutil.UPLOAD_ENDPOINT 46 wmBlobberBase := t.Name() + "/" + mockBlobberUrl + strconv.Itoa(i) + zboxutil.WM_LOCK_ENDPOINT 47 commitBlobberBase := t.Name() + "/" + mockBlobberUrl + strconv.Itoa(i) + zboxutil.COMMIT_ENDPOINT 48 refPathBlobberBase := t.Name() + "/" + mockBlobberUrl + strconv.Itoa(i) + zboxutil.REFERENCE_ENDPOINT 49 latestMarkerBlobberBase := t.Name() + "/" + mockBlobberUrl + strconv.Itoa(i) + zboxutil.LATEST_WRITE_MARKER_ENDPOINT 50 rollbackBlobberBase := t.Name() + "/" + mockBlobberUrl + strconv.Itoa(i) + zboxutil.ROLLBACK_ENDPOINT 51 52 mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 53 return req.Method == "POST" && 54 strings.Contains(req.URL.String(), metaBlobberBase) 55 })).Return(&http.Response{ 56 StatusCode: func() int { 57 if i < numCorrect { 58 return http.StatusOK 59 } 60 return http.StatusBadRequest 61 }(), 62 Body: ioutil.NopCloser(bytes.NewReader(fileMetaInput)), 63 }, nil) 64 65 mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 66 return req.Method == "GET" && 67 strings.Contains(req.URL.String(), refsBlobberBase) 68 })).Return(&http.Response{ 69 StatusCode: func() int { 70 if i < numCorrect { 71 return http.StatusOK 72 } 73 return http.StatusBadRequest 74 }(), 75 Body: ioutil.NopCloser(bytes.NewReader(refsInput)), 76 }, nil) 77 78 mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 79 if isUpdate { 80 return req.Method == "PUT" && 81 strings.Contains(req.URL.String(), uploadBlobberBase) 82 } 83 return req.Method == "POST" && 84 strings.Contains(req.URL.String(), uploadBlobberBase) 85 86 })).Return(&http.Response{ 87 StatusCode: func() int { 88 if i < numCorrect { 89 return http.StatusOK 90 } 91 return http.StatusBadRequest 92 }(), 93 Body: func() io.ReadCloser { 94 hash := hashes[i] 95 r := UploadResult{ 96 Filename: "1.txt", 97 Hash: hash, 98 } 99 b, _ := json.Marshal(r) 100 return io.NopCloser(bytes.NewReader(b)) 101 }(), 102 }, nil) 103 j := i 104 fastMock.On("DoTimeout", mock.AnythingOfType("*fasthttp.Request"), mock.AnythingOfType("*fasthttp.Response"), mock.AnythingOfType("time.Duration")).Run(func(args mock.Arguments) { 105 resp := args.Get(1).(*fasthttp.Response) 106 if j < numCorrect { 107 resp.Header.SetStatusCode(http.StatusOK) 108 } else { 109 resp.Header.SetStatusCode(http.StatusBadRequest) 110 } 111 hash := hashes[j] 112 r := UploadResult{ 113 Filename: "1.txt", 114 Hash: hash, 115 } 116 b, _ := json.Marshal(r) 117 resp.SetBodyRaw(b) 118 }).Return(nil) 119 120 mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 121 return req.Method == "POST" && 122 strings.Contains(req.URL.String(), wmBlobberBase) 123 })).Return(&http.Response{ 124 StatusCode: func() int { 125 if i < numCorrect { 126 return http.StatusOK 127 } 128 return http.StatusBadRequest 129 }(), 130 Body: ioutil.NopCloser(bytes.NewReader([]byte(`{"status":2}`))), 131 }, nil) 132 133 mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 134 return req.Method == "GET" && 135 strings.Contains(req.URL.String(), refPathBlobberBase) 136 })).Return(&http.Response{ 137 StatusCode: func() int { 138 if i < numCorrect { 139 return http.StatusOK 140 } 141 return http.StatusBadRequest 142 }(), 143 Body: func() io.ReadCloser { 144 s := `{"meta_data":{"chunk_size":0,"created_at":0,"hash":"","lookup_hash":"","name":"/","num_of_blocks":0,"path":"/","path_hash":"","size":0,"type":"d","updated_at":0},"Ref":{"ID":0,"Type":"d","AllocationID":"` + allocID + `","LookupHash":"","Name":"/","Path":"/","Hash":"","NumBlocks":0,"PathHash":"","ParentPath":"","PathLevel":1,"CustomMeta":"","ContentHash":"","Size":0,"MerkleRoot":"","ActualFileSize":0,"ActualFileHash":"","MimeType":"","WriteMarker":"","ThumbnailSize":0,"ThumbnailHash":"","ActualThumbnailSize":0,"ActualThumbnailHash":"","EncryptedKey":"","Children":null,"OnCloud":false,"CreatedAt":0,"UpdatedAt":0,"ChunkSize":0},"list":[{"meta_data":{"chunk_size":0,"created_at":0,"hash":"","lookup_hash":"","name":"1.txt","num_of_blocks":0,"path":"/1.txt","path_hash":"","size":0,"type":"f","updated_at":0},"Ref":{"ID":0,"Type":"f","AllocationID":"` + allocID + `","LookupHash":"","Name":"1.txt","Path":"/1.txt","Hash":"","NumBlocks":0,"PathHash":"","ParentPath":"/","PathLevel":1,"CustomMeta":"","ContentHash":"","Size":0,"MerkleRoot":"","ActualFileSize":0,"ActualFileHash":"","MimeType":"","WriteMarker":"","ThumbnailSize":0,"ThumbnailHash":"","ActualThumbnailSize":0,"ActualThumbnailHash":"","EncryptedKey":"","Children":null,"OnCloud":false,"CreatedAt":0,"UpdatedAt":0,"ChunkSize":0}}],"latest_write_marker":null}` 145 return ioutil.NopCloser(bytes.NewReader([]byte(s))) 146 }(), 147 }, nil) 148 149 mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 150 return req.Method == "GET" && 151 strings.Contains(req.URL.String(), latestMarkerBlobberBase) 152 })).Return(&http.Response{ 153 StatusCode: func() int { 154 if i < numCorrect { 155 return http.StatusOK 156 } 157 return http.StatusBadRequest 158 }(), 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 mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 166 return req.Method == "POST" && 167 strings.Contains(req.URL.String(), commitBlobberBase) 168 })).Return(&http.Response{ 169 StatusCode: func() int { 170 if i < numCorrect { 171 return http.StatusOK 172 } 173 return http.StatusBadRequest 174 }(), 175 Body: ioutil.NopCloser(bytes.NewReader(nil)), 176 }, nil) 177 178 mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 179 return req.Method == "POST" && 180 strings.Contains(req.URL.String(), rollbackBlobberBase) 181 })).Return(&http.Response{ 182 StatusCode: func() int { 183 if i < numCorrect { 184 return http.StatusOK 185 } 186 return http.StatusBadRequest 187 }(), 188 Body: ioutil.NopCloser(bytes.NewReader(nil)), 189 }, nil) 190 191 mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 192 return req.Method == "DELETE" && 193 strings.Contains(req.URL.String(), wmBlobberBase) 194 })).Return(&http.Response{ 195 StatusCode: func() int { 196 if i < numCorrect { 197 return http.StatusOK 198 } 199 return http.StatusBadRequest 200 }(), 201 }, nil) 202 } 203 } 204 205 func TestAllocation_UpdateFile(t *testing.T) { 206 mockClient := mocks.HttpClient{} 207 zboxutil.Client = &mockClient 208 mockFastClient := mocks.FastClient{} 209 zboxutil.FastHttpClient = &mockFastClient 210 211 const mockLocalPath = "1.txt" 212 213 a := &Allocation{ 214 ID: "TestAllocation_UpdateFile", 215 Tx: "TestAllocation_UpdateFile", 216 ParityShards: 2, 217 DataShards: 2, 218 Size: 2 * GB, 219 } 220 setupMockAllocation(t, a) 221 222 for i := 0; i < numBlobbers; i++ { 223 a.Blobbers = append(a.Blobbers, &blockchain.StorageNode{ 224 ID: mockBlobberId + strconv.Itoa(i), 225 Baseurl: "http://" + t.Name() + "/" + mockBlobberUrl + strconv.Itoa(i), 226 }) 227 } 228 229 teardown := setupMockFile(t, mockLocalPath) 230 defer teardown(t) 231 232 refsInput := map[string]interface{}{ 233 "total_pages": 1, 234 "refs": []map[string]interface{}{ 235 { 236 "file_id": "2", 237 "type": "f", 238 "allocation_id": a.ID, 239 "lookup_hash": "lookup_hash", 240 "name": mockLocalPath, 241 "path": pathutil.Join("/", mockLocalPath), 242 "path_hash": "path_hash", 243 "parent_path": "/", 244 "level": 1, 245 "size": 65536, 246 "actual_file_size": 65536 * int64(len(a.Blobbers)), 247 "actual_file_hash": "actual_file_hash", 248 "created_at": common.Timestamp(time.Now().Unix()), 249 "updated_at": common.Timestamp(time.Now().Unix()), 250 "id": 3, 251 }, 252 }, 253 } 254 255 resfsIn, err := json.Marshal(refsInput) 256 require.NoError(t, err) 257 258 fileMetaIn := []byte("{\"actual_file_size\":1}") 259 260 hashes := []string{ 261 "5c84c73878159775992d20425c13bafc8bc10515c40e0365dde068626918fceb", 262 "f8d78ca33bd3c532f4d9c56bcd969944b61350c1be64df22f9353f359e3a8ba4", 263 "f435a42af309218e88196d4ed2e0c1977a701641b06434be0bb0263099f3faa9", 264 "6b3e932bfd2b2c09e39d35e7c4928c42b73bee194045e545560229234d695669", 265 } 266 setupHttpResponses(t, &mockClient, &mockFastClient, a.ID, resfsIn, fileMetaIn, hashes, len(a.Blobbers), len(a.Blobbers), true) 267 268 err = a.UpdateFile(os.TempDir(), mockLocalPath, "/", nil) 269 require.NoErrorf(t, err, "Unexpected error %v", err) 270 } 271 272 func TestAllocation_UploadFile(t *testing.T) { 273 mockClient := mocks.HttpClient{} 274 zboxutil.Client = &mockClient 275 mockFastClient := mocks.FastClient{} 276 zboxutil.FastHttpClient = &mockFastClient 277 278 const mockLocalPath = "1.txt" 279 require := require.New(t) 280 if teardown := setupMockFile(t, mockLocalPath); teardown != nil { 281 defer teardown(t) 282 } 283 a := &Allocation{ 284 Tx: "TestAllocation_UploadFile", 285 ParityShards: 2, 286 DataShards: 2, 287 Size: 2 * GB, 288 } 289 290 setupMockAllocation(t, a) 291 for i := 0; i < numBlobbers; i++ { 292 a.Blobbers = append(a.Blobbers, &blockchain.StorageNode{ 293 ID: mockBlobberId + strconv.Itoa(i), 294 Baseurl: "http://" + t.Name() + "/" + mockBlobberUrl + strconv.Itoa(i), 295 }) 296 } 297 hashes := []string{ 298 "5c84c73878159775992d20425c13bafc8bc10515c40e0365dde068626918fceb", 299 "f8d78ca33bd3c532f4d9c56bcd969944b61350c1be64df22f9353f359e3a8ba4", 300 "f435a42af309218e88196d4ed2e0c1977a701641b06434be0bb0263099f3faa9", 301 "6b3e932bfd2b2c09e39d35e7c4928c42b73bee194045e545560229234d695669", 302 } 303 setupHttpResponses(t, &mockClient, &mockFastClient, a.ID, nil, nil, hashes, len(a.Blobbers), len(a.Blobbers), false) 304 305 err := a.UploadFile(os.TempDir(), mockLocalPath, "/", nil) 306 require.NoErrorf(err, "Unexpected error %v", err) 307 } 308 309 func TestAllocation_UpdateFileWithThumbnail(t *testing.T) { 310 const ( 311 mockLocalPath = "1.txt" 312 mockThumbnailPath = "thumbnail_alloc" 313 ) 314 315 mockClient := mocks.HttpClient{} 316 zboxutil.Client = &mockClient 317 mockFastClient := mocks.FastClient{} 318 zboxutil.FastHttpClient = &mockFastClient 319 320 a := &Allocation{ 321 ID: "TestAllocation_UpdateFile_WithThumbNail", 322 Tx: "TestAllocation_UpdateFile_WithThumbNail", 323 ParityShards: 2, 324 DataShards: 2, 325 Size: 2 * GB, 326 } 327 setupMockAllocation(t, a) 328 329 for i := 0; i < numBlobbers; i++ { 330 a.Blobbers = append(a.Blobbers, &blockchain.StorageNode{ 331 ID: mockBlobberId + strconv.Itoa(i), 332 Baseurl: "http://" + t.Name() + "/" + mockBlobberUrl + strconv.Itoa(i), 333 }) 334 } 335 336 teardown1 := setupMockFile(t, mockLocalPath) 337 defer teardown1(t) 338 teardown2 := setupMockFile(t, mockThumbnailPath) 339 defer teardown2(t) 340 341 refsInput := map[string]interface{}{ 342 "total_pages": 1, 343 "refs": []map[string]interface{}{ 344 { 345 "file_id": "2", 346 "type": "f", 347 "allocation_id": a.ID, 348 "lookup_hash": "lookup_hash", 349 "name": mockLocalPath, 350 "path": pathutil.Join("/", mockLocalPath), 351 "path_hash": "path_hash", 352 "parent_path": "/", 353 "level": 1, 354 "size": 65536, 355 "actual_file_size": 65536 * int64(len(a.Blobbers)), 356 "actual_file_hash": "actual_file_hash", 357 "created_at": common.Timestamp(time.Now().Unix()), 358 "updated_at": common.Timestamp(time.Now().Unix()), 359 "id": 3, 360 }, 361 }, 362 } 363 364 resfsIn, err := json.Marshal(refsInput) 365 require.NoError(t, err) 366 367 fileMetaIn := []byte("{\"actual_file_size\":1}") 368 369 hashes := []string{ 370 "5c84c73878159775992d20425c13bafc8bc10515c40e0365dde068626918fceb", 371 "f8d78ca33bd3c532f4d9c56bcd969944b61350c1be64df22f9353f359e3a8ba4", 372 "f435a42af309218e88196d4ed2e0c1977a701641b06434be0bb0263099f3faa9", 373 "6b3e932bfd2b2c09e39d35e7c4928c42b73bee194045e545560229234d695669", 374 } 375 setupHttpResponses(t, &mockClient, &mockFastClient, a.ID, resfsIn, fileMetaIn, hashes, len(a.Blobbers), len(a.Blobbers), true) 376 377 err = a.UpdateFileWithThumbnail(os.TempDir(), mockLocalPath, "/", mockThumbnailPath, nil) 378 require.NoErrorf(t, err, "Unexpected error %v", err) 379 } 380 381 func TestAllocation_UploadFileWithThumbnail(t *testing.T) { 382 const ( 383 mockTmpPath = "/tmp" 384 mockLocalPath = "1.txt" 385 mockThumbnailPath = "thumbnail_alloc" 386 ) 387 388 mockClient := mocks.HttpClient{} 389 zboxutil.Client = &mockClient 390 mockFastClient := mocks.FastClient{} 391 zboxutil.FastHttpClient = &mockFastClient 392 393 if teardown := setupMockFile(t, mockLocalPath); teardown != nil { 394 defer teardown(t) 395 } 396 397 if teardown := setupMockFile(t, mockThumbnailPath); teardown != nil { 398 defer teardown(t) 399 } 400 a := &Allocation{ 401 Tx: "TestAllocation_UploadFileWithThumbnail", 402 ParityShards: 2, 403 DataShards: 2, 404 Size: 2 * GB, 405 } 406 407 setupMockAllocation(t, a) 408 for i := 0; i < numBlobbers; i++ { 409 a.Blobbers = append(a.Blobbers, &blockchain.StorageNode{ 410 ID: mockBlobberId + strconv.Itoa(i), 411 Baseurl: "http://" + t.Name() + "/" + mockBlobberUrl + strconv.Itoa(i), 412 }) 413 } 414 hashes := []string{ 415 "5c84c73878159775992d20425c13bafc8bc10515c40e0365dde068626918fceb", 416 "f8d78ca33bd3c532f4d9c56bcd969944b61350c1be64df22f9353f359e3a8ba4", 417 "f435a42af309218e88196d4ed2e0c1977a701641b06434be0bb0263099f3faa9", 418 "6b3e932bfd2b2c09e39d35e7c4928c42b73bee194045e545560229234d695669", 419 } 420 setupHttpResponses(t, &mockClient, &mockFastClient, a.ID, nil, nil, hashes, len(a.Blobbers), len(a.Blobbers), false) 421 422 err := a.UploadFileWithThumbnail(mockTmpPath, mockLocalPath, "/", mockThumbnailPath, nil) 423 require.NoErrorf(t, err, "Unexpected error %v", err) 424 } 425 426 func TestAllocation_EncryptAndUpdateFile(t *testing.T) { 427 mockClient := mocks.HttpClient{} 428 zboxutil.Client = &mockClient 429 mockFastClient := mocks.FastClient{} 430 zboxutil.FastHttpClient = &mockFastClient 431 432 const mockLocalPath = "1.txt" 433 434 a := &Allocation{ 435 ID: "TestAllocation_Encrypt_And_UpdateFile", 436 Tx: "TestAllocation_Encrypt_And_UpdateFile", 437 ParityShards: 2, 438 DataShards: 2, 439 Size: 2 * GB, 440 } 441 setupMockAllocation(t, a) 442 443 for i := 0; i < numBlobbers; i++ { 444 a.Blobbers = append(a.Blobbers, &blockchain.StorageNode{ 445 ID: mockBlobberId + strconv.Itoa(i), 446 Baseurl: "http://" + t.Name() + "/" + mockBlobberUrl + strconv.Itoa(i), 447 }) 448 } 449 450 teardown := setupMockFile(t, mockLocalPath) 451 defer teardown(t) 452 453 refsInput := map[string]interface{}{ 454 "total_pages": 1, 455 "refs": []map[string]interface{}{ 456 { 457 "file_id": "2", 458 "type": "f", 459 "allocation_id": a.ID, 460 "lookup_hash": "lookup_hash", 461 "name": mockLocalPath, 462 "path": pathutil.Join("/", mockLocalPath), 463 "path_hash": "path_hash", 464 "parent_path": "/", 465 "level": 1, 466 "size": 65536, 467 "actual_file_size": 65536 * int64(len(a.Blobbers)), 468 "actual_file_hash": "actual_file_hash", 469 "created_at": common.Timestamp(time.Now().Unix()), 470 "updated_at": common.Timestamp(time.Now().Unix()), 471 "id": 3, 472 }, 473 }, 474 } 475 476 resfsIn, err := json.Marshal(refsInput) 477 require.NoError(t, err) 478 479 fileMetaIn := []byte("{\"actual_file_size\":1}") 480 hashes := []string{ 481 "a9ad93057a092ebeeab2e34f16cd6c1135d08b5a165708d072e6d2da75b47e81", 482 "bf116d80708522b6e006e818c05e1de4d6197e5882f17cd806702c4396100176", 483 "3c4f6a43748f6b7cefee11216540414cb9b2563c294a5f7d633c2e9cda26f7bc", 484 "249684daaeef1a8d38d0be0ea38777886e0b3ddf3deaef2eabe4117cc6e67256", 485 } 486 setupHttpResponses(t, &mockClient, &mockFastClient, a.ID, resfsIn, fileMetaIn, hashes, len(a.Blobbers), len(a.Blobbers), true) 487 488 err = a.EncryptAndUpdateFile(os.TempDir(), mockLocalPath, "/", nil) 489 require.NoError(t, err) 490 } 491 492 func TestAllocation_EncryptAndUploadFile(t *testing.T) { 493 mockClient := mocks.HttpClient{} 494 zboxutil.Client = &mockClient 495 mockFastClient := mocks.FastClient{} 496 zboxutil.FastHttpClient = &mockFastClient 497 498 const ( 499 mockLocalPath = "1.txt" 500 mockTmpPath = "/tmp" 501 ) 502 503 if teardown := setupMockFile(t, mockLocalPath); teardown != nil { 504 defer teardown(t) 505 } 506 a := &Allocation{ 507 Tx: "TestAllocation_EncryptAndUploadFile", 508 ParityShards: 2, 509 DataShards: 2, 510 Size: 2 * GB, 511 } 512 513 setupMockAllocation(t, a) 514 for i := 0; i < numBlobbers; i++ { 515 a.Blobbers = append(a.Blobbers, &blockchain.StorageNode{ 516 ID: mockBlobberId + strconv.Itoa(i), 517 Baseurl: "http://" + t.Name() + "/" + mockBlobberUrl + strconv.Itoa(i), 518 }) 519 } 520 521 hashes := []string{ 522 "5c84c73878159775992d20425c13bafc8bc10515c40e0365dde068626918fceb", 523 "f8d78ca33bd3c532f4d9c56bcd969944b61350c1be64df22f9353f359e3a8ba4", 524 "f435a42af309218e88196d4ed2e0c1977a701641b06434be0bb0263099f3faa9", 525 "6b3e932bfd2b2c09e39d35e7c4928c42b73bee194045e545560229234d695669", 526 } 527 setupHttpResponses(t, &mockClient, &mockFastClient, a.ID, nil, nil, hashes, len(a.Blobbers), len(a.Blobbers), false) 528 529 err := a.EncryptAndUploadFile(mockTmpPath, mockLocalPath, "/", nil) 530 require.NoError(t, err) 531 } 532 533 func TestAllocation_EncryptAndUpdateFileWithThumbnail(t *testing.T) { 534 mockClient := mocks.HttpClient{} 535 zboxutil.Client = &mockClient 536 mockFastClient := mocks.FastClient{} 537 zboxutil.FastHttpClient = &mockFastClient 538 539 const ( 540 mockLocalPath = "1.txt" 541 mockThumbnailPath = "thumbnail_alloc" 542 mockTmpPath = "/tmp" 543 ) 544 545 if teardown := setupMockFile(t, mockLocalPath); teardown != nil { 546 defer teardown(t) 547 } 548 549 if teardown := setupMockFile(t, mockThumbnailPath); teardown != nil { 550 defer teardown(t) 551 } 552 553 a := &Allocation{ 554 ID: "TestAllocation_EncryptAndUpdateFileWithThumbnail", 555 Tx: "TestAllocation_EncryptAndUpdateFileWithThumbnail", 556 ParityShards: 2, 557 DataShards: 2, 558 Size: 2 * GB, 559 } 560 561 setupMockAllocation(t, a) 562 for i := 0; i < numBlobbers; i++ { 563 a.Blobbers = append(a.Blobbers, &blockchain.StorageNode{ 564 ID: mockBlobberId + strconv.Itoa(i), 565 Baseurl: "http://" + t.Name() + "/" + mockBlobberUrl + strconv.Itoa(i), 566 }) 567 } 568 569 refsInput := map[string]interface{}{ 570 "total_pages": 1, 571 "refs": []map[string]interface{}{ 572 { 573 "file_id": "2", 574 "type": "f", 575 "allocation_id": a.ID, 576 "lookup_hash": "lookup_hash", 577 "name": mockLocalPath, 578 "path": pathutil.Join("/", mockLocalPath), 579 "path_hash": "path_hash", 580 "parent_path": "/", 581 "level": 1, 582 "size": 65536, 583 "actual_file_size": 65536 * int64(len(a.Blobbers)), 584 "actual_file_hash": "actual_file_hash", 585 "created_at": common.Timestamp(time.Now().Unix()), 586 "updated_at": common.Timestamp(time.Now().Unix()), 587 "id": 3, 588 }, 589 }, 590 } 591 592 resfsIn, err := json.Marshal(refsInput) 593 require.NoError(t, err) 594 595 fileMetaIn := []byte("{\"actual_file_size\":1}") 596 597 hashes := []string{ 598 "a9ad93057a092ebeeab2e34f16cd6c1135d08b5a165708d072e6d2da75b47e81", 599 "bf116d80708522b6e006e818c05e1de4d6197e5882f17cd806702c4396100176", 600 "3c4f6a43748f6b7cefee11216540414cb9b2563c294a5f7d633c2e9cda26f7bc", 601 "249684daaeef1a8d38d0be0ea38777886e0b3ddf3deaef2eabe4117cc6e67256", 602 } 603 setupHttpResponses(t, &mockClient, &mockFastClient, a.ID, resfsIn, fileMetaIn, hashes, len(a.Blobbers), len(a.Blobbers), true) 604 err = a.EncryptAndUpdateFileWithThumbnail(mockTmpPath, mockLocalPath, "/", mockThumbnailPath, nil) 605 606 require.NoError(t, err) 607 } 608 609 func TestAllocation_EncryptAndUploadFileWithThumbnail(t *testing.T) { 610 mockClient := mocks.HttpClient{} 611 zboxutil.Client = &mockClient 612 mockFastClient := mocks.FastClient{} 613 zboxutil.FastHttpClient = &mockFastClient 614 615 const ( 616 mockLocalPath = "1.txt" 617 mockThumbnailPath = "thumbnail_alloc" 618 mockTmpPath = "/tmp" 619 ) 620 621 if teardown := setupMockFile(t, mockLocalPath); teardown != nil { 622 defer teardown(t) 623 } 624 625 if teardown := setupMockFile(t, mockThumbnailPath); teardown != nil { 626 defer teardown(t) 627 } 628 629 a := &Allocation{ 630 Tx: "TestAllocation_EncryptAndUploadFileWithThumbnail", 631 ParityShards: 2, 632 DataShards: 2, 633 Size: 2 * GB, 634 ctx: context.TODO(), 635 } 636 637 setupMockAllocation(t, a) 638 for i := 0; i < numBlobbers; i++ { 639 a.Blobbers = append(a.Blobbers, &blockchain.StorageNode{ 640 ID: mockBlobberId + strconv.Itoa(i), 641 Baseurl: "http://" + t.Name() + "/" + mockBlobberUrl + strconv.Itoa(i), 642 }) 643 } 644 645 hashes := []string{ 646 "5c84c73878159775992d20425c13bafc8bc10515c40e0365dde068626918fceb", 647 "f8d78ca33bd3c532f4d9c56bcd969944b61350c1be64df22f9353f359e3a8ba4", 648 "f435a42af309218e88196d4ed2e0c1977a701641b06434be0bb0263099f3faa9", 649 "6b3e932bfd2b2c09e39d35e7c4928c42b73bee194045e545560229234d695669", 650 } 651 652 setupHttpResponses(t, &mockClient, &mockFastClient, a.ID, nil, nil, hashes, len(a.Blobbers), len(a.Blobbers), false) 653 654 err := a.EncryptAndUploadFileWithThumbnail(mockTmpPath, mockLocalPath, "/", mockThumbnailPath, nil) 655 require.NoError(t, err) 656 } 657 658 func TestAllocation_RepairFile(t *testing.T) { 659 const ( 660 mockFileRefName = "mock file ref name" 661 mockLocalPath = "1.txt" 662 mockActualHash = "75a919d23622c29ade8096ed1add6606ec970579459178db3a7d1d0ff8df92d3" 663 mockChunkHash = "a6fb1cb61c9a3b8709242de28e44fb0b4de3753995396ae1d21ca9d4e956e9e2" 664 ) 665 666 rawClient := zboxutil.Client 667 createClient := resty.CreateClient 668 669 var mockClient = mocks.HttpClient{} 670 671 zboxutil.Client = &mockClient 672 resty.CreateClient = func(t *http.Transport, timeout time.Duration) resty.Client { 673 return &mockClient 674 } 675 676 defer func() { 677 zboxutil.Client = rawClient 678 resty.CreateClient = createClient 679 }() 680 681 client := zclient.GetClient() 682 client.Wallet = &zcncrypto.Wallet{ 683 ClientID: mockClientId, 684 ClientKey: mockClientKey, 685 } 686 687 // setupHttpResponses := func(t *testing.T, testName string, numBlobbers, numCorrect int) { 688 // require.True(t, numBlobbers >= numCorrect) 689 // for i := 0; i < numBlobbers; i++ { 690 // var hash string 691 // if i < numCorrect { 692 // hash = mockActualHash 693 // } 694 // frName := mockFileRefName + strconv.Itoa(i) 695 // url := "http://TestAllocation_RepairFile" + testName + mockBlobberUrl + strconv.Itoa(i) + "/v1/file/meta" 696 // mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 697 // return strings.HasPrefix(req.URL.String(), url) 698 // })).Return(&http.Response{ 699 // StatusCode: http.StatusOK, 700 // Body: func(fileRefName, hash string) io.ReadCloser { 701 // jsonFR, err := json.Marshal(&fileref.FileRef{ 702 // ActualFileHash: hash, 703 // Ref: fileref.Ref{ 704 // Name: fileRefName, 705 // }, 706 // }) 707 // require.NoError(t, err) 708 // return ioutil.NopCloser(bytes.NewReader([]byte(jsonFR))) 709 // }(frName, hash), 710 // }, nil) 711 // } 712 // } 713 714 setupHttpResponsesWithUpload := func(t *testing.T, testName string, numBlobbers, numCorrect int) { 715 require.True(t, numBlobbers >= numCorrect) 716 for i := 0; i < numBlobbers; i++ { 717 var hash string 718 if i < numCorrect { 719 hash = mockActualHash 720 } 721 722 frName := mockFileRefName + strconv.Itoa(i) 723 httpResponse := &http.Response{ 724 StatusCode: http.StatusOK, 725 Body: func(fileRefName, hash string) io.ReadCloser { 726 jsonFR, err := json.Marshal(&fileref.FileRef{ 727 ActualFileHash: hash, 728 ActualFileSize: 14, 729 Ref: fileref.Ref{ 730 Name: fileRefName, 731 FileMetaHash: hash, 732 }, 733 }) 734 require.NoError(t, err) 735 return ioutil.NopCloser(bytes.NewReader([]byte(jsonFR))) 736 }(frName, hash), 737 } 738 739 urlMeta := "http://TestAllocation_RepairFile" + testName + mockBlobberUrl + strconv.Itoa(i) + "/v1/file/meta" 740 mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 741 return strings.HasPrefix(req.URL.String(), urlMeta) 742 })).Return(httpResponse, nil) 743 744 urlUpload := "http://TestAllocation_RepairFile" + testName + mockBlobberUrl + strconv.Itoa(i) + "/v1/file/upload" 745 mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 746 return strings.HasPrefix(req.URL.String(), urlUpload) 747 })).Return(&http.Response{ 748 StatusCode: http.StatusOK, 749 Body: func(fileRefName, hash string) io.ReadCloser { 750 jsonFR, err := json.Marshal(&UploadResult{ 751 Filename: mockLocalPath, 752 Hash: mockChunkHash, 753 }) 754 require.NoError(t, err) 755 return ioutil.NopCloser(bytes.NewReader([]byte(jsonFR))) 756 }(frName, hash), 757 }, nil) 758 759 urlLatestWritemarker := "http://TestAllocation_RepairFile" + testName + mockBlobberUrl + strconv.Itoa(i) + "/v1/file/latestwritemarker" 760 mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 761 return strings.HasPrefix(req.URL.String(), urlLatestWritemarker) 762 })).Return(&http.Response{ 763 StatusCode: http.StatusOK, 764 Body: func() io.ReadCloser { 765 s := `{"latest_write_marker":null,"prev_write_marker":null}` 766 return ioutil.NopCloser(bytes.NewReader([]byte(s))) 767 }(), 768 }, nil) 769 770 urlRollback := "http://TestAllocation_RepairFile" + testName + mockBlobberUrl + strconv.Itoa(i) + "/v1/connection/rollback" 771 mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 772 return strings.HasPrefix(req.URL.String(), urlRollback) 773 })).Return(&http.Response{ 774 StatusCode: http.StatusOK, 775 Body: ioutil.NopCloser(bytes.NewReader(nil)), 776 }, nil) 777 778 urlFilePath := "http://TestAllocation_RepairFile" + testName + mockBlobberUrl + strconv.Itoa(i) + "/v1/file/referencepath" 779 mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 780 return strings.HasPrefix(req.URL.String(), urlFilePath) 781 })).Return(&http.Response{ 782 StatusCode: http.StatusOK, 783 Body: func(fileRefName, hash string) io.ReadCloser { 784 jsonFR, err := json.Marshal(&ReferencePathResult{ 785 ReferencePath: &fileref.ReferencePath{ 786 Meta: map[string]interface{}{ 787 "type": "d", 788 }, 789 }, 790 LatestWM: nil, 791 }) 792 require.NoError(t, err) 793 return ioutil.NopCloser(bytes.NewReader([]byte(jsonFR))) 794 }(frName, hash), 795 }, nil) 796 797 urlCommit := "http://TestAllocation_RepairFile" + testName + mockBlobberUrl + strconv.Itoa(i) + "/v1/connection/commit" 798 mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 799 return strings.HasPrefix(req.URL.String(), urlCommit) 800 })).Return(&http.Response{ 801 StatusCode: http.StatusOK, 802 Body: func(fileRefName, hash string) io.ReadCloser { 803 jsonFR, err := json.Marshal(&ReferencePathResult{}) 804 require.NoError(t, err) 805 return ioutil.NopCloser(bytes.NewReader([]byte(jsonFR))) 806 }(frName, hash), 807 }, nil) 808 809 urlLock := "http://TestAllocation_RepairFile" + testName + mockBlobberUrl + strconv.Itoa(i) + zboxutil.WM_LOCK_ENDPOINT 810 urlLock = strings.TrimRight(urlLock, "/") 811 mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 812 return strings.HasPrefix(req.URL.String(), urlLock) 813 })).Return(&http.Response{ 814 StatusCode: http.StatusOK, 815 Body: func() io.ReadCloser { 816 resp := &WMLockResult{ 817 Status: WMLockStatusOK, 818 } 819 respBuf, _ := json.Marshal(resp) 820 return ioutil.NopCloser(bytes.NewReader(respBuf)) 821 }(), 822 }, nil) 823 824 urlCreateConnection := "http://TestAllocation_RepairFile" + testName + mockBlobberUrl + strconv.Itoa(i) + zboxutil.CREATE_CONNECTION_ENDPOINT 825 urlCreateConnection = strings.TrimRight(urlCreateConnection, "/") 826 mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 827 return strings.HasPrefix(req.URL.String(), urlCreateConnection) 828 })).Return(&http.Response{ 829 StatusCode: http.StatusOK, 830 Body: func() io.ReadCloser { 831 respBuf, _ := json.Marshal("connection_id") 832 return ioutil.NopCloser(bytes.NewReader(respBuf)) 833 }(), 834 }, nil) 835 } 836 } 837 838 type parameters struct { 839 localPath string 840 remotePath string 841 status StatusCallback 842 } 843 tests := []struct { 844 name string 845 parameters parameters 846 numBlobbers int 847 numCorrect int 848 setup func(*testing.T, string, int, int) 849 wantErr bool 850 wantRepair bool 851 errMsg string 852 }{ 853 // { 854 // name: "Test_Repair_Not_Required_Failed", 855 // parameters: parameters{ 856 // localPath: mockLocalPath, 857 // remotePath: "/", 858 // }, 859 // numBlobbers: 4, 860 // numCorrect: 4, 861 // setup: setupHttpResponses, 862 // wantRepair: false, 863 // }, 864 { 865 name: "Test_Repair_Required_Success", 866 parameters: parameters{ 867 localPath: mockLocalPath, 868 remotePath: "/", 869 }, 870 numBlobbers: 6, 871 numCorrect: 5, 872 setup: setupHttpResponsesWithUpload, 873 wantRepair: true, 874 }, 875 } 876 877 if teardown := setupMockFile(t, mockLocalPath); teardown != nil { 878 defer teardown(t) 879 } 880 881 for _, tt := range tests { 882 t.Run(tt.name, func(t *testing.T) { 883 require := require.New(t) 884 885 a := &Allocation{ 886 ParityShards: tt.numBlobbers / 2, 887 DataShards: tt.numBlobbers / 2, 888 Size: 2 * GB, 889 } 890 a.downloadChan = make(chan *DownloadRequest, 10) 891 a.repairChan = make(chan *RepairRequest, 1) 892 a.ctx, a.ctxCancelF = context.WithCancel(context.Background()) 893 a.downloadProgressMap = make(map[string]*DownloadRequest) 894 a.mutex = &sync.Mutex{} 895 a.initialized = true 896 sdkInitialized = true 897 for i := 0; i < tt.numBlobbers; i++ { 898 a.Blobbers = append(a.Blobbers, &blockchain.StorageNode{ 899 ID: mockBlobberId + strconv.Itoa(i), 900 Baseurl: "http://TestAllocation_RepairFile" + tt.name + mockBlobberUrl + strconv.Itoa(i), 901 }) 902 } 903 setupMockAllocation(t, a) 904 tt.setup(t, tt.name, tt.numBlobbers, tt.numCorrect) 905 found, _, isRequired, ref, err := a.RepairRequired(tt.parameters.remotePath) 906 require.Nil(err) 907 require.Equal(tt.wantRepair, isRequired) 908 if !tt.wantRepair { 909 return 910 } 911 f, err := os.Open(tt.parameters.localPath) 912 require.Nil(err) 913 sz, err := f.Stat() 914 require.Nil(err) 915 require.NotNil(sz) 916 ref.ActualSize = sz.Size() 917 op := a.RepairFile(f, tt.parameters.remotePath, tt.parameters.status, found, ref) 918 err = a.DoMultiOperation([]OperationRequest{*op}, WithRepair()) 919 if tt.wantErr { 920 require.NotNil(err) 921 } else { 922 require.Nil(err) 923 } 924 925 if err != nil { 926 require.EqualValues(tt.errMsg, errors.Top(err)) 927 return 928 } 929 require.NoErrorf(err, "Unexpected error %v", err) 930 }) 931 } 932 }