You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
560 lines
14 KiB
560 lines
14 KiB
package cluster
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"errors"
|
|
model "github.com/cloudreve/Cloudreve/v3/models"
|
|
"github.com/cloudreve/Cloudreve/v3/pkg/cache"
|
|
"github.com/cloudreve/Cloudreve/v3/pkg/mocks/requestmock"
|
|
"github.com/cloudreve/Cloudreve/v3/pkg/request"
|
|
"github.com/cloudreve/Cloudreve/v3/pkg/serializer"
|
|
"github.com/stretchr/testify/assert"
|
|
testMock "github.com/stretchr/testify/mock"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestSlaveNode_InitAndKill(t *testing.T) {
|
|
a := assert.New(t)
|
|
n := &SlaveNode{
|
|
callback: func(b bool, u uint) {
|
|
|
|
},
|
|
}
|
|
|
|
a.NotPanics(func() {
|
|
n.Init(&model.Node{})
|
|
time.Sleep(time.Millisecond * 500)
|
|
n.Init(&model.Node{})
|
|
n.Kill()
|
|
})
|
|
}
|
|
|
|
func TestSlaveNode_DummyMethods(t *testing.T) {
|
|
a := assert.New(t)
|
|
m := &SlaveNode{
|
|
Model: &model.Node{},
|
|
}
|
|
|
|
m.Model.ID = 5
|
|
a.Equal(m.Model.ID, m.ID())
|
|
a.Equal(m.Model.ID, m.DBModel().ID)
|
|
|
|
a.False(m.IsActive())
|
|
a.False(m.IsMater())
|
|
|
|
m.SubscribeStatusChange(func(isActive bool, id uint) {})
|
|
}
|
|
|
|
func TestSlaveNode_IsFeatureEnabled(t *testing.T) {
|
|
a := assert.New(t)
|
|
m := &SlaveNode{
|
|
Model: &model.Node{},
|
|
}
|
|
|
|
a.False(m.IsFeatureEnabled("aria2"))
|
|
a.False(m.IsFeatureEnabled("random"))
|
|
m.Model.Aria2Enabled = true
|
|
a.True(m.IsFeatureEnabled("aria2"))
|
|
}
|
|
|
|
func TestSlaveNode_Ping(t *testing.T) {
|
|
a := assert.New(t)
|
|
m := &SlaveNode{
|
|
Model: &model.Node{},
|
|
}
|
|
|
|
// master return error code
|
|
{
|
|
mockRequest := &requestMock{}
|
|
mockRequest.On("Request", "POST", "heartbeat", testMock.Anything, testMock.Anything).Return(&request.Response{
|
|
Response: &http.Response{
|
|
StatusCode: 200,
|
|
Body: ioutil.NopCloser(strings.NewReader("{\"code\":1}")),
|
|
},
|
|
})
|
|
m.caller.Client = mockRequest
|
|
res, err := m.Ping(&serializer.NodePingReq{})
|
|
a.Error(err)
|
|
a.Nil(res)
|
|
a.Equal(1, err.(serializer.AppError).Code)
|
|
}
|
|
|
|
// return unexpected json
|
|
{
|
|
mockRequest := &requestMock{}
|
|
mockRequest.On("Request", "POST", "heartbeat", testMock.Anything, testMock.Anything).Return(&request.Response{
|
|
Response: &http.Response{
|
|
StatusCode: 200,
|
|
Body: ioutil.NopCloser(strings.NewReader("{\"data\":\"233\"}")),
|
|
},
|
|
})
|
|
m.caller.Client = mockRequest
|
|
res, err := m.Ping(&serializer.NodePingReq{})
|
|
a.Error(err)
|
|
a.Nil(res)
|
|
}
|
|
|
|
// return success
|
|
{
|
|
mockRequest := &requestMock{}
|
|
mockRequest.On("Request", "POST", "heartbeat", testMock.Anything, testMock.Anything).Return(&request.Response{
|
|
Response: &http.Response{
|
|
StatusCode: 200,
|
|
Body: ioutil.NopCloser(strings.NewReader("{\"data\":\"{}\"}")),
|
|
},
|
|
})
|
|
m.caller.Client = mockRequest
|
|
res, err := m.Ping(&serializer.NodePingReq{})
|
|
a.NoError(err)
|
|
a.NotNil(res)
|
|
}
|
|
}
|
|
|
|
func TestSlaveNode_GetAria2Instance(t *testing.T) {
|
|
a := assert.New(t)
|
|
m := &SlaveNode{
|
|
Model: &model.Node{},
|
|
}
|
|
|
|
a.NotNil(m.GetAria2Instance())
|
|
m.Model.Aria2Enabled = true
|
|
a.NotNil(m.GetAria2Instance())
|
|
a.NotNil(m.GetAria2Instance())
|
|
}
|
|
|
|
func TestSlaveNode_StartPingLoop(t *testing.T) {
|
|
callbackCount := 0
|
|
finishedChan := make(chan struct{})
|
|
mockRequest := requestMock{}
|
|
mockRequest.On("Request", "POST", "heartbeat", testMock.Anything, testMock.Anything).Return(&request.Response{
|
|
Response: &http.Response{
|
|
StatusCode: 404,
|
|
},
|
|
})
|
|
m := &SlaveNode{
|
|
Active: true,
|
|
Model: &model.Node{},
|
|
callback: func(b bool, u uint) {
|
|
callbackCount++
|
|
if callbackCount == 2 {
|
|
close(finishedChan)
|
|
}
|
|
if callbackCount == 1 {
|
|
mockRequest.AssertExpectations(t)
|
|
mockRequest = requestMock{}
|
|
mockRequest.On("Request", "POST", "heartbeat", testMock.Anything, testMock.Anything).Return(&request.Response{
|
|
Response: &http.Response{
|
|
StatusCode: 200,
|
|
Body: ioutil.NopCloser(strings.NewReader("{\"data\":\"{}\"}")),
|
|
},
|
|
})
|
|
}
|
|
},
|
|
}
|
|
cache.Set("setting_slave_ping_interval", "0", 0)
|
|
cache.Set("setting_slave_recover_interval", "0", 0)
|
|
cache.Set("setting_slave_node_retry", "1", 0)
|
|
|
|
m.caller.Client = &mockRequest
|
|
go func() {
|
|
select {
|
|
case <-finishedChan:
|
|
m.Kill()
|
|
}
|
|
}()
|
|
m.StartPingLoop()
|
|
mockRequest.AssertExpectations(t)
|
|
}
|
|
|
|
func TestSlaveNode_AuthInstance(t *testing.T) {
|
|
a := assert.New(t)
|
|
m := &SlaveNode{
|
|
Model: &model.Node{},
|
|
}
|
|
|
|
a.NotNil(m.MasterAuthInstance())
|
|
a.NotNil(m.SlaveAuthInstance())
|
|
}
|
|
|
|
func TestSlaveNode_ChangeStatus(t *testing.T) {
|
|
a := assert.New(t)
|
|
isActive := false
|
|
m := &SlaveNode{
|
|
Model: &model.Node{},
|
|
callback: func(b bool, u uint) {
|
|
isActive = b
|
|
},
|
|
}
|
|
|
|
a.NotPanics(func() {
|
|
m.changeStatus(false)
|
|
})
|
|
m.changeStatus(true)
|
|
a.True(isActive)
|
|
}
|
|
|
|
func getTestRPCNodeSlave() *SlaveNode {
|
|
m := &SlaveNode{
|
|
Model: &model.Node{},
|
|
}
|
|
m.caller.parent = m
|
|
return m
|
|
}
|
|
|
|
func TestSlaveCaller_CreateTask(t *testing.T) {
|
|
a := assert.New(t)
|
|
m := getTestRPCNodeSlave()
|
|
|
|
// master return 404
|
|
{
|
|
mockRequest := requestMock{}
|
|
mockRequest.On("Request", "POST", "aria2/task", testMock.Anything, testMock.Anything).Return(&request.Response{
|
|
Response: &http.Response{
|
|
StatusCode: 404,
|
|
},
|
|
})
|
|
m.caller.Client = mockRequest
|
|
res, err := m.caller.CreateTask(&model.Download{}, nil)
|
|
a.Empty(res)
|
|
a.Error(err)
|
|
}
|
|
|
|
// master return error
|
|
{
|
|
mockRequest := requestMock{}
|
|
mockRequest.On("Request", "POST", "aria2/task", testMock.Anything, testMock.Anything).Return(&request.Response{
|
|
Response: &http.Response{
|
|
StatusCode: 200,
|
|
Body: ioutil.NopCloser(strings.NewReader("{\"code\":1}")),
|
|
},
|
|
})
|
|
m.caller.Client = mockRequest
|
|
res, err := m.caller.CreateTask(&model.Download{}, nil)
|
|
a.Empty(res)
|
|
a.Error(err)
|
|
}
|
|
|
|
// master return success
|
|
{
|
|
mockRequest := requestMock{}
|
|
mockRequest.On("Request", "POST", "aria2/task", testMock.Anything, testMock.Anything).Return(&request.Response{
|
|
Response: &http.Response{
|
|
StatusCode: 200,
|
|
Body: ioutil.NopCloser(strings.NewReader("{\"data\":\"res\"}")),
|
|
},
|
|
})
|
|
m.caller.Client = mockRequest
|
|
res, err := m.caller.CreateTask(&model.Download{}, nil)
|
|
a.Equal("res", res)
|
|
a.NoError(err)
|
|
}
|
|
}
|
|
|
|
func TestSlaveCaller_Status(t *testing.T) {
|
|
a := assert.New(t)
|
|
m := getTestRPCNodeSlave()
|
|
|
|
// master return 404
|
|
{
|
|
mockRequest := requestMock{}
|
|
mockRequest.On("Request", "POST", "aria2/status", testMock.Anything, testMock.Anything).Return(&request.Response{
|
|
Response: &http.Response{
|
|
StatusCode: 404,
|
|
},
|
|
})
|
|
m.caller.Client = mockRequest
|
|
res, err := m.caller.Status(&model.Download{})
|
|
a.Empty(res.Status)
|
|
a.Error(err)
|
|
}
|
|
|
|
// master return error
|
|
{
|
|
mockRequest := requestMock{}
|
|
mockRequest.On("Request", "POST", "aria2/status", testMock.Anything, testMock.Anything).Return(&request.Response{
|
|
Response: &http.Response{
|
|
StatusCode: 200,
|
|
Body: ioutil.NopCloser(strings.NewReader("{\"code\":1}")),
|
|
},
|
|
})
|
|
m.caller.Client = mockRequest
|
|
res, err := m.caller.Status(&model.Download{})
|
|
a.Empty(res.Status)
|
|
a.Error(err)
|
|
}
|
|
|
|
// master return success
|
|
{
|
|
mockRequest := requestMock{}
|
|
mockRequest.On("Request", "POST", "aria2/status", testMock.Anything, testMock.Anything).Return(&request.Response{
|
|
Response: &http.Response{
|
|
StatusCode: 200,
|
|
Body: ioutil.NopCloser(strings.NewReader("{\"data\":\"re456456s\"}")),
|
|
},
|
|
})
|
|
m.caller.Client = mockRequest
|
|
res, err := m.caller.Status(&model.Download{})
|
|
a.Empty(res.Status)
|
|
a.NoError(err)
|
|
}
|
|
}
|
|
|
|
func TestSlaveCaller_Cancel(t *testing.T) {
|
|
a := assert.New(t)
|
|
m := getTestRPCNodeSlave()
|
|
|
|
// master return 404
|
|
{
|
|
mockRequest := requestMock{}
|
|
mockRequest.On("Request", "POST", "aria2/cancel", testMock.Anything, testMock.Anything).Return(&request.Response{
|
|
Response: &http.Response{
|
|
StatusCode: 404,
|
|
},
|
|
})
|
|
m.caller.Client = mockRequest
|
|
err := m.caller.Cancel(&model.Download{})
|
|
a.Error(err)
|
|
}
|
|
|
|
// master return error
|
|
{
|
|
mockRequest := requestMock{}
|
|
mockRequest.On("Request", "POST", "aria2/cancel", testMock.Anything, testMock.Anything).Return(&request.Response{
|
|
Response: &http.Response{
|
|
StatusCode: 200,
|
|
Body: ioutil.NopCloser(strings.NewReader("{\"code\":1}")),
|
|
},
|
|
})
|
|
m.caller.Client = mockRequest
|
|
err := m.caller.Cancel(&model.Download{})
|
|
a.Error(err)
|
|
}
|
|
|
|
// master return success
|
|
{
|
|
mockRequest := requestMock{}
|
|
mockRequest.On("Request", "POST", "aria2/cancel", testMock.Anything, testMock.Anything).Return(&request.Response{
|
|
Response: &http.Response{
|
|
StatusCode: 200,
|
|
Body: ioutil.NopCloser(strings.NewReader("{\"data\":\"res\"}")),
|
|
},
|
|
})
|
|
m.caller.Client = mockRequest
|
|
err := m.caller.Cancel(&model.Download{})
|
|
a.NoError(err)
|
|
}
|
|
}
|
|
|
|
func TestSlaveCaller_Select(t *testing.T) {
|
|
a := assert.New(t)
|
|
m := getTestRPCNodeSlave()
|
|
m.caller.Init()
|
|
m.caller.GetConfig()
|
|
|
|
// master return 404
|
|
{
|
|
mockRequest := requestMock{}
|
|
mockRequest.On("Request", "POST", "aria2/select", testMock.Anything, testMock.Anything).Return(&request.Response{
|
|
Response: &http.Response{
|
|
StatusCode: 404,
|
|
},
|
|
})
|
|
m.caller.Client = mockRequest
|
|
err := m.caller.Select(&model.Download{}, nil)
|
|
a.Error(err)
|
|
}
|
|
|
|
// master return error
|
|
{
|
|
mockRequest := requestMock{}
|
|
mockRequest.On("Request", "POST", "aria2/select", testMock.Anything, testMock.Anything).Return(&request.Response{
|
|
Response: &http.Response{
|
|
StatusCode: 200,
|
|
Body: ioutil.NopCloser(strings.NewReader("{\"code\":1}")),
|
|
},
|
|
})
|
|
m.caller.Client = mockRequest
|
|
err := m.caller.Select(&model.Download{}, nil)
|
|
a.Error(err)
|
|
}
|
|
|
|
// master return success
|
|
{
|
|
mockRequest := requestMock{}
|
|
mockRequest.On("Request", "POST", "aria2/select", testMock.Anything, testMock.Anything).Return(&request.Response{
|
|
Response: &http.Response{
|
|
StatusCode: 200,
|
|
Body: ioutil.NopCloser(strings.NewReader("{\"data\":\"res\"}")),
|
|
},
|
|
})
|
|
m.caller.Client = mockRequest
|
|
err := m.caller.Select(&model.Download{}, nil)
|
|
a.NoError(err)
|
|
}
|
|
}
|
|
|
|
func TestSlaveCaller_DeleteTempFile(t *testing.T) {
|
|
a := assert.New(t)
|
|
m := getTestRPCNodeSlave()
|
|
m.caller.Init()
|
|
m.caller.GetConfig()
|
|
|
|
// master return 404
|
|
{
|
|
mockRequest := requestMock{}
|
|
mockRequest.On("Request", "POST", "aria2/delete", testMock.Anything, testMock.Anything).Return(&request.Response{
|
|
Response: &http.Response{
|
|
StatusCode: 404,
|
|
},
|
|
})
|
|
m.caller.Client = mockRequest
|
|
err := m.caller.DeleteTempFile(&model.Download{})
|
|
a.Error(err)
|
|
}
|
|
|
|
// master return error
|
|
{
|
|
mockRequest := requestMock{}
|
|
mockRequest.On("Request", "POST", "aria2/delete", testMock.Anything, testMock.Anything).Return(&request.Response{
|
|
Response: &http.Response{
|
|
StatusCode: 200,
|
|
Body: ioutil.NopCloser(strings.NewReader("{\"code\":1}")),
|
|
},
|
|
})
|
|
m.caller.Client = mockRequest
|
|
err := m.caller.DeleteTempFile(&model.Download{})
|
|
a.Error(err)
|
|
}
|
|
|
|
// master return success
|
|
{
|
|
mockRequest := requestMock{}
|
|
mockRequest.On("Request", "POST", "aria2/delete", testMock.Anything, testMock.Anything).Return(&request.Response{
|
|
Response: &http.Response{
|
|
StatusCode: 200,
|
|
Body: ioutil.NopCloser(strings.NewReader("{\"data\":\"res\"}")),
|
|
},
|
|
})
|
|
m.caller.Client = mockRequest
|
|
err := m.caller.DeleteTempFile(&model.Download{})
|
|
a.NoError(err)
|
|
}
|
|
}
|
|
|
|
func TestRemoteCallback(t *testing.T) {
|
|
asserts := assert.New(t)
|
|
|
|
// 回调成功
|
|
{
|
|
clientMock := requestmock.RequestMock{}
|
|
mockResp, _ := json.Marshal(serializer.Response{Code: 0})
|
|
clientMock.On(
|
|
"Request",
|
|
"POST",
|
|
"http://test/test/url",
|
|
testMock.Anything,
|
|
testMock.Anything,
|
|
).Return(&request.Response{
|
|
Err: nil,
|
|
Response: &http.Response{
|
|
StatusCode: 200,
|
|
Body: ioutil.NopCloser(bytes.NewReader(mockResp)),
|
|
},
|
|
})
|
|
request.GeneralClient = clientMock
|
|
resp := RemoteCallback("http://test/test/url", serializer.UploadCallback{})
|
|
asserts.NoError(resp)
|
|
clientMock.AssertExpectations(t)
|
|
}
|
|
|
|
// 服务端返回业务错误
|
|
{
|
|
clientMock := requestmock.RequestMock{}
|
|
mockResp, _ := json.Marshal(serializer.Response{Code: 401})
|
|
clientMock.On(
|
|
"Request",
|
|
"POST",
|
|
"http://test/test/url",
|
|
testMock.Anything,
|
|
testMock.Anything,
|
|
).Return(&request.Response{
|
|
Err: nil,
|
|
Response: &http.Response{
|
|
StatusCode: 200,
|
|
Body: ioutil.NopCloser(bytes.NewReader(mockResp)),
|
|
},
|
|
})
|
|
request.GeneralClient = clientMock
|
|
resp := RemoteCallback("http://test/test/url", serializer.UploadCallback{})
|
|
asserts.EqualValues(401, resp.(serializer.AppError).Code)
|
|
clientMock.AssertExpectations(t)
|
|
}
|
|
|
|
// 无法解析回调响应
|
|
{
|
|
clientMock := requestmock.RequestMock{}
|
|
clientMock.On(
|
|
"Request",
|
|
"POST",
|
|
"http://test/test/url",
|
|
testMock.Anything,
|
|
testMock.Anything,
|
|
).Return(&request.Response{
|
|
Err: nil,
|
|
Response: &http.Response{
|
|
StatusCode: 200,
|
|
Body: ioutil.NopCloser(strings.NewReader("mockResp")),
|
|
},
|
|
})
|
|
request.GeneralClient = clientMock
|
|
resp := RemoteCallback("http://test/test/url", serializer.UploadCallback{})
|
|
asserts.Error(resp)
|
|
clientMock.AssertExpectations(t)
|
|
}
|
|
|
|
// HTTP状态码非200
|
|
{
|
|
clientMock := requestmock.RequestMock{}
|
|
clientMock.On(
|
|
"Request",
|
|
"POST",
|
|
"http://test/test/url",
|
|
testMock.Anything,
|
|
testMock.Anything,
|
|
).Return(&request.Response{
|
|
Err: nil,
|
|
Response: &http.Response{
|
|
StatusCode: 404,
|
|
Body: ioutil.NopCloser(strings.NewReader("mockResp")),
|
|
},
|
|
})
|
|
request.GeneralClient = clientMock
|
|
resp := RemoteCallback("http://test/test/url", serializer.UploadCallback{})
|
|
asserts.Error(resp)
|
|
clientMock.AssertExpectations(t)
|
|
}
|
|
|
|
// 无法发起回调
|
|
{
|
|
clientMock := requestmock.RequestMock{}
|
|
clientMock.On(
|
|
"Request",
|
|
"POST",
|
|
"http://test/test/url",
|
|
testMock.Anything,
|
|
testMock.Anything,
|
|
).Return(&request.Response{
|
|
Err: errors.New("error"),
|
|
})
|
|
request.GeneralClient = clientMock
|
|
resp := RemoteCallback("http://test/test/url", serializer.UploadCallback{})
|
|
asserts.Error(resp)
|
|
clientMock.AssertExpectations(t)
|
|
}
|
|
}
|