github.com/cloudreve/Cloudreve/v3@v3.0.0-20240224133659-3edb00a6484c/pkg/filesystem/upload_test.go (about)

     1  package filesystem
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"github.com/DATA-DOG/go-sqlmock"
     7  	model "github.com/cloudreve/Cloudreve/v3/models"
     8  	"github.com/cloudreve/Cloudreve/v3/pkg/cache"
     9  	"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/fsctx"
    10  	"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/response"
    11  	"github.com/cloudreve/Cloudreve/v3/pkg/serializer"
    12  	"github.com/gin-gonic/gin"
    13  	"github.com/jinzhu/gorm"
    14  	"github.com/stretchr/testify/assert"
    15  	testMock "github.com/stretchr/testify/mock"
    16  	"io/ioutil"
    17  	"net/http"
    18  	"net/http/httptest"
    19  	"strings"
    20  	"testing"
    21  )
    22  
    23  type FileHeaderMock struct {
    24  	testMock.Mock
    25  }
    26  
    27  func (m FileHeaderMock) Put(ctx context.Context, file fsctx.FileHeader) error {
    28  	args := m.Called(ctx, file)
    29  	return args.Error(0)
    30  }
    31  
    32  func (m FileHeaderMock) Token(ctx context.Context, ttl int64, uploadSession *serializer.UploadSession, file fsctx.FileHeader) (*serializer.UploadCredential, error) {
    33  	args := m.Called(ctx, ttl, uploadSession, file)
    34  	return args.Get(0).(*serializer.UploadCredential), args.Error(1)
    35  }
    36  
    37  func (m FileHeaderMock) CancelToken(ctx context.Context, uploadSession *serializer.UploadSession) error {
    38  	args := m.Called(ctx, uploadSession)
    39  	return args.Error(0)
    40  }
    41  
    42  func (m FileHeaderMock) List(ctx context.Context, path string, recursive bool) ([]response.Object, error) {
    43  	args := m.Called(ctx, path, recursive)
    44  	return args.Get(0).([]response.Object), args.Error(1)
    45  }
    46  
    47  func (m FileHeaderMock) Get(ctx context.Context, path string) (response.RSCloser, error) {
    48  	args := m.Called(ctx, path)
    49  	return args.Get(0).(response.RSCloser), args.Error(1)
    50  }
    51  
    52  func (m FileHeaderMock) Delete(ctx context.Context, files []string) ([]string, error) {
    53  	args := m.Called(ctx, files)
    54  	return args.Get(0).([]string), args.Error(1)
    55  }
    56  
    57  func (m FileHeaderMock) Thumb(ctx context.Context, files *model.File) (*response.ContentResponse, error) {
    58  	args := m.Called(ctx, files)
    59  	return args.Get(0).(*response.ContentResponse), args.Error(1)
    60  }
    61  
    62  func (m FileHeaderMock) Source(ctx context.Context, path string, expires int64, isDownload bool, speed int) (string, error) {
    63  	args := m.Called(ctx, path, expires, isDownload, speed)
    64  	return args.Get(0).(string), args.Error(1)
    65  }
    66  
    67  func TestFileSystem_Upload(t *testing.T) {
    68  	asserts := assert.New(t)
    69  
    70  	// 正常
    71  	testHandler := new(FileHeaderMock)
    72  	testHandler.On("Put", testMock.Anything, testMock.Anything, testMock.Anything).Return(nil)
    73  	fs := &FileSystem{
    74  		Handler: testHandler,
    75  		User: &model.User{
    76  			Model: gorm.Model{
    77  				ID: 1,
    78  			},
    79  		},
    80  		Policy: &model.Policy{
    81  			AutoRename:  false,
    82  			DirNameRule: "{path}",
    83  		},
    84  	}
    85  	ctx, cancel := context.WithCancel(context.Background())
    86  	c, _ := gin.CreateTestContext(httptest.NewRecorder())
    87  	c.Request, _ = http.NewRequest("POST", "/", nil)
    88  	ctx = context.WithValue(ctx, fsctx.GinCtx, c)
    89  	cancel()
    90  	file := &fsctx.FileStream{
    91  		Size:        5,
    92  		VirtualPath: "/",
    93  		Name:        "1.txt",
    94  	}
    95  	err := fs.Upload(ctx, file)
    96  	asserts.NoError(err)
    97  
    98  	// 正常,上下文已指定源文件
    99  	testHandler = new(FileHeaderMock)
   100  	testHandler.On("Put", testMock.Anything, testMock.Anything).Return(nil)
   101  	fs = &FileSystem{
   102  		Handler: testHandler,
   103  		User: &model.User{
   104  			Model: gorm.Model{
   105  				ID: 1,
   106  			},
   107  		},
   108  		Policy: &model.Policy{
   109  			AutoRename:  false,
   110  			DirNameRule: "{path}",
   111  		},
   112  	}
   113  	ctx, cancel = context.WithCancel(context.Background())
   114  	c, _ = gin.CreateTestContext(httptest.NewRecorder())
   115  	c.Request, _ = http.NewRequest("POST", "/", nil)
   116  	ctx = context.WithValue(ctx, fsctx.GinCtx, c)
   117  	ctx = context.WithValue(ctx, fsctx.FileModelCtx, model.File{SourceName: "123/123.txt"})
   118  	cancel()
   119  	file = &fsctx.FileStream{
   120  		Size:        5,
   121  		VirtualPath: "/",
   122  		Name:        "1.txt",
   123  		File:        ioutil.NopCloser(strings.NewReader("")),
   124  	}
   125  	err = fs.Upload(ctx, file)
   126  	asserts.NoError(err)
   127  
   128  	// BeforeUpload 返回错误
   129  	fs.Use("BeforeUpload", func(ctx context.Context, fs *FileSystem, file fsctx.FileHeader) error {
   130  		return errors.New("error")
   131  	})
   132  	err = fs.Upload(ctx, file)
   133  	asserts.Error(err)
   134  	fs.Hooks["BeforeUpload"] = nil
   135  	testHandler.AssertExpectations(t)
   136  
   137  	// 上传文件失败
   138  	testHandler2 := new(FileHeaderMock)
   139  	testHandler2.On("Put", testMock.Anything, testMock.Anything).Return(errors.New("error"))
   140  	fs.Handler = testHandler2
   141  	err = fs.Upload(ctx, file)
   142  	asserts.Error(err)
   143  	testHandler2.AssertExpectations(t)
   144  
   145  	// AfterUpload失败
   146  	testHandler3 := new(FileHeaderMock)
   147  	testHandler3.On("Put", testMock.Anything, testMock.Anything).Return(nil)
   148  	fs.Handler = testHandler3
   149  	fs.Use("AfterUpload", func(ctx context.Context, fs *FileSystem, file fsctx.FileHeader) error {
   150  		return errors.New("error")
   151  	})
   152  	fs.Use("AfterValidateFailed", func(ctx context.Context, fs *FileSystem, file fsctx.FileHeader) error {
   153  		return errors.New("error")
   154  	})
   155  	err = fs.Upload(ctx, file)
   156  	asserts.Error(err)
   157  	testHandler2.AssertExpectations(t)
   158  
   159  }
   160  
   161  func TestFileSystem_GetUploadToken(t *testing.T) {
   162  	asserts := assert.New(t)
   163  	fs := FileSystem{
   164  		User:   &model.User{Model: gorm.Model{ID: 1}},
   165  		Policy: &model.Policy{},
   166  	}
   167  	ctx := context.Background()
   168  
   169  	// 成功
   170  	{
   171  		cache.SetSettings(map[string]string{
   172  			"upload_session_timeout": "10",
   173  		}, "setting_")
   174  		testHandler := new(FileHeaderMock)
   175  		testHandler.On("Token", testMock.Anything, int64(10), testMock.Anything, testMock.Anything).Return(&serializer.UploadCredential{Credential: "test"}, nil)
   176  		fs.Handler = testHandler
   177  		mock.ExpectQuery("SELECT(.+)").
   178  			WithArgs(1).
   179  			WillReturnRows(sqlmock.NewRows([]string{"id", "owner_id"}).AddRow(1, 1))
   180  		mock.ExpectQuery("SELECT(.+)files(.+)").WillReturnError(errors.New("not found"))
   181  		mock.ExpectBegin()
   182  		mock.ExpectExec("INSERT(.+)files(.+)").WillReturnResult(sqlmock.NewResult(1, 1))
   183  		mock.ExpectExec("UPDATE(.+)storage(.+)").WillReturnResult(sqlmock.NewResult(1, 1))
   184  		mock.ExpectCommit()
   185  		res, err := fs.CreateUploadSession(ctx, &fsctx.FileStream{
   186  			Size:        0,
   187  			Name:        "file",
   188  			VirtualPath: "/",
   189  		})
   190  		asserts.NoError(mock.ExpectationsWereMet())
   191  		testHandler.AssertExpectations(t)
   192  		asserts.NoError(err)
   193  		asserts.Equal("test", res.Credential)
   194  	}
   195  
   196  	// 无法获取上传凭证
   197  	{
   198  		cache.SetSettings(map[string]string{
   199  			"upload_credential_timeout": "10",
   200  			"upload_session_timeout":    "10",
   201  		}, "setting_")
   202  		testHandler := new(FileHeaderMock)
   203  		testHandler.On("Token", testMock.Anything, int64(10), testMock.Anything, testMock.Anything).Return(&serializer.UploadCredential{}, errors.New("error"))
   204  		fs.Handler = testHandler
   205  		mock.ExpectQuery("SELECT(.+)").
   206  			WithArgs(1).
   207  			WillReturnRows(sqlmock.NewRows([]string{"id", "owner_id"}).AddRow(1, 1))
   208  		mock.ExpectQuery("SELECT(.+)files(.+)").WillReturnError(errors.New("not found"))
   209  		mock.ExpectBegin()
   210  		mock.ExpectExec("INSERT(.+)files(.+)").WillReturnResult(sqlmock.NewResult(1, 1))
   211  		mock.ExpectExec("UPDATE(.+)storage(.+)").WillReturnResult(sqlmock.NewResult(1, 1))
   212  		mock.ExpectCommit()
   213  		_, err := fs.CreateUploadSession(ctx, &fsctx.FileStream{
   214  			Size:        0,
   215  			Name:        "file",
   216  			VirtualPath: "/",
   217  		})
   218  		asserts.NoError(mock.ExpectationsWereMet())
   219  		testHandler.AssertExpectations(t)
   220  		asserts.Error(err)
   221  	}
   222  }
   223  
   224  func TestFileSystem_UploadFromStream(t *testing.T) {
   225  	asserts := assert.New(t)
   226  	fs := FileSystem{
   227  		User: &model.User{
   228  			Model:  gorm.Model{ID: 1},
   229  			Policy: model.Policy{Type: "mock"},
   230  		},
   231  		Policy: &model.Policy{Type: "mock"},
   232  	}
   233  	ctx := context.Background()
   234  
   235  	err := fs.UploadFromStream(ctx, &fsctx.FileStream{
   236  		File: ioutil.NopCloser(strings.NewReader("123")),
   237  	}, true)
   238  	asserts.Error(err)
   239  }
   240  
   241  func TestFileSystem_UploadFromPath(t *testing.T) {
   242  	asserts := assert.New(t)
   243  	fs := FileSystem{
   244  		User: &model.User{
   245  			Model:  gorm.Model{ID: 1},
   246  			Policy: model.Policy{Type: "mock"},
   247  		},
   248  		Policy: &model.Policy{Type: "mock"},
   249  	}
   250  	ctx := context.Background()
   251  
   252  	// 文件不存在
   253  	{
   254  		err := fs.UploadFromPath(ctx, "test/not_exist", "/", fsctx.Overwrite)
   255  		asserts.Error(err)
   256  	}
   257  
   258  	// 文存在,上传失败
   259  	{
   260  		err := fs.UploadFromPath(ctx, "tests/test.zip", "/", fsctx.Overwrite)
   261  		asserts.Error(err)
   262  	}
   263  }