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  }