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  }