github.com/cloudreve/Cloudreve/v3@v3.0.0-20240224133659-3edb00a6484c/pkg/filesystem/driver/remote/handler_test.go (about) 1 package remote 2 3 import ( 4 "context" 5 "errors" 6 "github.com/cloudreve/Cloudreve/v3/pkg/filesystem/driver" 7 "github.com/cloudreve/Cloudreve/v3/pkg/mocks/remoteclientmock" 8 "github.com/cloudreve/Cloudreve/v3/pkg/serializer" 9 "io" 10 "io/ioutil" 11 "net/http" 12 "strings" 13 "testing" 14 15 model "github.com/cloudreve/Cloudreve/v3/models" 16 "github.com/cloudreve/Cloudreve/v3/pkg/auth" 17 "github.com/cloudreve/Cloudreve/v3/pkg/cache" 18 "github.com/cloudreve/Cloudreve/v3/pkg/filesystem/fsctx" 19 "github.com/cloudreve/Cloudreve/v3/pkg/request" 20 "github.com/stretchr/testify/assert" 21 testMock "github.com/stretchr/testify/mock" 22 ) 23 24 func TestNewDriver(t *testing.T) { 25 a := assert.New(t) 26 27 // remoteClient 初始化失败 28 { 29 d, err := NewDriver(&model.Policy{Server: string([]byte{0x7f})}) 30 a.Error(err) 31 a.Nil(d) 32 } 33 34 // 成功 35 { 36 d, err := NewDriver(&model.Policy{}) 37 a.NoError(err) 38 a.NotNil(d) 39 } 40 } 41 42 func TestHandler_Source(t *testing.T) { 43 asserts := assert.New(t) 44 auth.General = auth.HMACAuth{SecretKey: []byte("test")} 45 46 // 无法获取上下文 47 { 48 handler := Driver{ 49 Policy: &model.Policy{Server: "/"}, 50 AuthInstance: auth.HMACAuth{}, 51 } 52 ctx := context.Background() 53 res, err := handler.Source(ctx, "", 0, true, 0) 54 asserts.NoError(err) 55 asserts.NotEmpty(res) 56 } 57 58 // 成功 59 { 60 handler := Driver{ 61 Policy: &model.Policy{Server: "/"}, 62 AuthInstance: auth.HMACAuth{}, 63 } 64 file := model.File{ 65 SourceName: "1.txt", 66 } 67 ctx := context.WithValue(context.Background(), fsctx.FileModelCtx, file) 68 res, err := handler.Source(ctx, "", 10, true, 0) 69 asserts.NoError(err) 70 asserts.Contains(res, "api/v3/slave/download/0") 71 } 72 73 // 成功 自定义CDN 74 { 75 handler := Driver{ 76 Policy: &model.Policy{Server: "/", BaseURL: "https://cqu.edu.cn"}, 77 AuthInstance: auth.HMACAuth{}, 78 } 79 file := model.File{ 80 SourceName: "1.txt", 81 } 82 ctx := context.WithValue(context.Background(), fsctx.FileModelCtx, file) 83 res, err := handler.Source(ctx, "", 10, true, 0) 84 asserts.NoError(err) 85 asserts.Contains(res, "api/v3/slave/download/0") 86 asserts.Contains(res, "https://cqu.edu.cn") 87 } 88 89 // 解析失败 自定义CDN 90 { 91 handler := Driver{ 92 Policy: &model.Policy{Server: "/", BaseURL: string([]byte{0x7f})}, 93 AuthInstance: auth.HMACAuth{}, 94 } 95 file := model.File{ 96 SourceName: "1.txt", 97 } 98 ctx := context.WithValue(context.Background(), fsctx.FileModelCtx, file) 99 res, err := handler.Source(ctx, "", 10, true, 0) 100 asserts.Error(err) 101 asserts.Empty(res) 102 } 103 104 // 成功 预览 105 { 106 handler := Driver{ 107 Policy: &model.Policy{Server: "/"}, 108 AuthInstance: auth.HMACAuth{}, 109 } 110 file := model.File{ 111 SourceName: "1.txt", 112 } 113 ctx := context.WithValue(context.Background(), fsctx.FileModelCtx, file) 114 res, err := handler.Source(ctx, "", 10, false, 0) 115 asserts.NoError(err) 116 asserts.Contains(res, "api/v3/slave/source/0") 117 } 118 } 119 120 type ClientMock struct { 121 testMock.Mock 122 } 123 124 func (m ClientMock) Request(method, target string, body io.Reader, opts ...request.Option) *request.Response { 125 args := m.Called(method, target, body, opts) 126 return args.Get(0).(*request.Response) 127 } 128 129 func TestHandler_Delete(t *testing.T) { 130 asserts := assert.New(t) 131 handler := Driver{ 132 Policy: &model.Policy{ 133 SecretKey: "test", 134 Server: "http://test.com", 135 }, 136 AuthInstance: auth.HMACAuth{}, 137 } 138 ctx := context.Background() 139 cache.Set("setting_slave_api_timeout", "60", 0) 140 141 // 成功 142 { 143 clientMock := ClientMock{} 144 clientMock.On( 145 "Request", 146 "POST", 147 "http://test.com/api/v3/slave/delete", 148 testMock.Anything, 149 testMock.Anything, 150 ).Return(&request.Response{ 151 Err: nil, 152 Response: &http.Response{ 153 StatusCode: 200, 154 Body: ioutil.NopCloser(strings.NewReader(`{"code":0}`)), 155 }, 156 }) 157 handler.Client = clientMock 158 failed, err := handler.Delete(ctx, []string{"/test1.txt", "test2.txt"}) 159 clientMock.AssertExpectations(t) 160 asserts.NoError(err) 161 asserts.Len(failed, 0) 162 163 } 164 165 // 结果解析失败 166 { 167 clientMock := ClientMock{} 168 clientMock.On( 169 "Request", 170 "POST", 171 "http://test.com/api/v3/slave/delete", 172 testMock.Anything, 173 testMock.Anything, 174 ).Return(&request.Response{ 175 Err: nil, 176 Response: &http.Response{ 177 StatusCode: 200, 178 Body: ioutil.NopCloser(strings.NewReader(`{"code":203}`)), 179 }, 180 }) 181 handler.Client = clientMock 182 failed, err := handler.Delete(ctx, []string{"/test1.txt", "test2.txt"}) 183 clientMock.AssertExpectations(t) 184 asserts.Error(err) 185 asserts.Len(failed, 2) 186 } 187 188 // 一个失败 189 { 190 clientMock := ClientMock{} 191 clientMock.On( 192 "Request", 193 "POST", 194 "http://test.com/api/v3/slave/delete", 195 testMock.Anything, 196 testMock.Anything, 197 ).Return(&request.Response{ 198 Err: nil, 199 Response: &http.Response{ 200 StatusCode: 200, 201 Body: ioutil.NopCloser(strings.NewReader(`{"code":203,"data":"{\"files\":[\"1\"]}"}`)), 202 }, 203 }) 204 handler.Client = clientMock 205 failed, err := handler.Delete(ctx, []string{"/test1.txt", "test2.txt"}) 206 clientMock.AssertExpectations(t) 207 asserts.Error(err) 208 asserts.Len(failed, 1) 209 } 210 } 211 212 func TestDriver_List(t *testing.T) { 213 asserts := assert.New(t) 214 handler := Driver{ 215 Policy: &model.Policy{ 216 SecretKey: "test", 217 Server: "http://test.com", 218 }, 219 AuthInstance: auth.HMACAuth{}, 220 } 221 ctx := context.Background() 222 cache.Set("setting_slave_api_timeout", "60", 0) 223 224 // 成功 225 { 226 clientMock := ClientMock{} 227 clientMock.On( 228 "Request", 229 "POST", 230 "http://test.com/api/v3/slave/list", 231 testMock.Anything, 232 testMock.Anything, 233 ).Return(&request.Response{ 234 Err: nil, 235 Response: &http.Response{ 236 StatusCode: 200, 237 Body: ioutil.NopCloser(strings.NewReader(`{"code":0,"data":"[{}]"}`)), 238 }, 239 }) 240 handler.Client = clientMock 241 res, err := handler.List(ctx, "/", true) 242 clientMock.AssertExpectations(t) 243 asserts.NoError(err) 244 asserts.Len(res, 1) 245 246 } 247 248 // 响应解析失败 249 { 250 clientMock := ClientMock{} 251 clientMock.On( 252 "Request", 253 "POST", 254 "http://test.com/api/v3/slave/list", 255 testMock.Anything, 256 testMock.Anything, 257 ).Return(&request.Response{ 258 Err: nil, 259 Response: &http.Response{ 260 StatusCode: 200, 261 Body: ioutil.NopCloser(strings.NewReader(`{"code":0,"data":"233"}`)), 262 }, 263 }) 264 handler.Client = clientMock 265 res, err := handler.List(ctx, "/", true) 266 clientMock.AssertExpectations(t) 267 asserts.Error(err) 268 asserts.Len(res, 0) 269 } 270 271 // 从机返回错误 272 { 273 clientMock := ClientMock{} 274 clientMock.On( 275 "Request", 276 "POST", 277 "http://test.com/api/v3/slave/list", 278 testMock.Anything, 279 testMock.Anything, 280 ).Return(&request.Response{ 281 Err: nil, 282 Response: &http.Response{ 283 StatusCode: 200, 284 Body: ioutil.NopCloser(strings.NewReader(`{"code":203}`)), 285 }, 286 }) 287 handler.Client = clientMock 288 res, err := handler.List(ctx, "/", true) 289 clientMock.AssertExpectations(t) 290 asserts.Error(err) 291 asserts.Len(res, 0) 292 } 293 } 294 295 func TestHandler_Get(t *testing.T) { 296 asserts := assert.New(t) 297 handler := Driver{ 298 Policy: &model.Policy{ 299 SecretKey: "test", 300 Server: "http://test.com", 301 }, 302 AuthInstance: auth.HMACAuth{}, 303 } 304 ctx := context.Background() 305 306 // 成功 307 { 308 ctx = context.WithValue(ctx, fsctx.UserCtx, model.User{}) 309 clientMock := ClientMock{} 310 clientMock.On( 311 "Request", 312 "GET", 313 testMock.Anything, 314 nil, 315 testMock.Anything, 316 ).Return(&request.Response{ 317 Err: nil, 318 Response: &http.Response{ 319 StatusCode: 200, 320 Body: ioutil.NopCloser(strings.NewReader(`{"code":0}`)), 321 }, 322 }) 323 handler.Client = clientMock 324 resp, err := handler.Get(ctx, "/test.txt") 325 clientMock.AssertExpectations(t) 326 asserts.NotNil(resp) 327 asserts.NoError(err) 328 } 329 330 // 请求失败 331 { 332 ctx = context.WithValue(ctx, fsctx.UserCtx, model.User{}) 333 clientMock := ClientMock{} 334 clientMock.On( 335 "Request", 336 "GET", 337 testMock.Anything, 338 nil, 339 testMock.Anything, 340 ).Return(&request.Response{ 341 Err: nil, 342 Response: &http.Response{ 343 StatusCode: 404, 344 Body: ioutil.NopCloser(strings.NewReader(`{"code":0}`)), 345 }, 346 }) 347 handler.Client = clientMock 348 resp, err := handler.Get(ctx, "/test.txt") 349 clientMock.AssertExpectations(t) 350 asserts.Nil(resp) 351 asserts.Error(err) 352 } 353 } 354 355 func TestHandler_Put(t *testing.T) { 356 a := assert.New(t) 357 handler, _ := NewDriver(&model.Policy{ 358 Type: "remote", 359 SecretKey: "test", 360 Server: "http://test.com", 361 }) 362 clientMock := &remoteclientmock.RemoteClientMock{} 363 handler.uploadClient = clientMock 364 clientMock.On("Upload", testMock.Anything, testMock.Anything).Return(errors.New("error")) 365 a.Error(handler.Put(context.Background(), &fsctx.FileStream{})) 366 clientMock.AssertExpectations(t) 367 } 368 369 func TestHandler_Thumb(t *testing.T) { 370 asserts := assert.New(t) 371 handler := Driver{ 372 Policy: &model.Policy{ 373 Type: "remote", 374 SecretKey: "test", 375 Server: "http://test.com", 376 OptionsSerialized: model.PolicyOption{ 377 ThumbExts: []string{"txt"}, 378 }, 379 }, 380 AuthInstance: auth.HMACAuth{}, 381 } 382 file := &model.File{ 383 Name: "1.txt", 384 SourceName: "1.txt", 385 } 386 ctx := context.Background() 387 asserts.NoError(cache.Set("setting_preview_timeout", "60", 0)) 388 389 // no error 390 { 391 resp, err := handler.Thumb(ctx, file) 392 asserts.NoError(err) 393 asserts.True(resp.Redirect) 394 } 395 396 // ext not support 397 { 398 file.Name = "1.jpg" 399 resp, err := handler.Thumb(ctx, file) 400 asserts.ErrorIs(err, driver.ErrorThumbNotSupported) 401 asserts.Nil(resp) 402 } 403 } 404 405 func TestHandler_Token(t *testing.T) { 406 a := assert.New(t) 407 handler, _ := NewDriver(&model.Policy{}) 408 409 // 无法创建上传会话 410 { 411 clientMock := &remoteclientmock.RemoteClientMock{} 412 handler.uploadClient = clientMock 413 clientMock.On("CreateUploadSession", testMock.Anything, testMock.Anything, int64(10), false).Return(errors.New("error")) 414 res, err := handler.Token(context.Background(), 10, &serializer.UploadSession{}, &fsctx.FileStream{}) 415 a.Error(err) 416 a.Contains(err.Error(), "error") 417 a.Nil(res) 418 clientMock.AssertExpectations(t) 419 } 420 421 // 无法创建上传地址 422 { 423 clientMock := &remoteclientmock.RemoteClientMock{} 424 handler.uploadClient = clientMock 425 clientMock.On("CreateUploadSession", testMock.Anything, testMock.Anything, int64(10), false).Return(nil) 426 clientMock.On("GetUploadURL", int64(10), "").Return("", "", errors.New("error")) 427 res, err := handler.Token(context.Background(), 10, &serializer.UploadSession{}, &fsctx.FileStream{}) 428 a.Error(err) 429 a.Contains(err.Error(), "error") 430 a.Nil(res) 431 clientMock.AssertExpectations(t) 432 } 433 434 // 成功 435 { 436 clientMock := &remoteclientmock.RemoteClientMock{} 437 handler.uploadClient = clientMock 438 clientMock.On("CreateUploadSession", testMock.Anything, testMock.Anything, int64(10), false).Return(nil) 439 clientMock.On("GetUploadURL", int64(10), "").Return("1", "2", nil) 440 res, err := handler.Token(context.Background(), 10, &serializer.UploadSession{}, &fsctx.FileStream{}) 441 a.NoError(err) 442 a.NotNil(res) 443 a.Equal("1", res.UploadURLs[0]) 444 a.Equal("2", res.Credential) 445 clientMock.AssertExpectations(t) 446 } 447 } 448 449 func TestDriver_CancelToken(t *testing.T) { 450 a := assert.New(t) 451 handler, _ := NewDriver(&model.Policy{}) 452 453 clientMock := &remoteclientmock.RemoteClientMock{} 454 handler.uploadClient = clientMock 455 clientMock.On("DeleteUploadSession", testMock.Anything, "key").Return(errors.New("error")) 456 err := handler.CancelToken(context.Background(), &serializer.UploadSession{Key: "key"}) 457 a.Error(err) 458 a.Contains(err.Error(), "error") 459 clientMock.AssertExpectations(t) 460 }