|
|
|
package cluster
|
|
|
|
|
|
|
|
import (
|
|
|
|
model "github.com/cloudreve/Cloudreve/v3/models"
|
|
|
|
"github.com/cloudreve/Cloudreve/v3/pkg/aria2/common"
|
|
|
|
"github.com/cloudreve/Cloudreve/v3/pkg/auth"
|
|
|
|
"github.com/cloudreve/Cloudreve/v3/pkg/mq"
|
|
|
|
"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"
|
|
|
|
"io/ioutil"
|
|
|
|
"net/http"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestInitController(t *testing.T) {
|
|
|
|
assert.NotPanics(t, func() {
|
|
|
|
InitController()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSlaveController_HandleHeartBeat(t *testing.T) {
|
|
|
|
a := assert.New(t)
|
|
|
|
c := &slaveController{
|
|
|
|
masters: make(map[string]MasterInfo),
|
|
|
|
}
|
|
|
|
|
|
|
|
// first heart beat
|
|
|
|
{
|
|
|
|
_, err := c.HandleHeartBeat(&serializer.NodePingReq{
|
|
|
|
SiteID: "1",
|
|
|
|
Node: &model.Node{},
|
|
|
|
})
|
|
|
|
a.NoError(err)
|
|
|
|
|
|
|
|
_, err = c.HandleHeartBeat(&serializer.NodePingReq{
|
|
|
|
SiteID: "2",
|
|
|
|
Node: &model.Node{},
|
|
|
|
})
|
|
|
|
a.NoError(err)
|
|
|
|
|
|
|
|
a.Len(c.masters, 2)
|
|
|
|
}
|
|
|
|
|
|
|
|
// second heart beat, no fresh
|
|
|
|
{
|
|
|
|
_, err := c.HandleHeartBeat(&serializer.NodePingReq{
|
|
|
|
SiteID: "1",
|
|
|
|
SiteURL: "http://127.0.0.1",
|
|
|
|
Node: &model.Node{},
|
|
|
|
})
|
|
|
|
a.NoError(err)
|
|
|
|
a.Len(c.masters, 2)
|
|
|
|
a.Empty(c.masters["1"].URL)
|
|
|
|
}
|
|
|
|
|
|
|
|
// second heart beat, fresh
|
|
|
|
{
|
|
|
|
_, err := c.HandleHeartBeat(&serializer.NodePingReq{
|
|
|
|
SiteID: "1",
|
|
|
|
IsUpdate: true,
|
|
|
|
SiteURL: "http://127.0.0.1",
|
|
|
|
Node: &model.Node{},
|
|
|
|
})
|
|
|
|
a.NoError(err)
|
|
|
|
a.Len(c.masters, 2)
|
|
|
|
a.Equal("http://127.0.0.1", c.masters["1"].URL.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
// second heart beat, fresh, url illegal
|
|
|
|
{
|
|
|
|
_, err := c.HandleHeartBeat(&serializer.NodePingReq{
|
|
|
|
SiteID: "1",
|
|
|
|
IsUpdate: true,
|
|
|
|
SiteURL: string([]byte{0x7f}),
|
|
|
|
Node: &model.Node{},
|
|
|
|
})
|
|
|
|
a.Error(err)
|
|
|
|
a.Len(c.masters, 2)
|
|
|
|
a.Equal("http://127.0.0.1", c.masters["1"].URL.String())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type nodeMock struct {
|
|
|
|
testMock.Mock
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n nodeMock) Init(node *model.Node) {
|
|
|
|
n.Called(node)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n nodeMock) IsFeatureEnabled(feature string) bool {
|
|
|
|
args := n.Called(feature)
|
|
|
|
return args.Bool(0)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n nodeMock) SubscribeStatusChange(callback func(isActive bool, id uint)) {
|
|
|
|
n.Called(callback)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n nodeMock) Ping(req *serializer.NodePingReq) (*serializer.NodePingResp, error) {
|
|
|
|
args := n.Called(req)
|
|
|
|
return args.Get(0).(*serializer.NodePingResp), args.Error(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n nodeMock) IsActive() bool {
|
|
|
|
args := n.Called()
|
|
|
|
return args.Bool(0)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n nodeMock) GetAria2Instance() common.Aria2 {
|
|
|
|
args := n.Called()
|
|
|
|
return args.Get(0).(common.Aria2)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n nodeMock) ID() uint {
|
|
|
|
args := n.Called()
|
|
|
|
return args.Get(0).(uint)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n nodeMock) Kill() {
|
|
|
|
n.Called()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n nodeMock) IsMater() bool {
|
|
|
|
args := n.Called()
|
|
|
|
return args.Bool(0)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n nodeMock) MasterAuthInstance() auth.Auth {
|
|
|
|
args := n.Called()
|
|
|
|
return args.Get(0).(auth.Auth)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n nodeMock) SlaveAuthInstance() auth.Auth {
|
|
|
|
args := n.Called()
|
|
|
|
return args.Get(0).(auth.Auth)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n nodeMock) DBModel() *model.Node {
|
|
|
|
args := n.Called()
|
|
|
|
return args.Get(0).(*model.Node)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSlaveController_GetAria2Instance(t *testing.T) {
|
|
|
|
a := assert.New(t)
|
|
|
|
mockNode := &nodeMock{}
|
|
|
|
mockNode.On("GetAria2Instance").Return(&common.DummyAria2{})
|
|
|
|
c := &slaveController{
|
|
|
|
masters: map[string]MasterInfo{
|
|
|
|
"1": {Instance: mockNode},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// node node found
|
|
|
|
{
|
|
|
|
res, err := c.GetAria2Instance("2")
|
|
|
|
a.Nil(res)
|
|
|
|
a.Equal(ErrMasterNotFound, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// node found
|
|
|
|
{
|
|
|
|
res, err := c.GetAria2Instance("1")
|
|
|
|
a.NotNil(res)
|
|
|
|
a.NoError(err)
|
|
|
|
mockNode.AssertExpectations(t)
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
type requestMock struct {
|
|
|
|
testMock.Mock
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r requestMock) Request(method, target string, body io.Reader, opts ...request.Option) *request.Response {
|
|
|
|
return r.Called(method, target, body, opts).Get(0).(*request.Response)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSlaveController_SendNotification(t *testing.T) {
|
|
|
|
a := assert.New(t)
|
|
|
|
c := &slaveController{
|
|
|
|
masters: map[string]MasterInfo{
|
|
|
|
"1": {},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// node not exit
|
|
|
|
{
|
|
|
|
a.Equal(ErrMasterNotFound, c.SendNotification("2", "", mq.Message{}))
|
|
|
|
}
|
|
|
|
|
|
|
|
// gob encode error
|
|
|
|
{
|
|
|
|
type randomType struct{}
|
|
|
|
a.Error(c.SendNotification("1", "", mq.Message{
|
|
|
|
Content: randomType{},
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
|
|
|
// return none 200
|
|
|
|
{
|
|
|
|
mockRequest := &requestMock{}
|
|
|
|
mockRequest.On("Request", "PUT", "/api/v3/slave/notification/s1", testMock.Anything, testMock.Anything).Return(&request.Response{
|
|
|
|
Response: &http.Response{StatusCode: http.StatusConflict},
|
|
|
|
})
|
|
|
|
c := &slaveController{
|
|
|
|
masters: map[string]MasterInfo{
|
|
|
|
"1": {Client: mockRequest},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
a.Error(c.SendNotification("1", "s1", mq.Message{}))
|
|
|
|
mockRequest.AssertExpectations(t)
|
|
|
|
}
|
|
|
|
|
|
|
|
// master return error
|
|
|
|
{
|
|
|
|
mockRequest := &requestMock{}
|
|
|
|
mockRequest.On("Request", "PUT", "/api/v3/slave/notification/s2", testMock.Anything, testMock.Anything).Return(&request.Response{
|
|
|
|
Response: &http.Response{
|
|
|
|
StatusCode: 200,
|
|
|
|
Body: ioutil.NopCloser(strings.NewReader("{\"code\":1}")),
|
|
|
|
},
|
|
|
|
})
|
|
|
|
c := &slaveController{
|
|
|
|
masters: map[string]MasterInfo{
|
|
|
|
"1": {Client: mockRequest},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
a.Equal(1, c.SendNotification("1", "s2", mq.Message{}).(serializer.AppError).Code)
|
|
|
|
mockRequest.AssertExpectations(t)
|
|
|
|
}
|
|
|
|
|
|
|
|
// success
|
|
|
|
{
|
|
|
|
mockRequest := &requestMock{}
|
|
|
|
mockRequest.On("Request", "PUT", "/api/v3/slave/notification/s3", testMock.Anything, testMock.Anything).Return(&request.Response{
|
|
|
|
Response: &http.Response{
|
|
|
|
StatusCode: 200,
|
|
|
|
Body: ioutil.NopCloser(strings.NewReader("{\"code\":0}")),
|
|
|
|
},
|
|
|
|
})
|
|
|
|
c := &slaveController{
|
|
|
|
masters: map[string]MasterInfo{
|
|
|
|
"1": {Client: mockRequest},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
a.NoError(c.SendNotification("1", "s3", mq.Message{}))
|
|
|
|
mockRequest.AssertExpectations(t)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSlaveController_SubmitTask(t *testing.T) {
|
|
|
|
a := assert.New(t)
|
|
|
|
c := &slaveController{
|
|
|
|
masters: map[string]MasterInfo{
|
|
|
|
"1": {
|
|
|
|
jobTracker: map[string]bool{},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// node not exit
|
|
|
|
{
|
|
|
|
a.Equal(ErrMasterNotFound, c.SubmitTask("2", "", "", nil))
|
|
|
|
}
|
|
|
|
|
|
|
|
// success
|
|
|
|
{
|
|
|
|
submitted := false
|
|
|
|
a.NoError(c.SubmitTask("1", "", "hash", func(i interface{}) {
|
|
|
|
submitted = true
|
|
|
|
}))
|
|
|
|
a.True(submitted)
|
|
|
|
}
|
|
|
|
|
|
|
|
// job already submitted
|
|
|
|
{
|
|
|
|
submitted := false
|
|
|
|
a.NoError(c.SubmitTask("1", "", "hash", func(i interface{}) {
|
|
|
|
submitted = true
|
|
|
|
}))
|
|
|
|
a.False(submitted)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSlaveController_GetMasterInfo(t *testing.T) {
|
|
|
|
a := assert.New(t)
|
|
|
|
c := &slaveController{
|
|
|
|
masters: map[string]MasterInfo{
|
|
|
|
"1": {},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// node not exit
|
|
|
|
{
|
|
|
|
res, err := c.GetMasterInfo("2")
|
|
|
|
a.Equal(ErrMasterNotFound, err)
|
|
|
|
a.Nil(res)
|
|
|
|
}
|
|
|
|
|
|
|
|
// success
|
|
|
|
{
|
|
|
|
res, err := c.GetMasterInfo("1")
|
|
|
|
a.NoError(err)
|
|
|
|
a.NotNil(res)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSlaveController_GetOneDriveToken(t *testing.T) {
|
|
|
|
a := assert.New(t)
|
|
|
|
c := &slaveController{
|
|
|
|
masters: map[string]MasterInfo{
|
|
|
|
"1": {},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// node not exit
|
|
|
|
{
|
|
|
|
res, err := c.GetOneDriveToken("2", 1)
|
|
|
|
a.Equal(ErrMasterNotFound, err)
|
|
|
|
a.Empty(res)
|
|
|
|
}
|
|
|
|
|
|
|
|
// return none 200
|
|
|
|
{
|
|
|
|
mockRequest := &requestMock{}
|
|
|
|
mockRequest.On("Request", "GET", "/api/v3/slave/credential/onedrive/1", testMock.Anything, testMock.Anything).Return(&request.Response{
|
|
|
|
Response: &http.Response{StatusCode: http.StatusConflict},
|
|
|
|
})
|
|
|
|
c := &slaveController{
|
|
|
|
masters: map[string]MasterInfo{
|
|
|
|
"1": {Client: mockRequest},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
res, err := c.GetOneDriveToken("1", 1)
|
|
|
|
a.Error(err)
|
|
|
|
a.Empty(res)
|
|
|
|
mockRequest.AssertExpectations(t)
|
|
|
|
}
|
|
|
|
|
|
|
|
// master return error
|
|
|
|
{
|
|
|
|
mockRequest := &requestMock{}
|
|
|
|
mockRequest.On("Request", "GET", "/api/v3/slave/credential/onedrive/1", testMock.Anything, testMock.Anything).Return(&request.Response{
|
|
|
|
Response: &http.Response{
|
|
|
|
StatusCode: 200,
|
|
|
|
Body: ioutil.NopCloser(strings.NewReader("{\"code\":1}")),
|
|
|
|
},
|
|
|
|
})
|
|
|
|
c := &slaveController{
|
|
|
|
masters: map[string]MasterInfo{
|
|
|
|
"1": {Client: mockRequest},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
res, err := c.GetOneDriveToken("1", 1)
|
|
|
|
a.Equal(1, err.(serializer.AppError).Code)
|
|
|
|
a.Empty(res)
|
|
|
|
mockRequest.AssertExpectations(t)
|
|
|
|
}
|
|
|
|
|
|
|
|
// success
|
|
|
|
{
|
|
|
|
mockRequest := &requestMock{}
|
|
|
|
mockRequest.On("Request", "GET", "/api/v3/slave/credential/onedrive/1", testMock.Anything, testMock.Anything).Return(&request.Response{
|
|
|
|
Response: &http.Response{
|
|
|
|
StatusCode: 200,
|
|
|
|
Body: ioutil.NopCloser(strings.NewReader("{\"data\":\"expected\"}")),
|
|
|
|
},
|
|
|
|
})
|
|
|
|
c := &slaveController{
|
|
|
|
masters: map[string]MasterInfo{
|
|
|
|
"1": {Client: mockRequest},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
res, err := c.GetOneDriveToken("1", 1)
|
|
|
|
a.NoError(err)
|
|
|
|
a.Equal("expected", res)
|
|
|
|
mockRequest.AssertExpectations(t)
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|