github.com/0chain/gosdk@v1.17.11/zboxcore/sdk/filestatsworker_test.go (about)

     1  package sdk
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"io"
     8  	"io/ioutil"
     9  	"mime"
    10  	"mime/multipart"
    11  	"net/http"
    12  	"strconv"
    13  	"strings"
    14  	"testing"
    15  
    16  	"github.com/0chain/errors"
    17  	"github.com/0chain/gosdk/core/encryption"
    18  	"github.com/0chain/gosdk/core/zcncrypto"
    19  	"github.com/0chain/gosdk/zboxcore/blockchain"
    20  	zclient "github.com/0chain/gosdk/zboxcore/client"
    21  	"github.com/0chain/gosdk/zboxcore/fileref"
    22  	"github.com/0chain/gosdk/zboxcore/mocks"
    23  	"github.com/0chain/gosdk/zboxcore/zboxutil"
    24  	"github.com/stretchr/testify/mock"
    25  	"github.com/stretchr/testify/require"
    26  )
    27  
    28  func TestListRequest_getFileStatsInfoFromBlobber(t *testing.T) {
    29  	const (
    30  		mockFileStatsName  = "mock fileStats name"
    31  		mockAllocationTxId = "mock transaction id"
    32  		mockClientId       = "mock client id"
    33  		mockClientKey      = "mock client key"
    34  		mockRemoteFilePath = "mock/remote/file/path"
    35  		mockAllocationId   = "mock allocation id"
    36  		mockErrorMessage   = "mock error message"
    37  		mockBlobberId      = "mock blobber Id"
    38  		mockBlobberIndex   = 87
    39  	)
    40  
    41  	var mockClient = mocks.HttpClient{}
    42  	zboxutil.Client = &mockClient
    43  
    44  	var client = zclient.GetClient()
    45  	client.Wallet = &zcncrypto.Wallet{
    46  		ClientID:  mockClientId,
    47  		ClientKey: mockClientKey,
    48  	}
    49  
    50  	type parameters struct {
    51  		fileStatsHttpResp FileStats
    52  		fileStatsFinal    FileStats
    53  		respStatusCode    int
    54  		requestFields     map[string]string
    55  		blobberIdx        int
    56  	}
    57  
    58  	tests := []struct {
    59  		name       string
    60  		parameters parameters
    61  		setup      func(*testing.T, string, parameters, string)
    62  		wantErr    bool
    63  		errMsg     string
    64  	}{
    65  		{
    66  			name: "Test_Http_Error",
    67  			parameters: parameters{
    68  				respStatusCode: 0,
    69  			},
    70  			setup: func(t *testing.T, name string, p parameters, errMsg string) {
    71  				mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool {
    72  					return strings.HasPrefix(req.URL.Path, "Test_Http_Error")
    73  				})).Return(&http.Response{
    74  					Body:       ioutil.NopCloser(bytes.NewReader([]byte(""))),
    75  					StatusCode: p.respStatusCode,
    76  				}, errors.New("", mockErrorMessage))
    77  			},
    78  			wantErr: true,
    79  			errMsg:  mockErrorMessage,
    80  		},
    81  		{
    82  			name: "Test_Badly_Formatted",
    83  			parameters: parameters{
    84  				respStatusCode: 200,
    85  			},
    86  			setup: func(t *testing.T, name string, p parameters, errMsg string) {
    87  				mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool {
    88  					return strings.HasPrefix(req.URL.Path, "Test_Badly_Formatted")
    89  				})).Return(&http.Response{
    90  					Body:       ioutil.NopCloser(bytes.NewReader([]byte(""))),
    91  					StatusCode: p.respStatusCode,
    92  				}, nil)
    93  			},
    94  			wantErr: true,
    95  			errMsg:  "file stats response parse error",
    96  		},
    97  		{
    98  			name: "Test_Success",
    99  			parameters: parameters{
   100  				fileStatsHttpResp: FileStats{
   101  					Name: mockFileStatsName,
   102  				},
   103  				fileStatsFinal: FileStats{
   104  					Name:       mockFileStatsName,
   105  					BlobberID:  mockBlobberId,
   106  					BlobberURL: "Test_Success",
   107  				},
   108  				blobberIdx:     mockBlobberIndex,
   109  				respStatusCode: 200,
   110  				requestFields: map[string]string{
   111  					"path_hash": fileref.GetReferenceLookup(mockAllocationId, mockRemoteFilePath),
   112  				},
   113  			},
   114  			setup: func(t *testing.T, testName string, p parameters, _ string) {
   115  				mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool {
   116  					mediaType, params, err := mime.ParseMediaType(req.Header.Get("Content-Type"))
   117  					require.NoError(t, err)
   118  					require.True(t, strings.HasPrefix(mediaType, "multipart/"))
   119  					reader := multipart.NewReader(req.Body, params["boundary"])
   120  					var part *multipart.Part
   121  					part, err = reader.NextPart()
   122  					require.NoError(t, err)
   123  					expected, ok := p.requestFields[part.FormName()]
   124  					require.True(t, ok)
   125  					actual, err := ioutil.ReadAll(part)
   126  					require.NoError(t, err)
   127  					require.EqualValues(t, expected, string(actual))
   128  
   129  					sign, _ := zclient.Sign(encryption.Hash(mockAllocationTxId))
   130  					return req.URL.Path == "Test_Success"+zboxutil.FILE_STATS_ENDPOINT+mockAllocationTxId &&
   131  						req.Method == "POST" &&
   132  						req.Header.Get("X-App-Client-ID") == mockClientId &&
   133  						req.Header.Get("X-App-Client-Key") == mockClientKey &&
   134  						req.Header.Get(zboxutil.CLIENT_SIGNATURE_HEADER) == sign &&
   135  						testName == "Test_Success"
   136  				})).Return(&http.Response{
   137  					StatusCode: p.respStatusCode,
   138  					Body: func(p parameters) io.ReadCloser {
   139  						jsonFR, err := json.Marshal(p.fileStatsHttpResp)
   140  						require.NoError(t, err)
   141  						return ioutil.NopCloser(bytes.NewReader([]byte(jsonFR)))
   142  					}(p),
   143  				}, nil).Once()
   144  			},
   145  		},
   146  	}
   147  	for _, tt := range tests {
   148  		t.Run(tt.name, func(t *testing.T) {
   149  			tt.setup(t, tt.name, tt.parameters, tt.errMsg)
   150  			blobber := blockchain.StorageNode{
   151  				ID:      mockBlobberId,
   152  				Baseurl: tt.name,
   153  			}
   154  			req := &ListRequest{
   155  				allocationID:   mockAllocationId,
   156  				allocationTx:   mockAllocationTxId,
   157  				remotefilepath: mockRemoteFilePath,
   158  				ctx:            context.Background(),
   159  			}
   160  			rspCh := make(chan *fileStatsResponse, 1)
   161  			go req.getFileStatsInfoFromBlobber(&blobber, mockBlobberIndex, rspCh)
   162  			resp := <-rspCh
   163  			require.EqualValues(t, tt.wantErr, resp.err != nil)
   164  			if resp.err != nil {
   165  				require.EqualValues(t, tt.errMsg, errors.Top(resp.err))
   166  				return
   167  			}
   168  			require.EqualValues(t, tt.parameters.fileStatsFinal, *resp.filestats)
   169  			require.EqualValues(t, tt.parameters.blobberIdx, resp.blobberIdx)
   170  		})
   171  	}
   172  }
   173  
   174  func TestListRequest_getFileStatsFromBlobbers(t *testing.T) {
   175  	const (
   176  		mockAllocationTxId = "mock transaction id"
   177  		mockAllocationId   = "mock allocation id"
   178  		mockFileRefName    = "mock file ref name"
   179  		mockBlobberUrl     = "mockBlobberUrl"
   180  		mockClientId       = "mock client id"
   181  		mockClientKey      = "mock client key"
   182  	)
   183  
   184  	var mockClient = mocks.HttpClient{}
   185  	zboxutil.Client = &mockClient
   186  
   187  	client := zclient.GetClient()
   188  	client.Wallet = &zcncrypto.Wallet{
   189  		ClientID:  mockClientId,
   190  		ClientKey: mockClientKey,
   191  	}
   192  
   193  	setupHttpResponses := func(t *testing.T, name string, numBlobbers, numCorrect int) {
   194  		for i := 0; i < numBlobbers; i++ {
   195  			frName := mockFileRefName + strconv.Itoa(i)
   196  			url := name + mockBlobberUrl + strconv.Itoa(i)
   197  			mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool {
   198  				return strings.HasPrefix(req.URL.Path, url)
   199  			})).Return(&http.Response{
   200  				StatusCode: http.StatusOK,
   201  				Body: func(fileStatsName string) io.ReadCloser {
   202  					jsonFR, err := json.Marshal(&FileStats{
   203  						Name: fileStatsName,
   204  					})
   205  					require.NoError(t, err)
   206  					return ioutil.NopCloser(bytes.NewReader([]byte(jsonFR)))
   207  				}(frName),
   208  			}, nil)
   209  		}
   210  	}
   211  
   212  	tests := []struct {
   213  		name        string
   214  		numBlobbers int
   215  		numCorrect  int
   216  
   217  		setup             func(*testing.T, string, int, int)
   218  		httpRespFileStats []FileStats
   219  		wantFileStats     []FileStats
   220  		wantErr           bool
   221  	}{
   222  		{
   223  			name:        "Test_Success",
   224  			numBlobbers: 10,
   225  			setup:       setupHttpResponses,
   226  			httpRespFileStats: func(number int) []FileStats {
   227  				var fileStats []FileStats
   228  				for i := 0; i < number; i++ {
   229  					fileStats = append(fileStats, FileStats{
   230  						Name: mockFileRefName + strconv.Itoa(i),
   231  					})
   232  				}
   233  				return fileStats
   234  			}(10),
   235  			wantFileStats: func(number int) []FileStats {
   236  				var fileStats []FileStats
   237  				for i := 0; i < number; i++ {
   238  					fileStats = append(fileStats, FileStats{
   239  						Name:       mockFileRefName + strconv.Itoa(i),
   240  						BlobberID:  strconv.Itoa(i),
   241  						BlobberURL: "Test_Success" + mockBlobberUrl + strconv.Itoa(i),
   242  					})
   243  				}
   244  				return fileStats
   245  			}(10),
   246  		},
   247  	}
   248  	for _, tt := range tests {
   249  		t.Run(tt.name, func(t *testing.T) {
   250  			tt.setup(t, tt.name, tt.numBlobbers, tt.numCorrect)
   251  			req := &ListRequest{
   252  				allocationID: mockAllocationId,
   253  				allocationTx: mockAllocationTxId,
   254  				ctx:          context.TODO(),
   255  				blobbers:     []*blockchain.StorageNode{},
   256  			}
   257  			for i := 0; i < tt.numBlobbers; i++ {
   258  				req.blobbers = append(req.blobbers, &blockchain.StorageNode{
   259  					ID:      strconv.Itoa(i),
   260  					Baseurl: tt.name + mockBlobberUrl + strconv.Itoa(i),
   261  				})
   262  			}
   263  			mapResp := req.getFileStatsFromBlobbers()
   264  			for _, fs := range mapResp {
   265  				index, err := strconv.Atoi(fs.BlobberID)
   266  				require.NoError(t, err)
   267  				require.EqualValues(t, tt.wantFileStats[index], *fs)
   268  			}
   269  		})
   270  	}
   271  }