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 }