github.com/0chain/gosdk@v1.17.11/zboxcore/sdk/filemetaworker_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  	"sync"
    15  	"testing"
    16  	"time"
    17  
    18  	"github.com/0chain/errors"
    19  
    20  	"github.com/0chain/gosdk/core/zcncrypto"
    21  	"github.com/0chain/gosdk/zboxcore/blockchain"
    22  	zclient "github.com/0chain/gosdk/zboxcore/client"
    23  	"github.com/0chain/gosdk/zboxcore/fileref"
    24  	"github.com/0chain/gosdk/zboxcore/marker"
    25  	"github.com/0chain/gosdk/zboxcore/mocks"
    26  	"github.com/0chain/gosdk/zboxcore/zboxutil"
    27  	"github.com/stretchr/testify/mock"
    28  	"github.com/stretchr/testify/require"
    29  )
    30  
    31  func TestListRequest_getFileMetaInfoFromBlobber(t *testing.T) {
    32  	const mockFileRefName = "mock fileRef name"
    33  	const mockAllocationTxId = "mock transaction id"
    34  	const mockClientId = "mock client id"
    35  	const mockClientKey = "mock client key"
    36  	const mockRemoteFilePath = "mock/remote/file/path"
    37  	const mockSignature = "mock signature"
    38  	const mockAllocationId = "mock allocation id"
    39  	const mockErrorMessage = "mock error message"
    40  
    41  	var mockClient = mocks.HttpClient{}
    42  	zboxutil.Client = &mockClient
    43  
    44  	client := zclient.GetClient()
    45  	client.Wallet = &zcncrypto.Wallet{
    46  		ClientID:  mockClientId,
    47  		ClientKey: mockClientKey,
    48  	}
    49  
    50  	type parameters struct {
    51  		fileRefToRetrieve fileref.FileRef
    52  		respStatusCode    int
    53  		requestFields     map[string]string
    54  		blobberIdx        int
    55  	}
    56  
    57  	tests := []struct {
    58  		name       string
    59  		parameters parameters
    60  		setup      func(*testing.T, string, parameters, string)
    61  		wantErr    bool
    62  		errMsg     string
    63  	}{
    64  		{
    65  			name: "Test_Http_Error",
    66  			parameters: parameters{
    67  				respStatusCode: 0,
    68  			},
    69  			setup: func(t *testing.T, name string, p parameters, errMsg string) {
    70  				mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool {
    71  					return strings.HasPrefix(req.URL.Path, "Test_Http_Error")
    72  				})).Return(&http.Response{
    73  					Body:       ioutil.NopCloser(bytes.NewReader([]byte(""))),
    74  					StatusCode: p.respStatusCode,
    75  				}, errors.New("", mockErrorMessage))
    76  			},
    77  			wantErr: true,
    78  			errMsg:  mockErrorMessage,
    79  		},
    80  		{
    81  			name: "Test_Badly_Formatted",
    82  			parameters: parameters{
    83  				respStatusCode: 200,
    84  			},
    85  			setup: func(t *testing.T, name string, p parameters, errMsg string) {
    86  				mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool {
    87  					return strings.HasPrefix(req.URL.Path, "Test_Badly_Formatted")
    88  				})).Return(&http.Response{
    89  					Body:       ioutil.NopCloser(bytes.NewReader([]byte(""))),
    90  					StatusCode: p.respStatusCode,
    91  				}, nil)
    92  			},
    93  			wantErr: true,
    94  			errMsg:  "file meta data response parse error",
    95  		},
    96  		{
    97  			name: "Test_Success",
    98  			parameters: parameters{
    99  				fileRefToRetrieve: fileref.FileRef{
   100  					Ref: fileref.Ref{
   101  						Name: mockFileRefName,
   102  					},
   103  				},
   104  				requestFields: map[string]string{
   105  					"auth_token": func() string {
   106  						authBytes, err := json.Marshal(&marker.AuthTicket{
   107  							Signature: mockSignature,
   108  						})
   109  						require.NoError(t, err)
   110  						return string(authBytes)
   111  					}(),
   112  					"path_hash": fileref.GetReferenceLookup(mockAllocationId, mockRemoteFilePath),
   113  				},
   114  				respStatusCode: http.StatusOK,
   115  				blobberIdx:     73,
   116  			},
   117  			setup: func(t *testing.T, testName string, p parameters, errMsg string) {
   118  				mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool {
   119  					mediaType, params, err := mime.ParseMediaType(req.Header.Get("Content-Type"))
   120  					require.NoError(t, err)
   121  					require.True(t, strings.HasPrefix(mediaType, "multipart/"))
   122  					reader := multipart.NewReader(req.Body, params["boundary"])
   123  
   124  					err = nil
   125  					for {
   126  						var part *multipart.Part
   127  						part, err = reader.NextPart()
   128  						if err != nil {
   129  							break
   130  						}
   131  						expected, ok := p.requestFields[part.FormName()]
   132  						require.True(t, ok)
   133  						actual, err := ioutil.ReadAll(part)
   134  						require.NoError(t, err)
   135  						require.EqualValues(t, expected, string(actual))
   136  					}
   137  					require.Error(t, err)
   138  					require.EqualValues(t, "EOF", errors.Top(err))
   139  
   140  					return req.URL.Path == "Test_Success"+zboxutil.FILE_META_ENDPOINT+mockAllocationTxId &&
   141  						req.Method == "POST" &&
   142  						req.Header.Get("X-App-Client-ID") == mockClientId &&
   143  						req.Header.Get("X-App-Client-Key") == mockClientKey &&
   144  						testName == "Test_Success"
   145  				})).Return(&http.Response{
   146  					StatusCode: p.respStatusCode,
   147  					Body: func(p parameters) io.ReadCloser {
   148  						jsonFR, err := json.Marshal(p.fileRefToRetrieve)
   149  						require.NoError(t, err)
   150  						return ioutil.NopCloser(bytes.NewReader([]byte(jsonFR)))
   151  					}(p),
   152  				}, nil)
   153  			},
   154  		},
   155  	}
   156  	for _, tt := range tests {
   157  		tt := tt
   158  		t.Run(tt.name, func(t *testing.T) {
   159  			t.Parallel()
   160  			tt.setup(t, tt.name, tt.parameters, tt.errMsg)
   161  			blobber := &blockchain.StorageNode{
   162  				Baseurl: tt.name,
   163  			}
   164  			req := &ListRequest{
   165  				allocationID:   mockAllocationId,
   166  				allocationTx:   mockAllocationTxId,
   167  				ctx:            context.TODO(),
   168  				remotefilepath: mockRemoteFilePath,
   169  				authToken: &marker.AuthTicket{
   170  					Signature: mockSignature,
   171  				},
   172  			}
   173  			rspCh := make(chan *fileMetaResponse, 1)
   174  			go req.getFileMetaInfoFromBlobber(blobber, 73, rspCh)
   175  
   176  			var resp *fileMetaResponse
   177  			select {
   178  			case <-time.After(time.Second * 10):
   179  				t.Log("Failed after time out of 10 second")
   180  			case resp = <-rspCh:
   181  			}
   182  			// resp := <-rspCh
   183  			require.EqualValues(t, tt.wantErr, resp.err != nil)
   184  			if resp.err != nil {
   185  				require.EqualValues(t, tt.errMsg, errors.Top(resp.err))
   186  				return
   187  			}
   188  			require.EqualValues(t, tt.parameters.fileRefToRetrieve, *resp.fileref)
   189  			require.EqualValues(t, tt.parameters.blobberIdx, resp.blobberIdx)
   190  		})
   191  	}
   192  }
   193  
   194  func TestListRequest_getFileConsensusFromBlobbers(t *testing.T) {
   195  	const mockAllocationTxId = "mock transaction id"
   196  	const mockAllocationId = "mock allocation id"
   197  	const mockFileRefName = "mock file ref name"
   198  	const mockBlobberUrl = "mockBlobberUrl"
   199  	const mockActualHash = "mockActualHash"
   200  
   201  	var mockClient = mocks.HttpClient{}
   202  	zboxutil.Client = &mockClient
   203  
   204  	const mockClientId = "mock client id"
   205  	const mockClientKey = "mock client key"
   206  	client := zclient.GetClient()
   207  	client.Wallet = &zcncrypto.Wallet{
   208  		ClientID:  mockClientId,
   209  		ClientKey: mockClientKey,
   210  	}
   211  
   212  	setupHttpResponses := func(t *testing.T, name string, numBlobbers, numCorrect int) {
   213  		require.True(t, numBlobbers >= numCorrect)
   214  		for i := 0; i < numBlobbers; i++ {
   215  			var hash string
   216  			if i < numCorrect {
   217  				hash = mockActualHash
   218  			}
   219  			frName := mockFileRefName + strconv.Itoa(i)
   220  			url := mockBlobberUrl + strconv.Itoa(i)
   221  			mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool {
   222  				return strings.HasPrefix(req.URL.Path, name+url)
   223  			})).Return(&http.Response{
   224  				StatusCode: http.StatusOK,
   225  				Body: func(fileRefName, hash string) io.ReadCloser {
   226  					jsonFR, err := json.Marshal(&fileref.FileRef{
   227  						ActualFileHash: hash,
   228  						Ref: fileref.Ref{
   229  							Name: fileRefName,
   230  						},
   231  					})
   232  					require.NoError(t, err)
   233  					return ioutil.NopCloser(bytes.NewReader([]byte(jsonFR)))
   234  				}(frName, hash),
   235  			}, nil)
   236  		}
   237  	}
   238  
   239  	tests := []struct {
   240  		name        string
   241  		numBlobbers int
   242  		consensus   Consensus
   243  		numCorrect  int
   244  
   245  		setup   func(*testing.T, string, int, int)
   246  		wantErr bool
   247  	}{
   248  		{
   249  			name:        "Fail_Consensus",
   250  			numBlobbers: 10,
   251  			consensus: Consensus{
   252  				RWMutex:         &sync.RWMutex{},
   253  				consensusThresh: 2,
   254  				fullconsensus:   50,
   255  			},
   256  			numCorrect: 5,
   257  			setup:      setupHttpResponses,
   258  			wantErr:    true,
   259  		},
   260  		{
   261  			name:        "Pass_Consensus",
   262  			numBlobbers: 10,
   263  			consensus: Consensus{
   264  				RWMutex:         &sync.RWMutex{},
   265  				consensusThresh: 2,
   266  				fullconsensus:   50,
   267  			},
   268  			numCorrect: 6,
   269  			setup:      setupHttpResponses,
   270  		},
   271  	}
   272  	for _, tt := range tests { //nolint
   273  		t.Run(tt.name, func(t *testing.T) {
   274  			tt.setup(t, tt.name, tt.numBlobbers, tt.numCorrect)
   275  			req := &ListRequest{
   276  				allocationID: mockAllocationId,
   277  				allocationTx: mockAllocationTxId,
   278  				ctx:          context.TODO(),
   279  				blobbers:     []*blockchain.StorageNode{},
   280  				Consensus:    tt.consensus, //nolint
   281  			}
   282  			for i := 0; i < tt.numBlobbers; i++ {
   283  				req.blobbers = append(req.blobbers, &blockchain.StorageNode{
   284  					Baseurl: tt.name + mockBlobberUrl + strconv.Itoa(i),
   285  				})
   286  			}
   287  
   288  			_, _, mask, fileRefs := req.getFileConsensusFromBlobbers()
   289  
   290  			if mask == nil && fileRefs == nil {
   291  				require.True(t, tt.wantErr)
   292  				return
   293  			}
   294  			require.Len(t, fileRefs, tt.numBlobbers)
   295  			for i, actual := range fileRefs {
   296  				expected := fileref.FileRef{}
   297  				expected.Name = mockFileRefName + strconv.Itoa(i)
   298  				if i < tt.numCorrect {
   299  					expected.ActualFileHash = mockActualHash
   300  				}
   301  				require.EqualValues(t, expected, *actual.fileref)
   302  			}
   303  		})
   304  	}
   305  }