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

     1  package filesystem
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"github.com/DATA-DOG/go-sqlmock"
     7  	"os"
     8  	"testing"
     9  
    10  	"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/response"
    11  	testMock "github.com/stretchr/testify/mock"
    12  
    13  	model "github.com/cloudreve/Cloudreve/v3/models"
    14  	"github.com/cloudreve/Cloudreve/v3/pkg/cache"
    15  	"github.com/cloudreve/Cloudreve/v3/pkg/conf"
    16  	"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/fsctx"
    17  	"github.com/cloudreve/Cloudreve/v3/pkg/serializer"
    18  	"github.com/cloudreve/Cloudreve/v3/pkg/util"
    19  	"github.com/jinzhu/gorm"
    20  	"github.com/stretchr/testify/assert"
    21  )
    22  
    23  func TestFileSystem_ListPhysical(t *testing.T) {
    24  	asserts := assert.New(t)
    25  	fs := &FileSystem{
    26  		User: &model.User{
    27  			Model: gorm.Model{
    28  				ID: 1,
    29  			},
    30  		},
    31  		Policy: &model.Policy{Type: "mock"},
    32  	}
    33  	ctx := context.Background()
    34  
    35  	// 未知存储策略
    36  	{
    37  		fs.Policy.Type = "unknown"
    38  		res, err := fs.ListPhysical(ctx, "/")
    39  		asserts.Equal(ErrUnknownPolicyType, err)
    40  		asserts.Empty(res)
    41  		fs.Policy.Type = "mock"
    42  	}
    43  
    44  	// 无法列取目录
    45  	{
    46  		testHandler := new(FileHeaderMock)
    47  		testHandler.On("List", testMock.Anything, "/", testMock.Anything).Return([]response.Object{}, errors.New("error"))
    48  		fs.Handler = testHandler
    49  		res, err := fs.ListPhysical(ctx, "/")
    50  		asserts.EqualError(err, "error")
    51  		asserts.Empty(res)
    52  	}
    53  
    54  	// 成功
    55  	{
    56  		testHandler := new(FileHeaderMock)
    57  		testHandler.On("List", testMock.Anything, "/", testMock.Anything).Return(
    58  			[]response.Object{{IsDir: true, Name: "1"}, {IsDir: false, Name: "2"}},
    59  			nil,
    60  		)
    61  		fs.Handler = testHandler
    62  		res, err := fs.ListPhysical(ctx, "/")
    63  		asserts.NoError(err)
    64  		asserts.Len(res, 1)
    65  		asserts.Equal("1", res[0].Name)
    66  	}
    67  }
    68  
    69  func TestFileSystem_List(t *testing.T) {
    70  	asserts := assert.New(t)
    71  	fs := &FileSystem{User: &model.User{
    72  		Model: gorm.Model{
    73  			ID: 1,
    74  		},
    75  	}}
    76  	ctx := context.Background()
    77  
    78  	// 成功,子目录包含文件和路径,不使用路径处理钩子
    79  	// 根目录
    80  	mock.ExpectQuery("SELECT(.+)").
    81  		WithArgs(1).
    82  		WillReturnRows(sqlmock.NewRows([]string{"id", "name", "owner_id"}).AddRow(1, "/", 1))
    83  	// folder
    84  	mock.ExpectQuery("SELECT(.+)").
    85  		WithArgs(1, 1, "folder").
    86  		WillReturnRows(sqlmock.NewRows([]string{"id", "name", "owner_id"}).AddRow(5, "folder", 1))
    87  
    88  	mock.ExpectQuery("SELECT(.+)folder(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(6, "sub_folder1").AddRow(7, "sub_folder2"))
    89  	mock.ExpectQuery("SELECT(.+)file(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(6, "sub_file1.txt").AddRow(7, "sub_file2.txt"))
    90  	objects, err := fs.List(ctx, "/folder", nil)
    91  	asserts.Len(objects, 4)
    92  	asserts.NoError(err)
    93  	asserts.NoError(mock.ExpectationsWereMet())
    94  
    95  	// 成功,子目录包含文件和路径,不使用路径处理钩子,包含分享key
    96  	// 根目录
    97  	mock.ExpectQuery("SELECT(.+)").
    98  		WithArgs(1).
    99  		WillReturnRows(sqlmock.NewRows([]string{"id", "name", "owner_id"}).AddRow(1, "/", 1))
   100  	// folder
   101  	mock.ExpectQuery("SELECT(.+)").
   102  		WithArgs(1, 1, "folder").
   103  		WillReturnRows(sqlmock.NewRows([]string{"id", "name", "owner_id"}).AddRow(5, "folder", 1))
   104  
   105  	mock.ExpectQuery("SELECT(.+)folder(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(6, "sub_folder1").AddRow(7, "sub_folder2"))
   106  	mock.ExpectQuery("SELECT(.+)file(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(6, "sub_file1.txt").AddRow(7, "sub_file2.txt"))
   107  	ctxWithKey := context.WithValue(ctx, fsctx.ShareKeyCtx, "share")
   108  	objects, err = fs.List(ctxWithKey, "/folder", nil)
   109  	asserts.Len(objects, 4)
   110  	asserts.Equal("share", objects[3].Key)
   111  	asserts.NoError(err)
   112  	asserts.NoError(mock.ExpectationsWereMet())
   113  
   114  	// 成功,子目录包含文件和路径,使用路径处理钩子
   115  	mock.ExpectQuery("SELECT(.+)").
   116  		WithArgs(1).
   117  		WillReturnRows(sqlmock.NewRows([]string{"id", "name", "owner_id"}).AddRow(1, "/", 1))
   118  	// folder
   119  	mock.ExpectQuery("SELECT(.+)").
   120  		WithArgs(1, 1, "folder").
   121  		WillReturnRows(sqlmock.NewRows([]string{"id", "name", "owner_id"}).AddRow(2, "folder", 1))
   122  
   123  	mock.ExpectQuery("SELECT(.+)folder(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "name", "position"}).AddRow(6, "sub_folder1", "/folder").AddRow(7, "sub_folder2", "/folder"))
   124  	mock.ExpectQuery("SELECT(.+)file(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "name", "dir"}).AddRow(6, "sub_file1.txt", "/folder").AddRow(7, "sub_file2.txt", "/folder"))
   125  	objects, err = fs.List(ctx, "/folder", func(s string) string {
   126  		return "prefix" + s
   127  	})
   128  	asserts.Len(objects, 4)
   129  	asserts.NoError(err)
   130  	asserts.NoError(mock.ExpectationsWereMet())
   131  	for _, value := range objects {
   132  		asserts.Contains(value.Path, "prefix/")
   133  	}
   134  
   135  	// 成功,子目录包含路径,使用路径处理钩子
   136  	mock.ExpectQuery("SELECT(.+)").
   137  		WithArgs(1).
   138  		WillReturnRows(sqlmock.NewRows([]string{"id", "name", "owner_id"}).AddRow(1, "/", 1))
   139  	// folder
   140  	mock.ExpectQuery("SELECT(.+)").
   141  		WithArgs(1, 1, "folder").
   142  		WillReturnRows(sqlmock.NewRows([]string{"id", "name", "owner_id"}).AddRow(2, "folder", 1))
   143  
   144  	mock.ExpectQuery("SELECT(.+)folder(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "name", "position"}))
   145  	mock.ExpectQuery("SELECT(.+)file(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "name", "dir"}).AddRow(6, "sub_file1.txt", "/folder").AddRow(7, "sub_file2.txt", "/folder"))
   146  	objects, err = fs.List(ctx, "/folder", func(s string) string {
   147  		return "prefix" + s
   148  	})
   149  	asserts.Len(objects, 2)
   150  	asserts.NoError(err)
   151  	asserts.NoError(mock.ExpectationsWereMet())
   152  	for _, value := range objects {
   153  		asserts.Contains(value.Path, "prefix/")
   154  	}
   155  
   156  	// 成功,子目录下为空,使用路径处理钩子
   157  	mock.ExpectQuery("SELECT(.+)").
   158  		WithArgs(1).
   159  		WillReturnRows(sqlmock.NewRows([]string{"id", "name", "owner_id"}).AddRow(1, "/", 1))
   160  	// folder
   161  	mock.ExpectQuery("SELECT(.+)").
   162  		WithArgs(1, 1, "folder").
   163  		WillReturnRows(sqlmock.NewRows([]string{"id", "name", "owner_id"}).AddRow(2, "folder", 1))
   164  
   165  	mock.ExpectQuery("SELECT(.+)folder(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "name", "position"}))
   166  	mock.ExpectQuery("SELECT(.+)file(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "name", "dir"}))
   167  	objects, err = fs.List(ctx, "/folder", func(s string) string {
   168  		return "prefix" + s
   169  	})
   170  	asserts.Len(objects, 0)
   171  	asserts.NoError(err)
   172  	asserts.NoError(mock.ExpectationsWereMet())
   173  
   174  	// 成功,子目录路径不存在
   175  	mock.ExpectQuery("SELECT(.+)").
   176  		WithArgs(1).
   177  		WillReturnRows(sqlmock.NewRows([]string{"id", "name", "owner_id"}).AddRow(1, "/", 1))
   178  	// folder
   179  	mock.ExpectQuery("SELECT(.+)").
   180  		WithArgs(1, 1, "folder").
   181  		WillReturnRows(sqlmock.NewRows([]string{"id", "name", "owner_id"}))
   182  
   183  	objects, err = fs.List(ctx, "/folder", func(s string) string {
   184  		return "prefix" + s
   185  	})
   186  	asserts.Len(objects, 0)
   187  	asserts.NoError(mock.ExpectationsWereMet())
   188  }
   189  
   190  func TestFileSystem_CreateDirectory(t *testing.T) {
   191  	asserts := assert.New(t)
   192  	fs := &FileSystem{User: &model.User{
   193  		Model: gorm.Model{
   194  			ID: 1,
   195  		},
   196  	}}
   197  	ctx := context.Background()
   198  
   199  	// 目录名非法
   200  	_, err := fs.CreateDirectory(ctx, "/ad/a+?")
   201  	asserts.Equal(ErrIllegalObjectName, err)
   202  
   203  	// 存在同名文件
   204  	// 根目录
   205  	mock.ExpectQuery("SELECT(.+)").
   206  		WithArgs(1).
   207  		WillReturnRows(sqlmock.NewRows([]string{"id", "owner_id"}).AddRow(1, 1))
   208  	// ad
   209  	mock.ExpectQuery("SELECT(.+)").
   210  		WithArgs(1, 1, "ad").
   211  		WillReturnRows(sqlmock.NewRows([]string{"id", "owner_id"}).AddRow(2, 1))
   212  
   213  	mock.ExpectQuery("SELECT(.+)files").WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(1, "ab"))
   214  	_, err = fs.CreateDirectory(ctx, "/ad/ab")
   215  	asserts.Equal(ErrFileExisted, err)
   216  	asserts.NoError(mock.ExpectationsWereMet())
   217  
   218  	// 存在同名目录,直接返回
   219  	mock.ExpectQuery("SELECT(.+)").
   220  		WithArgs(1).
   221  		WillReturnRows(sqlmock.NewRows([]string{"id", "owner_id"}).AddRow(1, 1))
   222  	// ad
   223  	mock.ExpectQuery("SELECT(.+)").
   224  		WithArgs(1, 1, "ad").
   225  		WillReturnRows(sqlmock.NewRows([]string{"id", "owner_id"}).AddRow(2, 1))
   226  
   227  	mock.ExpectQuery("SELECT(.+)files").WillReturnRows(sqlmock.NewRows([]string{"id", "name"}))
   228  	// ab
   229  	mock.ExpectQuery("SELECT(.+)").
   230  		WithArgs("ab", 2, 1).
   231  		WillReturnRows(sqlmock.NewRows([]string{"id", "owner_id"}).AddRow(3, 1))
   232  	res, err := fs.CreateDirectory(ctx, "/ad/ab")
   233  	asserts.NoError(err)
   234  	asserts.EqualValues(3, res.ID)
   235  	asserts.NoError(mock.ExpectationsWereMet())
   236  
   237  	// 成功创建
   238  	mock.ExpectQuery("SELECT(.+)").
   239  		WithArgs(1).
   240  		WillReturnRows(sqlmock.NewRows([]string{"id", "owner_id"}).AddRow(1, 1))
   241  	// ad
   242  	mock.ExpectQuery("SELECT(.+)").
   243  		WithArgs(1, 1, "ad").
   244  		WillReturnRows(sqlmock.NewRows([]string{"id", "owner_id"}).AddRow(2, 1))
   245  
   246  	mock.ExpectQuery("SELECT(.+)files").WillReturnRows(sqlmock.NewRows([]string{"id", "name"}))
   247  	mock.ExpectQuery("SELECT(.+)").
   248  		WithArgs("ab", 2, 1).
   249  		WillReturnRows(sqlmock.NewRows([]string{"id", "owner_id"}))
   250  	mock.ExpectBegin()
   251  	mock.ExpectExec("INSERT(.+)").WillReturnResult(sqlmock.NewResult(1, 1))
   252  	mock.ExpectCommit()
   253  	_, err = fs.CreateDirectory(ctx, "/ad/ab")
   254  	asserts.NoError(err)
   255  	asserts.NoError(mock.ExpectationsWereMet())
   256  
   257  	// 成功创建, 递归创建父目录
   258  	// 根目录
   259  	mock.ExpectQuery("SELECT(.+)").
   260  		WithArgs(1).
   261  		WillReturnRows(sqlmock.NewRows([]string{"id", "owner_id"}).AddRow(1, 1))
   262  	// ad
   263  	mock.ExpectQuery("SELECT(.+)").
   264  		WithArgs(1, 1, "ad").
   265  		WillReturnRows(sqlmock.NewRows([]string{"id", "owner_id"}))
   266  	// 根目录
   267  	mock.ExpectQuery("SELECT(.+)").
   268  		WithArgs(1).
   269  		WillReturnRows(sqlmock.NewRows([]string{"id", "owner_id"}).AddRow(1, 1))
   270  	mock.ExpectQuery("SELECT(.+)files").WillReturnRows(sqlmock.NewRows([]string{"id", "name"}))
   271  	// 创建ad
   272  	mock.ExpectQuery("SELECT(.+)").
   273  		WithArgs("ad", 1, 1).
   274  		WillReturnRows(sqlmock.NewRows([]string{"id", "owner_id"}))
   275  	mock.ExpectBegin()
   276  	mock.ExpectExec("INSERT(.+)").WillReturnResult(sqlmock.NewResult(2, 1))
   277  	mock.ExpectCommit()
   278  	mock.ExpectQuery("SELECT(.+)files").WillReturnRows(sqlmock.NewRows([]string{"id", "name"}))
   279  	// 创建ab
   280  	mock.ExpectQuery("SELECT(.+)").
   281  		WithArgs("ab", 2, 1).
   282  		WillReturnRows(sqlmock.NewRows([]string{"id", "owner_id"}))
   283  	mock.ExpectBegin()
   284  	mock.ExpectExec("INSERT(.+)").WillReturnResult(sqlmock.NewResult(1, 1))
   285  	mock.ExpectCommit()
   286  	_, err = fs.CreateDirectory(ctx, "/ad/ab")
   287  	asserts.NoError(err)
   288  	asserts.NoError(mock.ExpectationsWereMet())
   289  
   290  	// 底层创建失败
   291  	// 成功创建
   292  	mock.ExpectQuery("SELECT(.+)").
   293  		WithArgs(1).
   294  		WillReturnRows(sqlmock.NewRows([]string{"id", "owner_id"}).AddRow(1, 1))
   295  	// ad
   296  	mock.ExpectQuery("SELECT(.+)").
   297  		WithArgs(1, 1, "ad").
   298  		WillReturnRows(sqlmock.NewRows([]string{"id", "owner_id"}))
   299  	// 根目录
   300  	mock.ExpectQuery("SELECT(.+)").
   301  		WithArgs(1).
   302  		WillReturnRows(sqlmock.NewRows([]string{"id", "owner_id"}).AddRow(1, 1))
   303  	mock.ExpectQuery("SELECT(.+)files").WillReturnRows(sqlmock.NewRows([]string{"id", "name"}))
   304  	// 创建ad
   305  	mock.ExpectQuery("SELECT(.+)").
   306  		WithArgs("ad", 1, 1).
   307  		WillReturnRows(sqlmock.NewRows([]string{"id", "owner_id"}))
   308  	mock.ExpectBegin()
   309  	mock.ExpectExec("INSERT(.+)").WillReturnResult(sqlmock.NewResult(2, 1)).WillReturnError(errors.New("error"))
   310  	mock.ExpectRollback()
   311  	mock.ExpectQuery("SELECT(.+)").
   312  		WillReturnError(errors.New("error"))
   313  	_, err = fs.CreateDirectory(ctx, "/ad/ab")
   314  	asserts.Error(err)
   315  	asserts.NoError(mock.ExpectationsWereMet())
   316  
   317  	// 直接创建根目录
   318  	mock.ExpectQuery("SELECT(.+)").
   319  		WithArgs(1).
   320  		WillReturnRows(sqlmock.NewRows([]string{"id", "owner_id"}).AddRow(1, 1))
   321  	_, err = fs.CreateDirectory(ctx, "/")
   322  	asserts.NoError(err)
   323  	asserts.NoError(mock.ExpectationsWereMet())
   324  
   325  	// 直接创建根目录, 重设根目录
   326  	fs.Root = &model.Folder{}
   327  	_, err = fs.CreateDirectory(ctx, "/")
   328  	asserts.NoError(err)
   329  	asserts.NoError(mock.ExpectationsWereMet())
   330  }
   331  
   332  func TestFileSystem_ListDeleteFiles(t *testing.T) {
   333  	conf.DatabaseConfig.Type = "mysql"
   334  	asserts := assert.New(t)
   335  	fs := &FileSystem{User: &model.User{
   336  		Model: gorm.Model{
   337  			ID: 1,
   338  		},
   339  	}}
   340  
   341  	// 成功
   342  	{
   343  		mock.ExpectQuery("SELECT(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(1, "1.txt").AddRow(2, "2.txt"))
   344  		err := fs.ListDeleteFiles(context.Background(), []uint{1})
   345  		asserts.NoError(err)
   346  		asserts.NoError(mock.ExpectationsWereMet())
   347  	}
   348  
   349  	// 失败
   350  	{
   351  		mock.ExpectQuery("SELECT(.+)").WillReturnError(errors.New("error"))
   352  		err := fs.ListDeleteFiles(context.Background(), []uint{1})
   353  		asserts.Error(err)
   354  		asserts.Equal(serializer.CodeDBError, err.(serializer.AppError).Code)
   355  		asserts.NoError(mock.ExpectationsWereMet())
   356  	}
   357  }
   358  
   359  func TestFileSystem_ListDeleteDirs(t *testing.T) {
   360  	conf.DatabaseConfig.Type = "mysql"
   361  	asserts := assert.New(t)
   362  	fs := &FileSystem{User: &model.User{
   363  		Model: gorm.Model{
   364  			ID: 1,
   365  		},
   366  	}}
   367  
   368  	// 成功
   369  	{
   370  		mock.ExpectQuery("SELECT(.+)").
   371  			WillReturnRows(
   372  				sqlmock.NewRows([]string{"id", "parent_id"}).
   373  					AddRow(1, 0).
   374  					AddRow(2, 0).
   375  					AddRow(3, 0),
   376  			)
   377  		mock.ExpectQuery("SELECT(.+)files(.+)").
   378  			WithArgs(1, 2, 3).
   379  			WillReturnRows(
   380  				sqlmock.NewRows([]string{"id", "name"}).
   381  					AddRow(4, "1.txt").
   382  					AddRow(5, "2.txt").
   383  					AddRow(6, "3.txt"),
   384  			)
   385  		err := fs.ListDeleteDirs(context.Background(), []uint{1})
   386  		asserts.NoError(err)
   387  		asserts.Len(fs.FileTarget, 3)
   388  		asserts.Len(fs.DirTarget, 3)
   389  		asserts.NoError(mock.ExpectationsWereMet())
   390  	}
   391  
   392  	// 成功,忽略根目录
   393  	{
   394  		mock.ExpectQuery("SELECT(.+)").
   395  			WillReturnRows(
   396  				sqlmock.NewRows([]string{"id", "parent_id"}).
   397  					AddRow(1, 0).
   398  					AddRow(2, nil).
   399  					AddRow(3, 0),
   400  			)
   401  		mock.ExpectQuery("SELECT(.+)files(.+)").
   402  			WithArgs(1, 3).
   403  			WillReturnRows(
   404  				sqlmock.NewRows([]string{"id", "name"}).
   405  					AddRow(4, "1.txt").
   406  					AddRow(5, "2.txt").
   407  					AddRow(6, "3.txt"),
   408  			)
   409  		fs.CleanTargets()
   410  		err := fs.ListDeleteDirs(context.Background(), []uint{1})
   411  		asserts.NoError(err)
   412  		asserts.Len(fs.FileTarget, 3)
   413  		asserts.Len(fs.DirTarget, 2)
   414  		asserts.NoError(mock.ExpectationsWereMet())
   415  	}
   416  
   417  	// 检索文件发生错误
   418  	{
   419  		mock.ExpectQuery("SELECT(.+)").
   420  			WillReturnRows(
   421  				sqlmock.NewRows([]string{"id", "parent_id"}).
   422  					AddRow(1, 0).
   423  					AddRow(2, 0).
   424  					AddRow(3, 0),
   425  			)
   426  		mock.ExpectQuery("SELECT(.+)").
   427  			WithArgs(1, 2, 3).
   428  			WillReturnError(errors.New("error"))
   429  		fs.CleanTargets()
   430  		err := fs.ListDeleteDirs(context.Background(), []uint{1})
   431  		asserts.Error(err)
   432  		asserts.Len(fs.DirTarget, 3)
   433  		asserts.NoError(mock.ExpectationsWereMet())
   434  	}
   435  	// 检索目录发生错误
   436  	{
   437  		mock.ExpectQuery("SELECT(.+)").
   438  			WillReturnError(errors.New("error"))
   439  		err := fs.ListDeleteDirs(context.Background(), []uint{1})
   440  		asserts.Error(err)
   441  		asserts.NoError(mock.ExpectationsWereMet())
   442  	}
   443  }
   444  
   445  func TestFileSystem_Delete(t *testing.T) {
   446  	conf.DatabaseConfig.Type = "mysql"
   447  	asserts := assert.New(t)
   448  	cache.Set("pack_size_1", uint64(0), 0)
   449  	fs := &FileSystem{User: &model.User{
   450  		Model: gorm.Model{
   451  			ID: 0,
   452  		},
   453  		Storage: 3,
   454  		Group:   model.Group{MaxStorage: 3},
   455  	}}
   456  	ctx := context.Background()
   457  
   458  	//全部未成功,强制
   459  	{
   460  		fs.CleanTargets()
   461  		mock.ExpectQuery("SELECT(.+)").
   462  			WillReturnRows(
   463  				sqlmock.NewRows([]string{"id", "parent_id"}).
   464  					AddRow(1, 0).
   465  					AddRow(2, 0).
   466  					AddRow(3, 0),
   467  			)
   468  		mock.ExpectQuery("SELECT(.+)").
   469  			WithArgs(1, 2, 3).
   470  			WillReturnRows(
   471  				sqlmock.NewRows([]string{"id", "name", "source_name", "policy_id", "size"}).
   472  					AddRow(4, "1.txt", "1.txt", 365, 1),
   473  			)
   474  		mock.ExpectQuery("SELECT(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "name", "source_name", "policy_id", "size"}).AddRow(1, "2.txt", "2.txt", 365, 2))
   475  		// 两次查询软连接
   476  		mock.ExpectQuery("SELECT(.+)files(.+)").
   477  			WillReturnRows(sqlmock.NewRows([]string{"id", "policy_id", "source_name"}))
   478  		mock.ExpectQuery("SELECT(.+)files(.+)").
   479  			WillReturnRows(sqlmock.NewRows([]string{"id", "policy_id", "source_name"}))
   480  		// 查询上传策略
   481  		mock.ExpectQuery("SELECT(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "type"}).AddRow(365, "local"))
   482  		// 删除文件记录
   483  		mock.ExpectBegin()
   484  		mock.ExpectExec("DELETE(.+)").
   485  			WillReturnResult(sqlmock.NewResult(0, 1))
   486  		mock.ExpectExec("DELETE(.+)").
   487  			WillReturnResult(sqlmock.NewResult(0, 1))
   488  		mock.ExpectCommit()
   489  		// 删除对应分享
   490  		mock.ExpectBegin()
   491  		mock.ExpectExec("UPDATE(.+)shares").
   492  			WillReturnResult(sqlmock.NewResult(0, 3))
   493  		mock.ExpectCommit()
   494  		// 删除目录
   495  		mock.ExpectBegin()
   496  		mock.ExpectExec("DELETE(.+)").
   497  			WillReturnResult(sqlmock.NewResult(0, 3))
   498  		mock.ExpectCommit()
   499  		// 删除对应分享
   500  		mock.ExpectBegin()
   501  		mock.ExpectExec("UPDATE(.+)shares").
   502  			WillReturnResult(sqlmock.NewResult(0, 3))
   503  		mock.ExpectCommit()
   504  
   505  		fs.FileTarget = []model.File{}
   506  		fs.DirTarget = []model.Folder{}
   507  		err := fs.Delete(ctx, []uint{1}, []uint{1}, true, false)
   508  		asserts.NoError(err)
   509  	}
   510  	//全部成功
   511  	{
   512  		fs.CleanTargets()
   513  		file, err := os.Create(util.RelativePath("1.txt"))
   514  		file2, err := os.Create(util.RelativePath("2.txt"))
   515  		file.Close()
   516  		file2.Close()
   517  		asserts.NoError(err)
   518  		mock.ExpectQuery("SELECT(.+)").
   519  			WillReturnRows(
   520  				sqlmock.NewRows([]string{"id", "parent_id"}).
   521  					AddRow(1, 0).
   522  					AddRow(2, 0).
   523  					AddRow(3, 0),
   524  			)
   525  		mock.ExpectQuery("SELECT(.+)").
   526  			WithArgs(1, 2, 3).
   527  			WillReturnRows(
   528  				sqlmock.NewRows([]string{"id", "name", "source_name", "policy_id", "size"}).
   529  					AddRow(4, "1.txt", "1.txt", 602, 1),
   530  			)
   531  		mock.ExpectQuery("SELECT(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "name", "source_name", "policy_id", "size"}).AddRow(1, "2.txt", "2.txt", 602, 2))
   532  		// 两次查询软连接
   533  		mock.ExpectQuery("SELECT(.+)files(.+)").
   534  			WillReturnRows(sqlmock.NewRows([]string{"id", "policy_id", "source_name"}))
   535  		mock.ExpectQuery("SELECT(.+)files(.+)").
   536  			WillReturnRows(sqlmock.NewRows([]string{"id", "policy_id", "source_name"}))
   537  		// 查询上传策略
   538  		mock.ExpectQuery("SELECT(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "type"}).AddRow(602, "local"))
   539  		// 删除文件记录
   540  		mock.ExpectBegin()
   541  		mock.ExpectExec("DELETE(.+)").
   542  			WillReturnResult(sqlmock.NewResult(0, 1))
   543  		mock.ExpectExec("DELETE(.+)").
   544  			WillReturnResult(sqlmock.NewResult(0, 1))
   545  		mock.ExpectCommit()
   546  		// 删除对应分享
   547  		mock.ExpectBegin()
   548  		mock.ExpectExec("UPDATE(.+)shares").
   549  			WillReturnResult(sqlmock.NewResult(0, 3))
   550  		mock.ExpectCommit()
   551  		// 删除目录
   552  		mock.ExpectBegin()
   553  		mock.ExpectExec("DELETE(.+)").
   554  			WillReturnResult(sqlmock.NewResult(0, 3))
   555  		mock.ExpectCommit()
   556  		// 删除对应分享
   557  		mock.ExpectBegin()
   558  		mock.ExpectExec("UPDATE(.+)shares").
   559  			WillReturnResult(sqlmock.NewResult(0, 3))
   560  		mock.ExpectCommit()
   561  
   562  		fs.FileTarget = []model.File{}
   563  		fs.DirTarget = []model.Folder{}
   564  		err = fs.Delete(ctx, []uint{1}, []uint{1}, false, false)
   565  		asserts.NoError(err)
   566  	}
   567  
   568  }
   569  
   570  func TestFileSystem_Copy(t *testing.T) {
   571  	asserts := assert.New(t)
   572  	cache.Set("pack_size_1", uint64(0), 0)
   573  	fs := &FileSystem{User: &model.User{
   574  		Model: gorm.Model{
   575  			ID: 1,
   576  		},
   577  		Storage: 3,
   578  		Group:   model.Group{MaxStorage: 3},
   579  	}}
   580  	ctx := context.Background()
   581  
   582  	// 目录不存在
   583  	{
   584  		mock.ExpectQuery("SELECT(.+)").WillReturnRows(
   585  			sqlmock.NewRows([]string{"name"}),
   586  		)
   587  		mock.ExpectQuery("SELECT(.+)").WillReturnRows(
   588  			sqlmock.NewRows([]string{"name"}),
   589  		)
   590  		err := fs.Copy(ctx, []uint{}, []uint{}, "/src", "/dst")
   591  		asserts.Equal(ErrPathNotExist, err)
   592  		asserts.NoError(mock.ExpectationsWereMet())
   593  	}
   594  
   595  	// 复制目录出错
   596  	{
   597  		// 根目录
   598  		mock.ExpectQuery("SELECT(.+)").
   599  			WithArgs(1).
   600  			WillReturnRows(sqlmock.NewRows([]string{"id", "owner_id"}).AddRow(1, 1))
   601  		// 1
   602  		mock.ExpectQuery("SELECT(.+)").
   603  			WithArgs(1, 1, "dst").
   604  			WillReturnRows(sqlmock.NewRows([]string{"id", "owner_id"}).AddRow(2, 1))
   605  		// 根目录
   606  		mock.ExpectQuery("SELECT(.+)").
   607  			WithArgs(1).
   608  			WillReturnRows(sqlmock.NewRows([]string{"id", "owner_id"}).AddRow(1, 1))
   609  		// 1
   610  		mock.ExpectQuery("SELECT(.+)").
   611  			WithArgs(1, 1, "src").
   612  			WillReturnRows(sqlmock.NewRows([]string{"id", "owner_id"}).AddRow(2, 1))
   613  
   614  		err := fs.Copy(ctx, []uint{1}, []uint{}, "/src", "/dst")
   615  		asserts.Error(err)
   616  		asserts.NoError(mock.ExpectationsWereMet())
   617  	}
   618  
   619  }
   620  
   621  func TestFileSystem_Move(t *testing.T) {
   622  	asserts := assert.New(t)
   623  	cache.Set("pack_size_1", uint64(0), 0)
   624  	fs := &FileSystem{User: &model.User{
   625  		Model: gorm.Model{
   626  			ID: 1,
   627  		},
   628  		Storage: 3,
   629  		Group:   model.Group{MaxStorage: 3},
   630  	}}
   631  	ctx := context.Background()
   632  
   633  	// 目录不存在
   634  	{
   635  		mock.ExpectQuery("SELECT(.+)").WillReturnRows(
   636  			sqlmock.NewRows([]string{"name"}),
   637  		)
   638  		err := fs.Move(ctx, []uint{}, []uint{}, "/src", "/dst")
   639  		asserts.Equal(ErrPathNotExist, err)
   640  		asserts.NoError(mock.ExpectationsWereMet())
   641  	}
   642  
   643  	// 移动目录出错
   644  	{
   645  		// 根目录
   646  		mock.ExpectQuery("SELECT(.+)").
   647  			WithArgs(1).
   648  			WillReturnRows(sqlmock.NewRows([]string{"id", "owner_id"}).AddRow(1, 1))
   649  		// 1
   650  		mock.ExpectQuery("SELECT(.+)").
   651  			WithArgs(1, 1, "dst").
   652  			WillReturnRows(sqlmock.NewRows([]string{"id", "owner_id"}).AddRow(2, 1))
   653  		// 根目录
   654  		mock.ExpectQuery("SELECT(.+)").
   655  			WithArgs(1).
   656  			WillReturnRows(sqlmock.NewRows([]string{"id", "owner_id"}).AddRow(1, 1))
   657  		// 1
   658  		mock.ExpectQuery("SELECT(.+)").
   659  			WithArgs(1, 1, "src").
   660  			WillReturnRows(sqlmock.NewRows([]string{"id", "owner_id"}).AddRow(2, 1))
   661  		err := fs.Move(ctx, []uint{1}, []uint{}, "/src", "/dst")
   662  		asserts.Error(err)
   663  		asserts.NoError(mock.ExpectationsWereMet())
   664  	}
   665  }
   666  
   667  func TestFileSystem_Rename(t *testing.T) {
   668  	asserts := assert.New(t)
   669  	fs := &FileSystem{User: &model.User{
   670  		Model: gorm.Model{
   671  			ID: 1,
   672  		},
   673  	},
   674  		Policy: &model.Policy{},
   675  	}
   676  	ctx := context.Background()
   677  
   678  	// 重命名文件 成功
   679  	{
   680  		mock.ExpectQuery("SELECT(.+)files(.+)").
   681  			WithArgs(10, 1).
   682  			WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(10, "old.text"))
   683  		mock.ExpectBegin()
   684  		mock.ExpectExec("UPDATE(.+)files(.+)SET(.+)").
   685  			WithArgs(sqlmock.AnyArg(), "new.txt", sqlmock.AnyArg(), 10).
   686  			WillReturnResult(sqlmock.NewResult(1, 1))
   687  		mock.ExpectCommit()
   688  		err := fs.Rename(ctx, []uint{}, []uint{10}, "new.txt")
   689  		asserts.NoError(mock.ExpectationsWereMet())
   690  		asserts.NoError(err)
   691  	}
   692  
   693  	// 重命名文件 不存在
   694  	{
   695  		mock.ExpectQuery("SELECT(.+)files(.+)").
   696  			WithArgs(10, 1).
   697  			WillReturnRows(sqlmock.NewRows([]string{"id", "name"}))
   698  		err := fs.Rename(ctx, []uint{}, []uint{10}, "new.txt")
   699  		asserts.NoError(mock.ExpectationsWereMet())
   700  		asserts.Error(err)
   701  		asserts.Equal(ErrPathNotExist, err)
   702  	}
   703  
   704  	// 重命名文件 失败
   705  	{
   706  		mock.ExpectQuery("SELECT(.+)files(.+)").
   707  			WithArgs(10, 1).
   708  			WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(10, "old.text"))
   709  		mock.ExpectBegin()
   710  		mock.ExpectExec("UPDATE(.+)files(.+)SET(.+)").
   711  			WithArgs(sqlmock.AnyArg(), "new.txt", sqlmock.AnyArg(), 10).
   712  			WillReturnError(errors.New("error"))
   713  		mock.ExpectRollback()
   714  		err := fs.Rename(ctx, []uint{}, []uint{10}, "new.txt")
   715  		asserts.NoError(mock.ExpectationsWereMet())
   716  		asserts.Error(err)
   717  		asserts.Equal(ErrFileExisted, err)
   718  	}
   719  
   720  	// 重命名目录 成功
   721  	{
   722  		mock.ExpectQuery("SELECT(.+)folders(.+)").
   723  			WithArgs(10, 1).
   724  			WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(10, "old"))
   725  		mock.ExpectBegin()
   726  		mock.ExpectExec("UPDATE(.+)folders(.+)SET(.+)").
   727  			WithArgs("new", 10).
   728  			WillReturnResult(sqlmock.NewResult(1, 1))
   729  		mock.ExpectCommit()
   730  		err := fs.Rename(ctx, []uint{10}, []uint{}, "new")
   731  		asserts.NoError(mock.ExpectationsWereMet())
   732  		asserts.NoError(err)
   733  	}
   734  
   735  	// 重命名目录 不存在
   736  	{
   737  		mock.ExpectQuery("SELECT(.+)folders(.+)").
   738  			WithArgs(10, 1).
   739  			WillReturnRows(sqlmock.NewRows([]string{"id", "name"}))
   740  		err := fs.Rename(ctx, []uint{10}, []uint{}, "new")
   741  		asserts.NoError(mock.ExpectationsWereMet())
   742  		asserts.Error(err)
   743  		asserts.Equal(ErrPathNotExist, err)
   744  	}
   745  
   746  	// 重命名目录 失败
   747  	{
   748  		mock.ExpectQuery("SELECT(.+)folders(.+)").
   749  			WithArgs(10, 1).
   750  			WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(10, "old"))
   751  		mock.ExpectBegin()
   752  		mock.ExpectExec("UPDATE(.+)folders(.+)SET(.+)").
   753  			WithArgs("new", 10).
   754  			WillReturnError(errors.New("error"))
   755  		mock.ExpectRollback()
   756  		err := fs.Rename(ctx, []uint{10}, []uint{}, "new")
   757  		asserts.NoError(mock.ExpectationsWereMet())
   758  		asserts.Error(err)
   759  		asserts.Equal(ErrFileExisted, err)
   760  	}
   761  
   762  	// 未选中任何对象
   763  	{
   764  		err := fs.Rename(ctx, []uint{}, []uint{}, "new")
   765  		asserts.Error(err)
   766  		asserts.Equal(ErrPathNotExist, err)
   767  	}
   768  
   769  	// 新名字是目录,不合法
   770  	{
   771  		err := fs.Rename(ctx, []uint{10}, []uint{}, "ne/w")
   772  		asserts.Error(err)
   773  		asserts.Equal(ErrIllegalObjectName, err)
   774  	}
   775  
   776  	// 新名字是文件,不合法
   777  	{
   778  		err := fs.Rename(ctx, []uint{}, []uint{10}, "ne/w")
   779  		asserts.Error(err)
   780  		asserts.Equal(ErrIllegalObjectName, err)
   781  	}
   782  
   783  	// 新名字是文件,扩展名不合法
   784  	{
   785  		fs.Policy.OptionsSerialized.FileType = []string{"txt"}
   786  		err := fs.Rename(ctx, []uint{}, []uint{10}, "1.jpg")
   787  		asserts.Error(err)
   788  		asserts.Equal(ErrIllegalObjectName, err)
   789  	}
   790  
   791  	// 新名字是目录,不应该检测扩展名
   792  	{
   793  		fs.Policy.OptionsSerialized.FileType = []string{"txt"}
   794  		mock.ExpectQuery("SELECT(.+)folders(.+)").
   795  			WithArgs(10, 1).
   796  			WillReturnRows(sqlmock.NewRows([]string{"id", "name"}))
   797  		err := fs.Rename(ctx, []uint{10}, []uint{}, "new")
   798  		asserts.NoError(mock.ExpectationsWereMet())
   799  		asserts.Error(err)
   800  		asserts.Equal(ErrPathNotExist, err)
   801  	}
   802  }
   803  
   804  func TestFileSystem_SaveTo(t *testing.T) {
   805  	asserts := assert.New(t)
   806  	fs := &FileSystem{User: &model.User{
   807  		Model: gorm.Model{
   808  			ID: 1,
   809  		},
   810  	}}
   811  	ctx := context.Background()
   812  
   813  	// 单文件 失败
   814  	{
   815  		// 根目录
   816  		mock.ExpectQuery("SELECT(.+)").
   817  			WithArgs(1).
   818  			WillReturnRows(sqlmock.NewRows([]string{"id", "owner_id"}).AddRow(1, 1))
   819  		mock.ExpectQuery("SELECT(.+)").WillReturnError(errors.New("error"))
   820  		fs.SetTargetFile(&[]model.File{{Name: "test.txt"}})
   821  		err := fs.SaveTo(ctx, "/")
   822  		asserts.NoError(mock.ExpectationsWereMet())
   823  		asserts.Error(err)
   824  	}
   825  	// 目录 成功
   826  	{
   827  		// 根目录
   828  		mock.ExpectQuery("SELECT(.+)").
   829  			WithArgs(1).
   830  			WillReturnRows(sqlmock.NewRows([]string{"id", "owner_id"}).AddRow(1, 1))
   831  		mock.ExpectQuery("SELECT(.+)").WillReturnError(errors.New("error"))
   832  		fs.SetTargetDir(&[]model.Folder{{Name: "folder"}})
   833  		err := fs.SaveTo(ctx, "/")
   834  		asserts.NoError(mock.ExpectationsWereMet())
   835  		asserts.Error(err)
   836  	}
   837  	// 父目录不存在
   838  	{
   839  		// 根目录
   840  		mock.ExpectQuery("SELECT(.+)").
   841  			WithArgs(1).
   842  			WillReturnRows(sqlmock.NewRows([]string{"id", "owner_id"}))
   843  		fs.SetTargetDir(&[]model.Folder{{Name: "folder"}})
   844  		err := fs.SaveTo(ctx, "/")
   845  		asserts.NoError(mock.ExpectationsWereMet())
   846  		asserts.Error(err)
   847  	}
   848  }