github.com/cloudreve/Cloudreve/v3@v3.0.0-20240224133659-3edb00a6484c/pkg/cluster/slave_test.go (about) 1 package cluster 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "errors" 7 model "github.com/cloudreve/Cloudreve/v3/models" 8 "github.com/cloudreve/Cloudreve/v3/pkg/cache" 9 "github.com/cloudreve/Cloudreve/v3/pkg/mocks/requestmock" 10 "github.com/cloudreve/Cloudreve/v3/pkg/request" 11 "github.com/cloudreve/Cloudreve/v3/pkg/serializer" 12 "github.com/stretchr/testify/assert" 13 testMock "github.com/stretchr/testify/mock" 14 "io/ioutil" 15 "net/http" 16 "strings" 17 "testing" 18 "time" 19 ) 20 21 func TestSlaveNode_InitAndKill(t *testing.T) { 22 a := assert.New(t) 23 n := &SlaveNode{ 24 callback: func(b bool, u uint) { 25 26 }, 27 } 28 29 a.NotPanics(func() { 30 n.Init(&model.Node{}) 31 time.Sleep(time.Millisecond * 500) 32 n.Init(&model.Node{}) 33 n.Kill() 34 }) 35 } 36 37 func TestSlaveNode_DummyMethods(t *testing.T) { 38 a := assert.New(t) 39 m := &SlaveNode{ 40 Model: &model.Node{}, 41 } 42 43 m.Model.ID = 5 44 a.Equal(m.Model.ID, m.ID()) 45 a.Equal(m.Model.ID, m.DBModel().ID) 46 47 a.False(m.IsActive()) 48 a.False(m.IsMater()) 49 50 m.SubscribeStatusChange(func(isActive bool, id uint) {}) 51 } 52 53 func TestSlaveNode_IsFeatureEnabled(t *testing.T) { 54 a := assert.New(t) 55 m := &SlaveNode{ 56 Model: &model.Node{}, 57 } 58 59 a.False(m.IsFeatureEnabled("aria2")) 60 a.False(m.IsFeatureEnabled("random")) 61 m.Model.Aria2Enabled = true 62 a.True(m.IsFeatureEnabled("aria2")) 63 } 64 65 func TestSlaveNode_Ping(t *testing.T) { 66 a := assert.New(t) 67 m := &SlaveNode{ 68 Model: &model.Node{}, 69 } 70 71 // master return error code 72 { 73 mockRequest := &requestMock{} 74 mockRequest.On("Request", "POST", "heartbeat", testMock.Anything, testMock.Anything).Return(&request.Response{ 75 Response: &http.Response{ 76 StatusCode: 200, 77 Body: ioutil.NopCloser(strings.NewReader("{\"code\":1}")), 78 }, 79 }) 80 m.caller.Client = mockRequest 81 res, err := m.Ping(&serializer.NodePingReq{}) 82 a.Error(err) 83 a.Nil(res) 84 a.Equal(1, err.(serializer.AppError).Code) 85 } 86 87 // return unexpected json 88 { 89 mockRequest := &requestMock{} 90 mockRequest.On("Request", "POST", "heartbeat", testMock.Anything, testMock.Anything).Return(&request.Response{ 91 Response: &http.Response{ 92 StatusCode: 200, 93 Body: ioutil.NopCloser(strings.NewReader("{\"data\":\"233\"}")), 94 }, 95 }) 96 m.caller.Client = mockRequest 97 res, err := m.Ping(&serializer.NodePingReq{}) 98 a.Error(err) 99 a.Nil(res) 100 } 101 102 // return success 103 { 104 mockRequest := &requestMock{} 105 mockRequest.On("Request", "POST", "heartbeat", testMock.Anything, testMock.Anything).Return(&request.Response{ 106 Response: &http.Response{ 107 StatusCode: 200, 108 Body: ioutil.NopCloser(strings.NewReader("{\"data\":\"{}\"}")), 109 }, 110 }) 111 m.caller.Client = mockRequest 112 res, err := m.Ping(&serializer.NodePingReq{}) 113 a.NoError(err) 114 a.NotNil(res) 115 } 116 } 117 118 func TestSlaveNode_GetAria2Instance(t *testing.T) { 119 a := assert.New(t) 120 m := &SlaveNode{ 121 Model: &model.Node{}, 122 } 123 124 a.NotNil(m.GetAria2Instance()) 125 m.Model.Aria2Enabled = true 126 a.NotNil(m.GetAria2Instance()) 127 a.NotNil(m.GetAria2Instance()) 128 } 129 130 func TestSlaveNode_StartPingLoop(t *testing.T) { 131 callbackCount := 0 132 finishedChan := make(chan struct{}) 133 mockRequest := requestMock{} 134 mockRequest.On("Request", "POST", "heartbeat", testMock.Anything, testMock.Anything).Return(&request.Response{ 135 Response: &http.Response{ 136 StatusCode: 404, 137 }, 138 }) 139 m := &SlaveNode{ 140 Active: true, 141 Model: &model.Node{}, 142 callback: func(b bool, u uint) { 143 callbackCount++ 144 if callbackCount == 2 { 145 close(finishedChan) 146 } 147 if callbackCount == 1 { 148 mockRequest.AssertExpectations(t) 149 mockRequest = requestMock{} 150 mockRequest.On("Request", "POST", "heartbeat", testMock.Anything, testMock.Anything).Return(&request.Response{ 151 Response: &http.Response{ 152 StatusCode: 200, 153 Body: ioutil.NopCloser(strings.NewReader("{\"data\":\"{}\"}")), 154 }, 155 }) 156 } 157 }, 158 } 159 cache.Set("setting_slave_ping_interval", "0", 0) 160 cache.Set("setting_slave_recover_interval", "0", 0) 161 cache.Set("setting_slave_node_retry", "1", 0) 162 163 m.caller.Client = &mockRequest 164 go func() { 165 select { 166 case <-finishedChan: 167 m.Kill() 168 } 169 }() 170 m.StartPingLoop() 171 mockRequest.AssertExpectations(t) 172 } 173 174 func TestSlaveNode_AuthInstance(t *testing.T) { 175 a := assert.New(t) 176 m := &SlaveNode{ 177 Model: &model.Node{}, 178 } 179 180 a.NotNil(m.MasterAuthInstance()) 181 a.NotNil(m.SlaveAuthInstance()) 182 } 183 184 func TestSlaveNode_ChangeStatus(t *testing.T) { 185 a := assert.New(t) 186 isActive := false 187 m := &SlaveNode{ 188 Model: &model.Node{}, 189 callback: func(b bool, u uint) { 190 isActive = b 191 }, 192 } 193 194 a.NotPanics(func() { 195 m.changeStatus(false) 196 }) 197 m.changeStatus(true) 198 a.True(isActive) 199 } 200 201 func getTestRPCNodeSlave() *SlaveNode { 202 m := &SlaveNode{ 203 Model: &model.Node{}, 204 } 205 m.caller.parent = m 206 return m 207 } 208 209 func TestSlaveCaller_CreateTask(t *testing.T) { 210 a := assert.New(t) 211 m := getTestRPCNodeSlave() 212 213 // master return 404 214 { 215 mockRequest := requestMock{} 216 mockRequest.On("Request", "POST", "aria2/task", testMock.Anything, testMock.Anything).Return(&request.Response{ 217 Response: &http.Response{ 218 StatusCode: 404, 219 }, 220 }) 221 m.caller.Client = mockRequest 222 res, err := m.caller.CreateTask(&model.Download{}, nil) 223 a.Empty(res) 224 a.Error(err) 225 } 226 227 // master return error 228 { 229 mockRequest := requestMock{} 230 mockRequest.On("Request", "POST", "aria2/task", testMock.Anything, testMock.Anything).Return(&request.Response{ 231 Response: &http.Response{ 232 StatusCode: 200, 233 Body: ioutil.NopCloser(strings.NewReader("{\"code\":1}")), 234 }, 235 }) 236 m.caller.Client = mockRequest 237 res, err := m.caller.CreateTask(&model.Download{}, nil) 238 a.Empty(res) 239 a.Error(err) 240 } 241 242 // master return success 243 { 244 mockRequest := requestMock{} 245 mockRequest.On("Request", "POST", "aria2/task", testMock.Anything, testMock.Anything).Return(&request.Response{ 246 Response: &http.Response{ 247 StatusCode: 200, 248 Body: ioutil.NopCloser(strings.NewReader("{\"data\":\"res\"}")), 249 }, 250 }) 251 m.caller.Client = mockRequest 252 res, err := m.caller.CreateTask(&model.Download{}, nil) 253 a.Equal("res", res) 254 a.NoError(err) 255 } 256 } 257 258 func TestSlaveCaller_Status(t *testing.T) { 259 a := assert.New(t) 260 m := getTestRPCNodeSlave() 261 262 // master return 404 263 { 264 mockRequest := requestMock{} 265 mockRequest.On("Request", "POST", "aria2/status", testMock.Anything, testMock.Anything).Return(&request.Response{ 266 Response: &http.Response{ 267 StatusCode: 404, 268 }, 269 }) 270 m.caller.Client = mockRequest 271 res, err := m.caller.Status(&model.Download{}) 272 a.Empty(res.Status) 273 a.Error(err) 274 } 275 276 // master return error 277 { 278 mockRequest := requestMock{} 279 mockRequest.On("Request", "POST", "aria2/status", testMock.Anything, testMock.Anything).Return(&request.Response{ 280 Response: &http.Response{ 281 StatusCode: 200, 282 Body: ioutil.NopCloser(strings.NewReader("{\"code\":1}")), 283 }, 284 }) 285 m.caller.Client = mockRequest 286 res, err := m.caller.Status(&model.Download{}) 287 a.Empty(res.Status) 288 a.Error(err) 289 } 290 291 // master return success 292 { 293 mockRequest := requestMock{} 294 mockRequest.On("Request", "POST", "aria2/status", testMock.Anything, testMock.Anything).Return(&request.Response{ 295 Response: &http.Response{ 296 StatusCode: 200, 297 Body: ioutil.NopCloser(strings.NewReader("{\"data\":\"re456456s\"}")), 298 }, 299 }) 300 m.caller.Client = mockRequest 301 res, err := m.caller.Status(&model.Download{}) 302 a.Empty(res.Status) 303 a.NoError(err) 304 } 305 } 306 307 func TestSlaveCaller_Cancel(t *testing.T) { 308 a := assert.New(t) 309 m := getTestRPCNodeSlave() 310 311 // master return 404 312 { 313 mockRequest := requestMock{} 314 mockRequest.On("Request", "POST", "aria2/cancel", testMock.Anything, testMock.Anything).Return(&request.Response{ 315 Response: &http.Response{ 316 StatusCode: 404, 317 }, 318 }) 319 m.caller.Client = mockRequest 320 err := m.caller.Cancel(&model.Download{}) 321 a.Error(err) 322 } 323 324 // master return error 325 { 326 mockRequest := requestMock{} 327 mockRequest.On("Request", "POST", "aria2/cancel", testMock.Anything, testMock.Anything).Return(&request.Response{ 328 Response: &http.Response{ 329 StatusCode: 200, 330 Body: ioutil.NopCloser(strings.NewReader("{\"code\":1}")), 331 }, 332 }) 333 m.caller.Client = mockRequest 334 err := m.caller.Cancel(&model.Download{}) 335 a.Error(err) 336 } 337 338 // master return success 339 { 340 mockRequest := requestMock{} 341 mockRequest.On("Request", "POST", "aria2/cancel", testMock.Anything, testMock.Anything).Return(&request.Response{ 342 Response: &http.Response{ 343 StatusCode: 200, 344 Body: ioutil.NopCloser(strings.NewReader("{\"data\":\"res\"}")), 345 }, 346 }) 347 m.caller.Client = mockRequest 348 err := m.caller.Cancel(&model.Download{}) 349 a.NoError(err) 350 } 351 } 352 353 func TestSlaveCaller_Select(t *testing.T) { 354 a := assert.New(t) 355 m := getTestRPCNodeSlave() 356 m.caller.Init() 357 m.caller.GetConfig() 358 359 // master return 404 360 { 361 mockRequest := requestMock{} 362 mockRequest.On("Request", "POST", "aria2/select", testMock.Anything, testMock.Anything).Return(&request.Response{ 363 Response: &http.Response{ 364 StatusCode: 404, 365 }, 366 }) 367 m.caller.Client = mockRequest 368 err := m.caller.Select(&model.Download{}, nil) 369 a.Error(err) 370 } 371 372 // master return error 373 { 374 mockRequest := requestMock{} 375 mockRequest.On("Request", "POST", "aria2/select", testMock.Anything, testMock.Anything).Return(&request.Response{ 376 Response: &http.Response{ 377 StatusCode: 200, 378 Body: ioutil.NopCloser(strings.NewReader("{\"code\":1}")), 379 }, 380 }) 381 m.caller.Client = mockRequest 382 err := m.caller.Select(&model.Download{}, nil) 383 a.Error(err) 384 } 385 386 // master return success 387 { 388 mockRequest := requestMock{} 389 mockRequest.On("Request", "POST", "aria2/select", testMock.Anything, testMock.Anything).Return(&request.Response{ 390 Response: &http.Response{ 391 StatusCode: 200, 392 Body: ioutil.NopCloser(strings.NewReader("{\"data\":\"res\"}")), 393 }, 394 }) 395 m.caller.Client = mockRequest 396 err := m.caller.Select(&model.Download{}, nil) 397 a.NoError(err) 398 } 399 } 400 401 func TestSlaveCaller_DeleteTempFile(t *testing.T) { 402 a := assert.New(t) 403 m := getTestRPCNodeSlave() 404 m.caller.Init() 405 m.caller.GetConfig() 406 407 // master return 404 408 { 409 mockRequest := requestMock{} 410 mockRequest.On("Request", "POST", "aria2/delete", testMock.Anything, testMock.Anything).Return(&request.Response{ 411 Response: &http.Response{ 412 StatusCode: 404, 413 }, 414 }) 415 m.caller.Client = mockRequest 416 err := m.caller.DeleteTempFile(&model.Download{}) 417 a.Error(err) 418 } 419 420 // master return error 421 { 422 mockRequest := requestMock{} 423 mockRequest.On("Request", "POST", "aria2/delete", testMock.Anything, testMock.Anything).Return(&request.Response{ 424 Response: &http.Response{ 425 StatusCode: 200, 426 Body: ioutil.NopCloser(strings.NewReader("{\"code\":1}")), 427 }, 428 }) 429 m.caller.Client = mockRequest 430 err := m.caller.DeleteTempFile(&model.Download{}) 431 a.Error(err) 432 } 433 434 // master return success 435 { 436 mockRequest := requestMock{} 437 mockRequest.On("Request", "POST", "aria2/delete", testMock.Anything, testMock.Anything).Return(&request.Response{ 438 Response: &http.Response{ 439 StatusCode: 200, 440 Body: ioutil.NopCloser(strings.NewReader("{\"data\":\"res\"}")), 441 }, 442 }) 443 m.caller.Client = mockRequest 444 err := m.caller.DeleteTempFile(&model.Download{}) 445 a.NoError(err) 446 } 447 } 448 449 func TestRemoteCallback(t *testing.T) { 450 asserts := assert.New(t) 451 452 // 回调成功 453 { 454 clientMock := requestmock.RequestMock{} 455 mockResp, _ := json.Marshal(serializer.Response{Code: 0}) 456 clientMock.On( 457 "Request", 458 "POST", 459 "http://test/test/url", 460 testMock.Anything, 461 testMock.Anything, 462 ).Return(&request.Response{ 463 Err: nil, 464 Response: &http.Response{ 465 StatusCode: 200, 466 Body: ioutil.NopCloser(bytes.NewReader(mockResp)), 467 }, 468 }) 469 request.GeneralClient = clientMock 470 resp := RemoteCallback("http://test/test/url", serializer.UploadCallback{}) 471 asserts.NoError(resp) 472 clientMock.AssertExpectations(t) 473 } 474 475 // 服务端返回业务错误 476 { 477 clientMock := requestmock.RequestMock{} 478 mockResp, _ := json.Marshal(serializer.Response{Code: 401}) 479 clientMock.On( 480 "Request", 481 "POST", 482 "http://test/test/url", 483 testMock.Anything, 484 testMock.Anything, 485 ).Return(&request.Response{ 486 Err: nil, 487 Response: &http.Response{ 488 StatusCode: 200, 489 Body: ioutil.NopCloser(bytes.NewReader(mockResp)), 490 }, 491 }) 492 request.GeneralClient = clientMock 493 resp := RemoteCallback("http://test/test/url", serializer.UploadCallback{}) 494 asserts.EqualValues(401, resp.(serializer.AppError).Code) 495 clientMock.AssertExpectations(t) 496 } 497 498 // 无法解析回调响应 499 { 500 clientMock := requestmock.RequestMock{} 501 clientMock.On( 502 "Request", 503 "POST", 504 "http://test/test/url", 505 testMock.Anything, 506 testMock.Anything, 507 ).Return(&request.Response{ 508 Err: nil, 509 Response: &http.Response{ 510 StatusCode: 200, 511 Body: ioutil.NopCloser(strings.NewReader("mockResp")), 512 }, 513 }) 514 request.GeneralClient = clientMock 515 resp := RemoteCallback("http://test/test/url", serializer.UploadCallback{}) 516 asserts.Error(resp) 517 clientMock.AssertExpectations(t) 518 } 519 520 // HTTP状态码非200 521 { 522 clientMock := requestmock.RequestMock{} 523 clientMock.On( 524 "Request", 525 "POST", 526 "http://test/test/url", 527 testMock.Anything, 528 testMock.Anything, 529 ).Return(&request.Response{ 530 Err: nil, 531 Response: &http.Response{ 532 StatusCode: 404, 533 Body: ioutil.NopCloser(strings.NewReader("mockResp")), 534 }, 535 }) 536 request.GeneralClient = clientMock 537 resp := RemoteCallback("http://test/test/url", serializer.UploadCallback{}) 538 asserts.Error(resp) 539 clientMock.AssertExpectations(t) 540 } 541 542 // 无法发起回调 543 { 544 clientMock := requestmock.RequestMock{} 545 clientMock.On( 546 "Request", 547 "POST", 548 "http://test/test/url", 549 testMock.Anything, 550 testMock.Anything, 551 ).Return(&request.Response{ 552 Err: errors.New("error"), 553 }) 554 request.GeneralClient = clientMock 555 resp := RemoteCallback("http://test/test/url", serializer.UploadCallback{}) 556 asserts.Error(resp) 557 clientMock.AssertExpectations(t) 558 } 559 }