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

     1  package sdk
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"fmt"
     8  	"io"
     9  	"io/ioutil"
    10  	"net/http"
    11  	"strconv"
    12  	"strings"
    13  	"sync"
    14  	"testing"
    15  
    16  	"github.com/0chain/errors"
    17  	"github.com/0chain/gosdk/core/zcncrypto"
    18  	"github.com/0chain/gosdk/zboxcore/blockchain"
    19  	zclient "github.com/0chain/gosdk/zboxcore/client"
    20  	"github.com/0chain/gosdk/zboxcore/fileref"
    21  	"github.com/0chain/gosdk/zboxcore/marker"
    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_getListInfoFromBlobber(t *testing.T) {
    29  	const (
    30  		mockAllocationTxId = "mock transaction id"
    31  		mockClientId       = "mock client id"
    32  		mockClientKey      = "mock client key"
    33  		mockRemoteFilePath = "mock/remote/file/path"
    34  		mockSignature      = "mock signature"
    35  		mockAllocationId   = "mock allocation id"
    36  		mockErrorMessage   = "mock error message"
    37  		mockBlobberId      = "mock blobber Id"
    38  		mockAllocationRoot = "mock allocation root"
    39  		mockType           = "d"
    40  	)
    41  	var mockClient = mocks.HttpClient{}
    42  	zboxutil.Client = &mockClient
    43  	client := zclient.GetClient()
    44  	client.Wallet = &zcncrypto.Wallet{
    45  		ClientID:  mockClientId,
    46  		ClientKey: mockClientKey,
    47  	}
    48  	type parameters struct {
    49  		listHttpResp   listResponse
    50  		ListResult     fileref.ListResult
    51  		respStatusCode int
    52  		blobberIdx     int
    53  		requestFields  map[string]string
    54  	}
    55  	tests := []struct {
    56  		name       string
    57  		parameters parameters
    58  		setup      func(t *testing.T, name string, p parameters, errMsg string)
    59  		wantErr    bool
    60  		errMsg     string
    61  	}{
    62  		{
    63  			name: "Test_Error_New_HTTP_Failed_By_Containing_" + string([]byte{0x7f, 0, 0}),
    64  			parameters: parameters{
    65  				blobberIdx:     41,
    66  				respStatusCode: 0,
    67  			},
    68  			setup:   func(t *testing.T, name string, p parameters, errMsg string) {},
    69  			wantErr: true,
    70  			errMsg:  `net/url: invalid control character in URL`,
    71  		},
    72  		{
    73  			name: "Test_HTTP_Error",
    74  			parameters: parameters{
    75  				blobberIdx:     41,
    76  				respStatusCode: 0,
    77  			},
    78  			setup: func(t *testing.T, name string, p parameters, errMsg string) {
    79  				mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool {
    80  					return strings.HasPrefix(req.URL.Path, name)
    81  				})).Return(&http.Response{
    82  					Body:       ioutil.NopCloser(bytes.NewReader([]byte(""))),
    83  					StatusCode: p.respStatusCode,
    84  				}, errors.New("", mockErrorMessage))
    85  			},
    86  			wantErr: true,
    87  			errMsg:  mockErrorMessage,
    88  		},
    89  		{
    90  			name: "Test_HTTP_Response_Failed",
    91  			parameters: parameters{
    92  				blobberIdx:     41,
    93  				respStatusCode: http.StatusInternalServerError,
    94  			},
    95  			setup: func(t *testing.T, name string, p parameters, errMsg string) {
    96  				mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool {
    97  					return strings.HasPrefix(req.URL.Path, name)
    98  				})).Return(&http.Response{
    99  					Body:       ioutil.NopCloser(bytes.NewReader([]byte(mockErrorMessage))),
   100  					StatusCode: p.respStatusCode,
   101  				}, nil)
   102  			},
   103  			wantErr: true,
   104  			errMsg:  "error from server list response: " + mockErrorMessage,
   105  		},
   106  		{
   107  			name: "Test_Error_HTTP_Response_Not_JSON_Format",
   108  			parameters: parameters{
   109  				blobberIdx:     41,
   110  				respStatusCode: http.StatusOK,
   111  			},
   112  			setup: func(t *testing.T, name string, p parameters, errMsg string) {
   113  				mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool {
   114  					return strings.HasPrefix(req.URL.Path, name)
   115  				})).Return(&http.Response{
   116  					Body:       ioutil.NopCloser(bytes.NewReader([]byte("this is not json format"))),
   117  					StatusCode: p.respStatusCode,
   118  				}, nil)
   119  			},
   120  			wantErr: true,
   121  			errMsg:  "list entities response parse error:",
   122  		},
   123  		{
   124  			name: "Test_Success",
   125  			parameters: parameters{
   126  				listHttpResp: listResponse{
   127  					ref: &fileref.Ref{
   128  						AllocationID: mockAllocationId,
   129  						Type:         mockType,
   130  					},
   131  				},
   132  				ListResult: fileref.ListResult{
   133  					AllocationRoot: mockAllocationRoot,
   134  					Meta: map[string]interface{}{
   135  						"type": mockType,
   136  					},
   137  				},
   138  				blobberIdx:     41,
   139  				respStatusCode: http.StatusOK,
   140  				requestFields: map[string]string{
   141  					"auth_token": func() string {
   142  						authBytes, err := json.Marshal(&marker.AuthTicket{
   143  							Signature: mockSignature,
   144  						})
   145  						require.NoError(t, err)
   146  						return string(authBytes)
   147  					}(),
   148  					"path_hash": fileref.GetReferenceLookup(mockAllocationId, mockRemoteFilePath),
   149  				},
   150  			},
   151  			setup: func(t *testing.T, testName string, p parameters, _ string) {
   152  				mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool {
   153  					query := req.URL.Query()
   154  					require.EqualValues(t, query.Get("auth_token"), p.requestFields["auth_token"])
   155  					require.EqualValues(t, query.Get("path_hash"), p.requestFields["path_hash"])
   156  					return req.URL.Path == "Test_Success"+zboxutil.LIST_ENDPOINT+mockAllocationTxId &&
   157  						req.Method == "GET" &&
   158  						req.Header.Get("X-App-Client-ID") == mockClientId &&
   159  						req.Header.Get("X-App-Client-Key") == mockClientKey &&
   160  						testName == "Test_Success"
   161  				})).Return(&http.Response{
   162  					StatusCode: p.respStatusCode,
   163  					Body: func(p parameters) io.ReadCloser {
   164  						jsonFR, err := json.Marshal(p.ListResult)
   165  						require.NoError(t, err)
   166  						return ioutil.NopCloser(bytes.NewReader([]byte(jsonFR)))
   167  					}(p),
   168  				}, nil).Once()
   169  			},
   170  		},
   171  	}
   172  	for _, tt := range tests {
   173  		t.Run(tt.name, func(t *testing.T) {
   174  			require := require.New(t)
   175  			tt.setup(t, tt.name, tt.parameters, tt.errMsg)
   176  			blobber := &blockchain.StorageNode{
   177  				ID:      mockBlobberId,
   178  				Baseurl: tt.name,
   179  			}
   180  			req := &ListRequest{
   181  				allocationID:   mockAllocationId,
   182  				allocationTx:   mockAllocationTxId,
   183  				ctx:            context.TODO(),
   184  				remotefilepath: mockRemoteFilePath,
   185  				authToken: &marker.AuthTicket{
   186  					Signature: mockSignature,
   187  				},
   188  			}
   189  			rspCh := make(chan *listResponse, 1)
   190  			go req.getListInfoFromBlobber(blobber, 41, rspCh)
   191  			resp := <-rspCh
   192  			require.EqualValues(tt.wantErr, resp.err != nil)
   193  			if resp.err != nil {
   194  				require.Contains(errors.Top(resp.err), tt.errMsg)
   195  				return
   196  			}
   197  			require.EqualValues(tt.parameters.listHttpResp.ref, resp.ref)
   198  			require.EqualValues(tt.parameters.blobberIdx, resp.blobberIdx)
   199  		})
   200  	}
   201  }
   202  
   203  func TestListRequest_GetListFromBlobbers(t *testing.T) {
   204  	const (
   205  		mockAllocationTxId = "mock transaction id"
   206  		mockAllocationId   = "mock allocation id"
   207  		mockBlobberUrl     = "mockBlobberUrl"
   208  		mockClientId       = "mock client id"
   209  		mockClientKey      = "mock client key"
   210  		mockAllocationRoot = "mock allocation root"
   211  		mockType           = "d"
   212  	)
   213  
   214  	var mockClient = mocks.HttpClient{}
   215  	zboxutil.Client = &mockClient
   216  
   217  	client := zclient.GetClient()
   218  	client.Wallet = &zcncrypto.Wallet{
   219  		ClientID:  mockClientId,
   220  		ClientKey: mockClientKey,
   221  	}
   222  
   223  	setupHttpResponses := func(t *testing.T, name string, numBlobbers int) {
   224  		for i := 0; i < numBlobbers; i++ {
   225  			url := mockBlobberUrl + strconv.Itoa(i)
   226  			mockClient.On("Do", mock.MatchedBy(func(req *http.Request) bool {
   227  				return strings.HasPrefix(req.URL.Path, name+url)
   228  			})).Return(&http.Response{
   229  				StatusCode: http.StatusOK,
   230  				Body: func() io.ReadCloser {
   231  					jsonFR, err := json.Marshal(&fileref.ListResult{
   232  						AllocationRoot: mockAllocationRoot,
   233  						Meta: map[string]interface{}{
   234  							"type":           mockType,
   235  							"file_meta_hash": "mock file meta hash",
   236  						},
   237  					})
   238  					fmt.Println("returned", string(jsonFR))
   239  					require.NoError(t, err)
   240  					return ioutil.NopCloser(bytes.NewReader([]byte(jsonFR)))
   241  				}(),
   242  			}, nil)
   243  		}
   244  	}
   245  
   246  	tests := []struct {
   247  		name            string
   248  		numBlobbers     int
   249  		consensusThresh int
   250  		fullconsensus   int
   251  
   252  		setup    func(*testing.T, string, int)
   253  		wantFunc func(require *require.Assertions, req *ListRequest)
   254  		wantErr  bool
   255  	}{
   256  		{
   257  			name:  "Test_Failed",
   258  			setup: nil,
   259  			wantFunc: func(require *require.Assertions, req *ListRequest) {
   260  				require.NotNil(req)
   261  				require.Equal(0, req.consensus)
   262  			},
   263  			wantErr: true,
   264  		},
   265  		{
   266  			name:            "Test_Success",
   267  			numBlobbers:     4,
   268  			consensusThresh: 2,
   269  			fullconsensus:   4,
   270  			setup:           setupHttpResponses,
   271  			wantFunc: func(require *require.Assertions, req *ListRequest) {
   272  				require.NotNil(req)
   273  				require.Equal(float32(2), req.consensus)
   274  			},
   275  		},
   276  	}
   277  	for _, tt := range tests {
   278  		t.Run(tt.name, func(t *testing.T) {
   279  			require := require.New(t)
   280  			if setup := tt.setup; setup != nil {
   281  				tt.setup(t, tt.name, tt.numBlobbers)
   282  			}
   283  			req := &ListRequest{
   284  				allocationID: mockAllocationId,
   285  				allocationTx: mockAllocationTxId,
   286  				ctx:          context.TODO(),
   287  				blobbers:     []*blockchain.StorageNode{},
   288  				Consensus: Consensus{
   289  					consensusThresh: tt.consensusThresh,
   290  					fullconsensus:   tt.fullconsensus,
   291  					RWMutex:         &sync.RWMutex{},
   292  				},
   293  				listOnly: true,
   294  			}
   295  			for i := 0; i < tt.numBlobbers; i++ {
   296  				req.blobbers = append(req.blobbers, &blockchain.StorageNode{
   297  					Baseurl: tt.name + mockBlobberUrl + strconv.Itoa(i),
   298  				})
   299  			}
   300  			got, _ := req.GetListFromBlobbers()
   301  			expectedResult := &ListResult{
   302  				Type:         mockType,
   303  				Size:         0,
   304  				deleteMask:   zboxutil.NewUint128(1).Lsh(uint64(len(req.blobbers))).Sub64(1),
   305  				FileMetaHash: "mock file meta hash",
   306  			}
   307  			if !tt.wantErr {
   308  				require.EqualValues(expectedResult, got)
   309  				return
   310  			}
   311  			tt.wantFunc(require, req)
   312  		})
   313  	}
   314  }