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

     1  package filesystem
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"os"
     7  	"testing"
     8  
     9  	"github.com/DATA-DOG/go-sqlmock"
    10  	model "github.com/cloudreve/Cloudreve/v3/models"
    11  	"github.com/cloudreve/Cloudreve/v3/pkg/auth"
    12  	"github.com/cloudreve/Cloudreve/v3/pkg/cache"
    13  	"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/fsctx"
    14  	"github.com/cloudreve/Cloudreve/v3/pkg/serializer"
    15  	"github.com/cloudreve/Cloudreve/v3/pkg/util"
    16  	"github.com/jinzhu/gorm"
    17  	"github.com/stretchr/testify/assert"
    18  )
    19  
    20  func TestFileSystem_AddFile(t *testing.T) {
    21  	asserts := assert.New(t)
    22  	file := fsctx.FileStream{
    23  		Size:     5,
    24  		Name:     "1.png",
    25  		SavePath: "/Uploads/1_sad.png",
    26  	}
    27  	folder := model.Folder{
    28  		Model: gorm.Model{
    29  			ID: 1,
    30  		},
    31  	}
    32  	fs := FileSystem{
    33  		User: &model.User{
    34  			Model: gorm.Model{
    35  				ID: 1,
    36  			},
    37  			Policy: model.Policy{
    38  				Type: "cos",
    39  				Model: gorm.Model{
    40  					ID: 1,
    41  				},
    42  			},
    43  		},
    44  		Policy: &model.Policy{Type: "cos"},
    45  	}
    46  
    47  	_, err := fs.AddFile(context.Background(), &folder, &file)
    48  
    49  	asserts.Error(err)
    50  
    51  	mock.ExpectBegin()
    52  	mock.ExpectExec("INSERT(.+)").WillReturnResult(sqlmock.NewResult(1, 1))
    53  	mock.ExpectExec("UPDATE(.+)storage(.+)").WillReturnResult(sqlmock.NewResult(1, 1))
    54  	mock.ExpectCommit()
    55  
    56  	f, err := fs.AddFile(context.Background(), &folder, &file)
    57  
    58  	asserts.NoError(err)
    59  	asserts.NoError(mock.ExpectationsWereMet())
    60  	asserts.Equal("/Uploads/1_sad.png", f.SourceName)
    61  
    62  	// 前置钩子执行失败
    63  	{
    64  		hookExecuted := false
    65  		fs.Use("BeforeAddFile", func(ctx context.Context, fs *FileSystem, file fsctx.FileHeader) error {
    66  			hookExecuted = true
    67  			return errors.New("error")
    68  		})
    69  		f, err := fs.AddFile(context.Background(), &folder, &file)
    70  		asserts.Error(err)
    71  		asserts.Nil(f)
    72  		asserts.True(hookExecuted)
    73  	}
    74  
    75  	// 后置钩子执行失败
    76  	{
    77  		hookExecuted := false
    78  		mock.ExpectBegin()
    79  		mock.ExpectExec("INSERT(.+)").WillReturnError(errors.New("error"))
    80  		mock.ExpectRollback()
    81  		fs.Hooks = map[string][]Hook{}
    82  		fs.Use("AfterValidateFailed", func(ctx context.Context, fs *FileSystem, file fsctx.FileHeader) error {
    83  			hookExecuted = true
    84  			return errors.New("error")
    85  		})
    86  		f, err := fs.AddFile(context.Background(), &folder, &file)
    87  		asserts.Error(err)
    88  		asserts.Nil(f)
    89  		asserts.True(hookExecuted)
    90  		asserts.NoError(mock.ExpectationsWereMet())
    91  	}
    92  }
    93  
    94  func TestFileSystem_GetContent(t *testing.T) {
    95  	asserts := assert.New(t)
    96  	ctx := context.Background()
    97  	fs := FileSystem{
    98  		User: &model.User{
    99  			Model: gorm.Model{
   100  				ID: 1,
   101  			},
   102  			Policy: model.Policy{
   103  				Model: gorm.Model{
   104  					ID: 1,
   105  				},
   106  			},
   107  		},
   108  	}
   109  
   110  	// 文件不存在
   111  	rs, err := fs.GetContent(ctx, 1)
   112  	asserts.Equal(ErrObjectNotExist, err)
   113  	asserts.Nil(rs)
   114  	fs.CleanTargets()
   115  
   116  	// 未知存储策略
   117  	file, err := os.Create(util.RelativePath("TestFileSystem_GetContent.txt"))
   118  	asserts.NoError(err)
   119  	_ = file.Close()
   120  
   121  	cache.Deletes([]string{"1"}, "policy_")
   122  	mock.ExpectQuery("SELECT(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "source_name", "policy_id"}).AddRow(1, "TestFileSystem_GetContent.txt", 1))
   123  	mock.ExpectQuery("SELECT(.+)poli(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "type"}).AddRow(1, "unknown"))
   124  
   125  	rs, err = fs.GetContent(ctx, 1)
   126  	asserts.Error(err)
   127  	asserts.NoError(mock.ExpectationsWereMet())
   128  	fs.CleanTargets()
   129  
   130  	// 打开文件失败
   131  	cache.Deletes([]string{"1"}, "policy_")
   132  	mock.ExpectQuery("SELECT(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "source_name", "policy_id"}).AddRow(1, "TestFileSystem_GetContent2.txt", 1))
   133  	mock.ExpectQuery("SELECT(.+)poli(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "type", "source_name"}).AddRow(1, "local", "not exist"))
   134  
   135  	rs, err = fs.GetContent(ctx, 1)
   136  	asserts.Equal(serializer.CodeIOFailed, err.(serializer.AppError).Code)
   137  	asserts.NoError(mock.ExpectationsWereMet())
   138  	fs.CleanTargets()
   139  
   140  	// 打开成功
   141  	cache.Deletes([]string{"1"}, "policy_")
   142  	mock.ExpectQuery("SELECT(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "source_name", "policy_id", "source_name"}).AddRow(1, "TestFileSystem_GetContent.txt", 1, "TestFileSystem_GetContent.txt"))
   143  	mock.ExpectQuery("SELECT(.+)poli(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "type"}).AddRow(1, "local"))
   144  
   145  	rs, err = fs.GetContent(ctx, 1)
   146  	asserts.NoError(err)
   147  	asserts.NoError(mock.ExpectationsWereMet())
   148  }
   149  
   150  func TestFileSystem_GetDownloadContent(t *testing.T) {
   151  	asserts := assert.New(t)
   152  	ctx := context.Background()
   153  	fs := FileSystem{
   154  		User: &model.User{
   155  			Model: gorm.Model{
   156  				ID: 1,
   157  			},
   158  			Policy: model.Policy{
   159  				Model: gorm.Model{
   160  					ID: 599,
   161  				},
   162  			},
   163  		},
   164  	}
   165  	file, err := os.Create(util.RelativePath("TestFileSystem_GetDownloadContent.txt"))
   166  	asserts.NoError(err)
   167  	_ = file.Close()
   168  
   169  	cache.Deletes([]string{"599"}, "policy_")
   170  	mock.ExpectQuery("SELECT(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "name", "policy_id", "source_name"}).AddRow(1, "TestFileSystem_GetDownloadContent.txt", 599, "TestFileSystem_GetDownloadContent.txt"))
   171  	mock.ExpectQuery("SELECT(.+)poli(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "type"}).AddRow(1, "local"))
   172  
   173  	// 无限速
   174  	cache.Deletes([]string{"599"}, "policy_")
   175  	_, err = fs.GetDownloadContent(ctx, 1)
   176  	asserts.NoError(err)
   177  	asserts.NoError(mock.ExpectationsWereMet())
   178  	fs.CleanTargets()
   179  
   180  	// 有限速
   181  	cache.Deletes([]string{"599"}, "policy_")
   182  	mock.ExpectQuery("SELECT(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "name", "policy_id", "source_name"}).AddRow(1, "TestFileSystem_GetDownloadContent.txt", 599, "TestFileSystem_GetDownloadContent.txt"))
   183  	mock.ExpectQuery("SELECT(.+)poli(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "type"}).AddRow(1, "local"))
   184  
   185  	fs.User.Group.SpeedLimit = 1
   186  	_, err = fs.GetDownloadContent(ctx, 1)
   187  	asserts.NoError(err)
   188  	asserts.NoError(mock.ExpectationsWereMet())
   189  }
   190  
   191  func TestFileSystem_GroupFileByPolicy(t *testing.T) {
   192  	asserts := assert.New(t)
   193  	ctx := context.Background()
   194  	files := []model.File{
   195  		model.File{
   196  			PolicyID: 1,
   197  			Name:     "1_1.txt",
   198  		},
   199  		model.File{
   200  			PolicyID: 2,
   201  			Name:     "2_1.txt",
   202  		},
   203  		model.File{
   204  			PolicyID: 3,
   205  			Name:     "3_1.txt",
   206  		},
   207  		model.File{
   208  			PolicyID: 2,
   209  			Name:     "2_2.txt",
   210  		},
   211  		model.File{
   212  			PolicyID: 1,
   213  			Name:     "1_2.txt",
   214  		},
   215  	}
   216  	fs := FileSystem{}
   217  	policyGroup := fs.GroupFileByPolicy(ctx, files)
   218  	asserts.Equal(map[uint][]*model.File{
   219  		1: {&files[0], &files[4]},
   220  		2: {&files[1], &files[3]},
   221  		3: {&files[2]},
   222  	}, policyGroup)
   223  }
   224  
   225  func TestFileSystem_deleteGroupedFile(t *testing.T) {
   226  	asserts := assert.New(t)
   227  	ctx := context.Background()
   228  	fs := FileSystem{}
   229  	files := []model.File{
   230  		{
   231  			PolicyID:   1,
   232  			Name:       "1_1.txt",
   233  			SourceName: "1_1.txt",
   234  			Policy:     model.Policy{Model: gorm.Model{ID: 1}, Type: "local"},
   235  		},
   236  		{
   237  			PolicyID:   2,
   238  			Name:       "2_1.txt",
   239  			SourceName: "2_1.txt",
   240  			Policy:     model.Policy{Model: gorm.Model{ID: 1}, Type: "local"},
   241  		},
   242  		{
   243  			PolicyID:   3,
   244  			Name:       "3_1.txt",
   245  			SourceName: "3_1.txt",
   246  			Policy:     model.Policy{Model: gorm.Model{ID: 1}, Type: "local"},
   247  		},
   248  		{
   249  			PolicyID:   2,
   250  			Name:       "2_2.txt",
   251  			SourceName: "2_2.txt",
   252  			Policy:     model.Policy{Model: gorm.Model{ID: 1}, Type: "local"},
   253  		},
   254  		{
   255  			PolicyID:   1,
   256  			Name:       "1_2.txt",
   257  			SourceName: "1_2.txt",
   258  			Policy:     model.Policy{Model: gorm.Model{ID: 1}, Type: "local"},
   259  		},
   260  	}
   261  
   262  	// 全部不存在
   263  	{
   264  		failed := fs.deleteGroupedFile(ctx, fs.GroupFileByPolicy(ctx, files))
   265  		asserts.Equal(map[uint][]string{
   266  			1: {},
   267  			2: {},
   268  			3: {},
   269  		}, failed)
   270  	}
   271  	// 部分不存在
   272  	{
   273  		file, err := os.Create(util.RelativePath("1_1.txt"))
   274  		asserts.NoError(err)
   275  		_ = file.Close()
   276  		failed := fs.deleteGroupedFile(ctx, fs.GroupFileByPolicy(ctx, files))
   277  		asserts.Equal(map[uint][]string{
   278  			1: {},
   279  			2: {},
   280  			3: {},
   281  		}, failed)
   282  	}
   283  	// 部分失败,包含整组未知存储策略导致的失败
   284  	{
   285  		file, err := os.Create(util.RelativePath("1_1.txt"))
   286  		asserts.NoError(err)
   287  		_ = file.Close()
   288  
   289  		files[1].Policy.Type = "unknown"
   290  		files[3].Policy.Type = "unknown"
   291  		failed := fs.deleteGroupedFile(ctx, fs.GroupFileByPolicy(ctx, files))
   292  		asserts.Equal(map[uint][]string{
   293  			1: {},
   294  			2: {"2_1.txt", "2_2.txt"},
   295  			3: {},
   296  		}, failed)
   297  	}
   298  	// 包含上传会话文件
   299  	{
   300  		sessionID := "session"
   301  		cache.Set(UploadSessionCachePrefix+sessionID, serializer.UploadSession{Key: sessionID}, 0)
   302  		files[1].Policy.Type = "local"
   303  		files[3].Policy.Type = "local"
   304  		files[0].UploadSessionID = &sessionID
   305  		failed := fs.deleteGroupedFile(ctx, fs.GroupFileByPolicy(ctx, files))
   306  		asserts.Equal(map[uint][]string{
   307  			1: {},
   308  			2: {},
   309  			3: {},
   310  		}, failed)
   311  		_, ok := cache.Get(UploadSessionCachePrefix + sessionID)
   312  		asserts.False(ok)
   313  	}
   314  
   315  	// 包含缩略图
   316  	{
   317  		files[0].MetadataSerialized = map[string]string{
   318  			model.ThumbSidecarMetadataKey: "1",
   319  		}
   320  		failed := fs.deleteGroupedFile(ctx, fs.GroupFileByPolicy(ctx, files))
   321  		asserts.Equal(map[uint][]string{
   322  			1: {},
   323  			2: {},
   324  			3: {},
   325  		}, failed)
   326  	}
   327  }
   328  
   329  func TestFileSystem_GetSource(t *testing.T) {
   330  	asserts := assert.New(t)
   331  	ctx := context.Background()
   332  	auth.General = auth.HMACAuth{SecretKey: []byte("123")}
   333  
   334  	// 正常
   335  	{
   336  		fs := FileSystem{
   337  			User: &model.User{Model: gorm.Model{ID: 1}},
   338  		}
   339  		// 清空缓存
   340  		err := cache.Deletes([]string{"siteURL"}, "setting_")
   341  		asserts.NoError(err)
   342  		// 查找文件
   343  		mock.ExpectQuery("SELECT(.+)").
   344  			WithArgs(2, 1).
   345  			WillReturnRows(
   346  				sqlmock.NewRows([]string{"id", "policy_id", "source_name"}).
   347  					AddRow(2, 35, "1.txt"),
   348  			)
   349  		// 查找上传策略
   350  		mock.ExpectQuery("SELECT(.+)").
   351  			WillReturnRows(
   352  				sqlmock.NewRows([]string{"id", "type", "is_origin_link_enable"}).
   353  					AddRow(35, "local", true),
   354  			)
   355  
   356  		sourceURL, err := fs.GetSource(ctx, 2)
   357  		asserts.NoError(mock.ExpectationsWereMet())
   358  		asserts.NoError(err)
   359  		asserts.NotEmpty(sourceURL)
   360  		fs.CleanTargets()
   361  	}
   362  
   363  	// 文件不存在
   364  	{
   365  		fs := FileSystem{
   366  			User: &model.User{Model: gorm.Model{ID: 1}},
   367  		}
   368  		// 清空缓存
   369  		err := cache.Deletes([]string{"siteURL"}, "setting_")
   370  		asserts.NoError(err)
   371  		// 查找文件
   372  		mock.ExpectQuery("SELECT(.+)").
   373  			WithArgs(2, 1).
   374  			WillReturnRows(
   375  				sqlmock.NewRows([]string{"id", "policy_id", "source_name"}),
   376  			)
   377  
   378  		sourceURL, err := fs.GetSource(ctx, 2)
   379  		asserts.NoError(mock.ExpectationsWereMet())
   380  		asserts.Error(err)
   381  		asserts.Equal(ErrObjectNotExist.Code, err.(serializer.AppError).Code)
   382  		asserts.Empty(sourceURL)
   383  		fs.CleanTargets()
   384  	}
   385  
   386  	// 未知上传策略
   387  	{
   388  		fs := FileSystem{
   389  			User: &model.User{Model: gorm.Model{ID: 1}},
   390  		}
   391  		// 清空缓存
   392  		err := cache.Deletes([]string{"siteURL"}, "setting_")
   393  		asserts.NoError(err)
   394  		// 查找文件
   395  		mock.ExpectQuery("SELECT(.+)").
   396  			WithArgs(2, 1).
   397  			WillReturnRows(
   398  				sqlmock.NewRows([]string{"id", "policy_id", "source_name"}).
   399  					AddRow(2, 36, "1.txt"),
   400  			)
   401  		// 查找上传策略
   402  		mock.ExpectQuery("SELECT(.+)").
   403  			WillReturnRows(
   404  				sqlmock.NewRows([]string{"id", "type", "is_origin_link_enable"}).
   405  					AddRow(36, "?", true),
   406  			)
   407  
   408  		sourceURL, err := fs.GetSource(ctx, 2)
   409  		asserts.NoError(mock.ExpectationsWereMet())
   410  		asserts.Error(err)
   411  		asserts.Empty(sourceURL)
   412  		fs.CleanTargets()
   413  	}
   414  
   415  	// 不允许获取外链
   416  	{
   417  		fs := FileSystem{
   418  			User: &model.User{Model: gorm.Model{ID: 1}},
   419  		}
   420  		// 清空缓存
   421  		err := cache.Deletes([]string{"siteURL"}, "setting_")
   422  		asserts.NoError(err)
   423  		// 查找文件
   424  		mock.ExpectQuery("SELECT(.+)").
   425  			WithArgs(2, 1).
   426  			WillReturnRows(
   427  				sqlmock.NewRows([]string{"id", "policy_id", "source_name"}).
   428  					AddRow(2, 37, "1.txt"),
   429  			)
   430  		// 查找上传策略
   431  		mock.ExpectQuery("SELECT(.+)").
   432  			WillReturnRows(
   433  				sqlmock.NewRows([]string{"id", "type", "is_origin_link_enable"}).
   434  					AddRow(37, "local", false),
   435  			)
   436  
   437  		sourceURL, err := fs.GetSource(ctx, 2)
   438  		asserts.NoError(mock.ExpectationsWereMet())
   439  		asserts.Error(err)
   440  		asserts.Equal(serializer.CodePolicyNotAllowed, err.(serializer.AppError).Code)
   441  		asserts.Empty(sourceURL)
   442  		fs.CleanTargets()
   443  	}
   444  }
   445  
   446  func TestFileSystem_GetDownloadURL(t *testing.T) {
   447  	asserts := assert.New(t)
   448  	ctx := context.Background()
   449  	fs := FileSystem{
   450  		User: &model.User{Model: gorm.Model{ID: 1}},
   451  	}
   452  	auth.General = auth.HMACAuth{SecretKey: []byte("123")}
   453  
   454  	// 正常
   455  	{
   456  		err := cache.Deletes([]string{"35"}, "policy_")
   457  		cache.Set("setting_download_timeout", "20", 0)
   458  		cache.Set("setting_siteURL", "https://cloudreve.org", 0)
   459  		asserts.NoError(err)
   460  		// 查找文件
   461  		mock.ExpectQuery("SELECT(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "name", "policy_id"}).AddRow(1, "1.txt", 35))
   462  		// 查找上传策略
   463  		mock.ExpectQuery("SELECT(.+)").
   464  			WillReturnRows(
   465  				sqlmock.NewRows([]string{"id", "type", "is_origin_link_enable"}).
   466  					AddRow(35, "local", true),
   467  			)
   468  		// 相关设置
   469  		downloadURL, err := fs.GetDownloadURL(ctx, 1, "download_timeout")
   470  		asserts.NoError(mock.ExpectationsWereMet())
   471  		asserts.NoError(err)
   472  		asserts.NotEmpty(downloadURL)
   473  		fs.CleanTargets()
   474  	}
   475  
   476  	// 文件不存在
   477  	{
   478  		err := cache.Deletes([]string{"siteURL"}, "setting_")
   479  		err = cache.Deletes([]string{"35"}, "policy_")
   480  		err = cache.Deletes([]string{"download_timeout"}, "setting_")
   481  		asserts.NoError(err)
   482  		// 查找文件
   483  		mock.ExpectQuery("SELECT(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "name", "policy_id"}))
   484  
   485  		downloadURL, err := fs.GetDownloadURL(ctx, 1, "download_timeout")
   486  		asserts.NoError(mock.ExpectationsWereMet())
   487  		asserts.Error(err)
   488  		asserts.Empty(downloadURL)
   489  		fs.CleanTargets()
   490  	}
   491  
   492  	// 未知存储策略
   493  	{
   494  		err := cache.Deletes([]string{"siteURL"}, "setting_")
   495  		err = cache.Deletes([]string{"35"}, "policy_")
   496  		err = cache.Deletes([]string{"download_timeout"}, "setting_")
   497  		asserts.NoError(err)
   498  		// 查找文件
   499  		mock.ExpectQuery("SELECT(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "name", "policy_id"}).AddRow(1, "1.txt", 35))
   500  		// 查找上传策略
   501  		mock.ExpectQuery("SELECT(.+)").
   502  			WillReturnRows(
   503  				sqlmock.NewRows([]string{"id", "type", "is_origin_link_enable"}).
   504  					AddRow(35, "unknown", true),
   505  			)
   506  
   507  		downloadURL, err := fs.GetDownloadURL(ctx, 1, "download_timeout")
   508  		asserts.NoError(mock.ExpectationsWereMet())
   509  		asserts.Error(err)
   510  		asserts.Empty(downloadURL)
   511  		fs.CleanTargets()
   512  	}
   513  }
   514  
   515  func TestFileSystem_GetPhysicalFileContent(t *testing.T) {
   516  	asserts := assert.New(t)
   517  	ctx := context.Background()
   518  	fs := FileSystem{
   519  		User: &model.User{},
   520  	}
   521  
   522  	// 文件不存在
   523  	{
   524  		rs, err := fs.GetPhysicalFileContent(ctx, "not_exist.txt")
   525  		asserts.Error(err)
   526  		asserts.Nil(rs)
   527  	}
   528  
   529  	// 成功
   530  	{
   531  		testFile, err := os.Create(util.RelativePath("GetPhysicalFileContent.txt"))
   532  		asserts.NoError(err)
   533  		asserts.NoError(testFile.Close())
   534  
   535  		rs, err := fs.GetPhysicalFileContent(ctx, "GetPhysicalFileContent.txt")
   536  		asserts.NoError(err)
   537  		asserts.NoError(rs.Close())
   538  		asserts.NotNil(rs)
   539  	}
   540  }
   541  
   542  func TestFileSystem_Preview(t *testing.T) {
   543  	asserts := assert.New(t)
   544  	ctx := context.Background()
   545  
   546  	// 文件不存在
   547  	{
   548  		fs := FileSystem{
   549  			User: &model.User{},
   550  		}
   551  		mock.ExpectQuery("SELECT(.+)").WillReturnRows(sqlmock.NewRows([]string{"id"}))
   552  		resp, err := fs.Preview(ctx, 1, false)
   553  		asserts.NoError(mock.ExpectationsWereMet())
   554  		asserts.Error(err)
   555  		asserts.Nil(resp)
   556  	}
   557  
   558  	// 直接返回文件内容,找不到文件
   559  	{
   560  		fs := FileSystem{
   561  			User: &model.User{},
   562  		}
   563  		fs.FileTarget = []model.File{
   564  			{
   565  				SourceName: "tests/no.txt",
   566  				PolicyID:   1,
   567  				Policy: model.Policy{
   568  					Model: gorm.Model{ID: 1},
   569  					Type:  "local",
   570  				},
   571  			},
   572  		}
   573  		resp, err := fs.Preview(ctx, 1, false)
   574  		asserts.Error(err)
   575  		asserts.Nil(resp)
   576  	}
   577  
   578  	// 直接返回文件内容
   579  	{
   580  		fs := FileSystem{
   581  			User: &model.User{},
   582  		}
   583  		fs.FileTarget = []model.File{
   584  			{
   585  				SourceName: "tests/file1.txt",
   586  				PolicyID:   1,
   587  				Policy: model.Policy{
   588  					Model: gorm.Model{ID: 1},
   589  					Type:  "local",
   590  				},
   591  			},
   592  		}
   593  		resp, err := fs.Preview(ctx, 1, false)
   594  		asserts.Error(err)
   595  		asserts.Nil(resp)
   596  	}
   597  
   598  	// 需要重定向,成功
   599  	{
   600  		fs := FileSystem{
   601  			User: &model.User{},
   602  		}
   603  		fs.FileTarget = []model.File{
   604  			{
   605  				SourceName: "tests/file1.txt",
   606  				PolicyID:   1,
   607  				Policy: model.Policy{
   608  					Model: gorm.Model{ID: 1},
   609  					Type:  "remote",
   610  				},
   611  			},
   612  		}
   613  		asserts.NoError(cache.Set("setting_preview_timeout", "233", 0))
   614  		resp, err := fs.Preview(ctx, 1, false)
   615  		asserts.NoError(err)
   616  		asserts.NotNil(resp)
   617  		asserts.True(resp.Redirect)
   618  	}
   619  
   620  	// 文本文件,大小超出限制
   621  	{
   622  		fs := FileSystem{
   623  			User: &model.User{},
   624  		}
   625  		fs.FileTarget = []model.File{
   626  			{
   627  				SourceName: "tests/file1.txt",
   628  				PolicyID:   1,
   629  				Policy: model.Policy{
   630  					Model: gorm.Model{ID: 1},
   631  					Type:  "remote",
   632  				},
   633  				Size: 11,
   634  			},
   635  		}
   636  		asserts.NoError(cache.Set("setting_maxEditSize", "10", 0))
   637  		resp, err := fs.Preview(ctx, 1, true)
   638  		asserts.Equal(ErrFileSizeTooBig, err)
   639  		asserts.Nil(resp)
   640  	}
   641  }
   642  
   643  func TestFileSystem_ResetFileIDIfNotExist(t *testing.T) {
   644  	asserts := assert.New(t)
   645  	ctx := context.WithValue(context.Background(), fsctx.LimitParentCtx, &model.Folder{Model: gorm.Model{ID: 1}})
   646  	fs := FileSystem{
   647  		FileTarget: []model.File{
   648  			{
   649  				FolderID: 2,
   650  			},
   651  		},
   652  	}
   653  	asserts.Equal(ErrObjectNotExist, fs.resetFileIDIfNotExist(ctx, 1))
   654  }
   655  
   656  func TestFileSystem_Search(t *testing.T) {
   657  	asserts := assert.New(t)
   658  	ctx := context.Background()
   659  	fs := &FileSystem{
   660  		User: &model.User{},
   661  	}
   662  	fs.User.ID = 1
   663  
   664  	mock.ExpectQuery("SELECT(.+)").WithArgs(1, "k1", "k2").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(1))
   665  	res, err := fs.Search(ctx, "k1", "k2")
   666  	asserts.NoError(mock.ExpectationsWereMet())
   667  	asserts.NoError(err)
   668  	asserts.Len(res, 1)
   669  }