diff --git a/models/file.go b/models/file.go index 19d5fc8..89984ee 100644 --- a/models/file.go +++ b/models/file.go @@ -61,7 +61,6 @@ func GetChildFilesOfFolders(folders *[]Folder) ([]File, error) { } // GetPolicy 获取文件所属策略 -// TODO:test func (file *File) GetPolicy() *Policy { if file.Policy.Model.ID == 0 { file.Policy, _ = GetPolicyByID(file.PolicyID) @@ -91,7 +90,7 @@ func RemoveFilesWithSoftLinks(files []File) ([]File, error) { var filesWithSoftLinks []File tx := DB for _, value := range files { - tx = tx.Or("source_name = ? and policy_id = ? and id != ?", value.SourceName, value.GetPolicy().ID, value.ID) + tx = tx.Or("source_name = ? and policy_id = ? and id != ?", value.SourceName, value.PolicyID, value.ID) } result := tx.Find(&filesWithSoftLinks) if result.Error != nil { @@ -99,16 +98,22 @@ func RemoveFilesWithSoftLinks(files []File) ([]File, error) { } // 过滤具有软连接的文件 + // TODO: 优化复杂度 if len(filesWithSoftLinks) == 0 { filteredFiles = files } else { for i := 0; i < len(files); i++ { + finder := false for _, value := range filesWithSoftLinks { - if value.PolicyID != files[i].PolicyID || value.SourceName != files[i].SourceName { - filteredFiles = append(filteredFiles, files[i]) + if value.PolicyID == files[i].PolicyID && value.SourceName == files[i].SourceName { + finder = true break } } + if !finder { + filteredFiles = append(filteredFiles, files[i]) + } + } } diff --git a/models/file_test.go b/models/file_test.go index 2c1be0b..952fbc4 100644 --- a/models/file_test.go +++ b/models/file_test.go @@ -66,3 +66,227 @@ func TestFolder_GetChildFile(t *testing.T) { asserts.NoError(mock.ExpectationsWereMet()) } + +func TestGetChildFilesOfFolders(t *testing.T) { + asserts := assert.New(t) + testFolder := []Folder{ + Folder{ + Model: gorm.Model{ID: 3}, + }, + Folder{ + Model: gorm.Model{ID: 4}, + }, Folder{ + Model: gorm.Model{ID: 5}, + }, + } + + // 出错 + { + mock.ExpectQuery("SELECT(.+)folder_id").WithArgs(3, 4, 5).WillReturnError(errors.New("not found")) + files, err := GetChildFilesOfFolders(&testFolder) + asserts.Error(err) + asserts.Len(files, 0) + asserts.NoError(mock.ExpectationsWereMet()) + } + + // 找到2个 + { + mock.ExpectQuery("SELECT(.+)folder_id"). + WithArgs(3, 4, 5). + WillReturnRows(sqlmock.NewRows([]string{"id", "name"}). + AddRow(3, "3"). + AddRow(4, "4"), + ) + files, err := GetChildFilesOfFolders(&testFolder) + asserts.NoError(err) + asserts.Len(files, 2) + asserts.NoError(mock.ExpectationsWereMet()) + } + + // 全部找到 + { + mock.ExpectQuery("SELECT(.+)folder_id"). + WithArgs(3, 4, 5). + WillReturnRows(sqlmock.NewRows([]string{"id", "name"}). + AddRow(3, "3"). + AddRow(4, "4"). + AddRow(5, "5"), + ) + files, err := GetChildFilesOfFolders(&testFolder) + asserts.NoError(err) + asserts.Len(files, 3) + asserts.NoError(mock.ExpectationsWereMet()) + } +} + +func TestFile_GetPolicy(t *testing.T) { + asserts := assert.New(t) + + // 空策略 + { + file := File{ + PolicyID: 23, + } + mock.ExpectQuery("SELECT(.+)policies(.+)"). + WillReturnRows( + sqlmock.NewRows([]string{"id", "name"}). + AddRow(23, "name"), + ) + file.GetPolicy() + asserts.NoError(mock.ExpectationsWereMet()) + asserts.Equal(uint(23), file.Policy.ID) + } + + // 非空策略 + { + file := File{ + PolicyID: 23, + Policy: Policy{Model: gorm.Model{ID: 24}}, + } + file.GetPolicy() + asserts.NoError(mock.ExpectationsWereMet()) + asserts.Equal(uint(24), file.Policy.ID) + } +} + +func TestGetFileByPaths(t *testing.T) { + asserts := assert.New(t) + paths := []string{"/我的目录/文件.txt", "/根目录文件.txt"} + + // 正常情况 + { + mock.ExpectQuery("SELECT(.+)files(.+)"). + WithArgs("/我的目录", "文件.txt", 1, "/", "根目录文件.txt", 1). + WillReturnRows( + sqlmock.NewRows([]string{"id", "name"}). + AddRow(1, "文件.txt"). + AddRow(2, "根目录文件.txt"), + ) + files, err := GetFileByPaths(paths, 1) + asserts.NoError(mock.ExpectationsWereMet()) + asserts.NoError(err) + asserts.Equal([]File{ + File{ + Model: gorm.Model{ID: 1}, + Name: "文件.txt", + }, + File{ + Model: gorm.Model{ID: 2}, + Name: "根目录文件.txt", + }, + }, files) + } + + // 出错 + { + mock.ExpectQuery("SELECT(.+)files(.+)"). + WithArgs("/我的目录", "文件.txt", 1, "/", "根目录文件.txt", 1). + WillReturnError(errors.New("error")) + files, err := GetFileByPaths(paths, 1) + asserts.NoError(mock.ExpectationsWereMet()) + asserts.Error(err) + asserts.Len(files, 0) + } +} + +func TestRemoveFilesWithSoftLinks(t *testing.T) { + asserts := assert.New(t) + files := []File{ + File{ + Model: gorm.Model{ID: 1}, + SourceName: "1.txt", + PolicyID: 23, + }, + File{ + Model: gorm.Model{ID: 2}, + SourceName: "2.txt", + PolicyID: 24, + }, + } + + // 全都没有 + { + mock.ExpectQuery("SELECT(.+)files(.+)"). + WithArgs("1.txt", 23, 1, "2.txt", 24, 2). + WillReturnRows(sqlmock.NewRows([]string{"id", "policy_id", "source_name"})) + file, err := RemoveFilesWithSoftLinks(files) + asserts.NoError(mock.ExpectationsWereMet()) + asserts.NoError(err) + asserts.Equal(files, file) + } + // 查询出错 + { + mock.ExpectQuery("SELECT(.+)files(.+)"). + WithArgs("1.txt", 23, 1, "2.txt", 24, 2). + WillReturnError(errors.New("error")) + file, err := RemoveFilesWithSoftLinks(files) + asserts.NoError(mock.ExpectationsWereMet()) + asserts.Error(err) + asserts.Nil(file) + } + // 第二个是软链 + { + mock.ExpectQuery("SELECT(.+)files(.+)"). + WithArgs("1.txt", 23, 1, "2.txt", 24, 2). + WillReturnRows( + sqlmock.NewRows([]string{"id", "policy_id", "source_name"}). + AddRow(3, 24, "2.txt"), + ) + file, err := RemoveFilesWithSoftLinks(files) + asserts.NoError(mock.ExpectationsWereMet()) + asserts.NoError(err) + asserts.Equal(files[:1], file) + } + // 第一个是软链 + { + mock.ExpectQuery("SELECT(.+)files(.+)"). + WithArgs("1.txt", 23, 1, "2.txt", 24, 2). + WillReturnRows( + sqlmock.NewRows([]string{"id", "policy_id", "source_name"}). + AddRow(3, 23, "1.txt"), + ) + file, err := RemoveFilesWithSoftLinks(files) + asserts.NoError(mock.ExpectationsWereMet()) + asserts.NoError(err) + asserts.Equal(files[1:], file) + } + // 全部是软链 + { + mock.ExpectQuery("SELECT(.+)files(.+)"). + WithArgs("1.txt", 23, 1, "2.txt", 24, 2). + WillReturnRows( + sqlmock.NewRows([]string{"id", "policy_id", "source_name"}). + AddRow(3, 24, "2.txt"). + AddRow(4, 23, "1.txt"), + ) + file, err := RemoveFilesWithSoftLinks(files) + asserts.NoError(mock.ExpectationsWereMet()) + asserts.NoError(err) + asserts.Len(file, 0) + } +} + +func TestDeleteFileByIDs(t *testing.T) { + asserts := assert.New(t) + + // 出错 + { + mock.ExpectBegin() + mock.ExpectExec("UPDATE(.+)delete(.+)"). + WillReturnError(errors.New("error")) + mock.ExpectRollback() + err := DeleteFileByIDs([]uint{1, 2, 3}) + asserts.NoError(mock.ExpectationsWereMet()) + asserts.Error(err) + } + // 成功 + { + mock.ExpectBegin() + mock.ExpectExec("UPDATE(.+)delete(.+)"). + WillReturnResult(sqlmock.NewResult(0, 3)) + mock.ExpectCommit() + err := DeleteFileByIDs([]uint{1, 2, 3}) + asserts.NoError(mock.ExpectationsWereMet()) + asserts.NoError(err) + } +} diff --git a/models/folder_test.go b/models/folder_test.go index 45b1e59..a9a8485 100644 --- a/models/folder_test.go +++ b/models/folder_test.go @@ -3,6 +3,7 @@ package model import ( "errors" "github.com/DATA-DOG/go-sqlmock" + "github.com/HFO4/cloudreve/pkg/util" "github.com/jinzhu/gorm" "github.com/stretchr/testify/assert" "testing" @@ -72,3 +73,59 @@ func TestFolder_GetChildFolder(t *testing.T) { asserts.Len(files, 2) asserts.NoError(mock.ExpectationsWereMet()) } + +func TestGetRecursiveChildFolder(t *testing.T) { + asserts := assert.New(t) + dirs := []string{"/目录1", "/目录2"} + + // 正常 + { + mock.ExpectQuery("SELECT(.+)folders(.+)"). + WithArgs(1, util.BuildRegexp(dirs, "^", "/", "|"), "/目录1", "/目录2"). + WillReturnRows( + sqlmock.NewRows([]string{"id", "name"}). + AddRow(1, "sub1"). + AddRow(2, "sub2"). + AddRow(3, "sub3"), + ) + subs, err := GetRecursiveChildFolder(dirs, 1) + asserts.NoError(mock.ExpectationsWereMet()) + asserts.NoError(err) + asserts.Len(subs, 3) + } + // 出错 + { + mock.ExpectQuery("SELECT(.+)folders(.+)"). + WithArgs(1, util.BuildRegexp(dirs, "^", "/", "|"), "/目录1", "/目录2"). + WillReturnError(errors.New("233")) + subs, err := GetRecursiveChildFolder(dirs, 1) + asserts.NoError(mock.ExpectationsWereMet()) + asserts.Error(err) + asserts.Len(subs, 0) + } +} + +func TestDeleteFolderByIDs(t *testing.T) { + asserts := assert.New(t) + + // 出错 + { + mock.ExpectBegin() + mock.ExpectExec("UPDATE(.+)delete(.+)"). + WillReturnError(errors.New("error")) + mock.ExpectRollback() + err := DeleteFolderByIDs([]uint{1, 2, 3}) + asserts.NoError(mock.ExpectationsWereMet()) + asserts.Error(err) + } + // 成功 + { + mock.ExpectBegin() + mock.ExpectExec("UPDATE(.+)delete(.+)"). + WillReturnResult(sqlmock.NewResult(0, 3)) + mock.ExpectCommit() + err := DeleteFolderByIDs([]uint{1, 2, 3}) + asserts.NoError(mock.ExpectationsWereMet()) + asserts.NoError(err) + } +} diff --git a/models/user.go b/models/user.go index d59c132..1ed1375 100644 --- a/models/user.go +++ b/models/user.go @@ -57,16 +57,26 @@ type UserOption struct { // DeductionStorage 减少用户已用容量 func (user *User) DeductionStorage(size uint64) bool { + if size == 0 { + return true + } if size <= user.Storage { user.Storage -= size DB.Model(user).UpdateColumn("storage", gorm.Expr("storage - ?", size)) return true } + // 如果要减少的容量超出以用容量,则设为零 + user.Storage = 0 + DB.Model(user).UpdateColumn("storage", 0) + return false } // IncreaseStorage 检查并增加用户已用容量 func (user *User) IncreaseStorage(size uint64) bool { + if size == 0 { + return true + } if size <= user.GetRemainingCapacity() { user.Storage += size DB.Model(user).UpdateColumn("storage", gorm.Expr("storage + ?", size)) diff --git a/pkg/filesystem/errors.go b/pkg/filesystem/errors.go index dac770b..2cc3527 100644 --- a/pkg/filesystem/errors.go +++ b/pkg/filesystem/errors.go @@ -18,4 +18,5 @@ var ( ErrObjectNotExist = serializer.NewError(404, "文件不存在", nil) ErrIO = serializer.NewError(serializer.CodeIOFailed, "无法读取文件数据", nil) ErrDBListObjects = serializer.NewError(serializer.CodeDBError, "无法列取对象记录", nil) + ErrDBDeleteObjects = serializer.NewError(serializer.CodeDBError, "无法删除对象记录", nil) ) diff --git a/pkg/filesystem/file.go b/pkg/filesystem/file.go index ec850f1..b34a136 100644 --- a/pkg/filesystem/file.go +++ b/pkg/filesystem/file.go @@ -138,13 +138,13 @@ func (fs *FileSystem) GroupFileByPolicy(ctx context.Context, files []model.File) var policyGroup = make(map[uint][]*model.File) for key := range files { - if file, ok := policyGroup[files[key].GetPolicy().ID]; ok { + if file, ok := policyGroup[files[key].PolicyID]; ok { // 如果已存在分组,直接追加 - policyGroup[files[key].GetPolicy().ID] = append(file, &files[key]) + policyGroup[files[key].PolicyID] = append(file, &files[key]) } else { // 分布不存在,创建 - policyGroup[files[key].GetPolicy().ID] = make([]*model.File, 0) - policyGroup[files[key].GetPolicy().ID] = append(policyGroup[files[key].GetPolicy().ID], &files[key]) + policyGroup[files[key].PolicyID] = make([]*model.File, 0) + policyGroup[files[key].PolicyID] = append(policyGroup[files[key].PolicyID], &files[key]) } } diff --git a/pkg/filesystem/file_test.go b/pkg/filesystem/file_test.go index e47bfc4..2c889ea 100644 --- a/pkg/filesystem/file_test.go +++ b/pkg/filesystem/file_test.go @@ -139,3 +139,112 @@ func TestFileSystem_GetDownloadContent(t *testing.T) { asserts.NoError(err) asserts.NoError(mock.ExpectationsWereMet()) } + +func TestFileSystem_GroupFileByPolicy(t *testing.T) { + asserts := assert.New(t) + ctx := context.Background() + files := []model.File{ + model.File{ + PolicyID: 1, + Name: "1_1.txt", + }, + model.File{ + PolicyID: 2, + Name: "2_1.txt", + }, + model.File{ + PolicyID: 3, + Name: "3_1.txt", + }, + model.File{ + PolicyID: 2, + Name: "2_2.txt", + }, + model.File{ + PolicyID: 1, + Name: "1_2.txt", + }, + } + fs := FileSystem{} + policyGroup := fs.GroupFileByPolicy(ctx, files) + asserts.Equal(map[uint][]*model.File{ + 1: {&files[0], &files[4]}, + 2: {&files[1], &files[3]}, + 3: {&files[2]}, + }, policyGroup) +} + +func TestFileSystem_deleteGroupedFile(t *testing.T) { + asserts := assert.New(t) + ctx := context.Background() + fs := FileSystem{} + files := []model.File{ + { + PolicyID: 1, + Name: "1_1.txt", + SourceName: "1_1.txt", + Policy: model.Policy{Model: gorm.Model{ID: 1}, Type: "local"}, + }, + { + PolicyID: 2, + Name: "2_1.txt", + SourceName: "2_1.txt", + Policy: model.Policy{Model: gorm.Model{ID: 1}, Type: "local"}, + }, + { + PolicyID: 3, + Name: "3_1.txt", + SourceName: "3_1.txt", + Policy: model.Policy{Model: gorm.Model{ID: 1}, Type: "local"}, + }, + { + PolicyID: 2, + Name: "2_2.txt", + SourceName: "2_2.txt", + Policy: model.Policy{Model: gorm.Model{ID: 1}, Type: "local"}, + }, + { + PolicyID: 1, + Name: "1_2.txt", + SourceName: "1_2.txt", + Policy: model.Policy{Model: gorm.Model{ID: 1}, Type: "local"}, + }, + } + + // 全部失败 + { + failed := fs.deleteGroupedFile(ctx, fs.GroupFileByPolicy(ctx, files)) + asserts.Equal(map[uint][]string{ + 1: {"1_1.txt", "1_2.txt"}, + 2: {"2_1.txt", "2_2.txt"}, + 3: {"3_1.txt"}, + }, failed) + } + // 部分失败 + { + file, err := os.Create("1_1.txt") + asserts.NoError(err) + _ = file.Close() + failed := fs.deleteGroupedFile(ctx, fs.GroupFileByPolicy(ctx, files)) + asserts.Equal(map[uint][]string{ + 1: {"1_2.txt"}, + 2: {"2_1.txt", "2_2.txt"}, + 3: {"3_1.txt"}, + }, failed) + } + // 部分失败,包含整组未知存储策略导致的失败 + { + file, err := os.Create("1_1.txt") + asserts.NoError(err) + _ = file.Close() + + files[1].Policy.Type = "unknown" + files[3].Policy.Type = "unknown" + failed := fs.deleteGroupedFile(ctx, fs.GroupFileByPolicy(ctx, files)) + asserts.Equal(map[uint][]string{ + 1: {"1_2.txt"}, + 2: {"2_1.txt", "2_2.txt"}, + 3: {"3_1.txt"}, + }, failed) + } +} diff --git a/pkg/filesystem/path.go b/pkg/filesystem/path.go index 0e10f8e..04f8c52 100644 --- a/pkg/filesystem/path.go +++ b/pkg/filesystem/path.go @@ -67,8 +67,8 @@ func (fs *FileSystem) Delete(ctx context.Context, dirs, files []string) error { // TODO 删除失败时不删除文件记录及父目录 } else { deletedFileIDs = append(deletedFileIDs, fs.FileTarget[i].ID) - deletedStorage[fs.FileTarget[i].ID] = fs.FileTarget[i].Size } + deletedStorage[fs.FileTarget[i].ID] = fs.FileTarget[i].Size allFileIDs = append(allFileIDs, fs.FileTarget[i].ID) } } @@ -76,7 +76,7 @@ func (fs *FileSystem) Delete(ctx context.Context, dirs, files []string) error { // 删除文件记录 err := model.DeleteFileByIDs(allFileIDs) if err != nil { - return ErrDBListObjects.WithError(err) + return ErrDBDeleteObjects.WithError(err) } // 归还容量 @@ -84,7 +84,7 @@ func (fs *FileSystem) Delete(ctx context.Context, dirs, files []string) error { for _, value := range deletedStorage { total += value } - fs.User.IncreaseStorage(total) + fs.User.DeductionStorage(total) // 删除目录 var allFolderIDs = make([]uint, 0, len(fs.DirTarget)) @@ -93,7 +93,7 @@ func (fs *FileSystem) Delete(ctx context.Context, dirs, files []string) error { } err = model.DeleteFolderByIDs(allFolderIDs) if err != nil { - return ErrDBListObjects.WithError(err) + return ErrDBDeleteObjects.WithError(err) } if notDeleted := len(fs.FileTarget) - len(deletedFileIDs); notDeleted > 0 { diff --git a/pkg/filesystem/path_test.go b/pkg/filesystem/path_test.go index 44cd1ee..b7b1bcd 100644 --- a/pkg/filesystem/path_test.go +++ b/pkg/filesystem/path_test.go @@ -2,10 +2,13 @@ package filesystem import ( "context" + "errors" "github.com/DATA-DOG/go-sqlmock" model "github.com/HFO4/cloudreve/models" + "github.com/HFO4/cloudreve/pkg/serializer" "github.com/jinzhu/gorm" "github.com/stretchr/testify/assert" + "os" "testing" ) @@ -170,3 +173,192 @@ func TestFileSystem_CreateDirectory(t *testing.T) { asserts.NoError(err) asserts.NoError(mock.ExpectationsWereMet()) } + +func TestFileSystem_ListDeleteFiles(t *testing.T) { + asserts := assert.New(t) + fs := &FileSystem{User: &model.User{ + Model: gorm.Model{ + ID: 1, + }, + }} + + // 成功 + { + mock.ExpectQuery("SELECT(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(1, "1.txt").AddRow(2, "2.txt")) + err := fs.ListDeleteFiles(context.Background(), []string{"/"}) + asserts.NoError(err) + asserts.NoError(mock.ExpectationsWereMet()) + } + + // 失败 + { + mock.ExpectQuery("SELECT(.+)").WillReturnError(errors.New("error")) + err := fs.ListDeleteFiles(context.Background(), []string{"/"}) + asserts.Error(err) + asserts.Equal(serializer.CodeDBError, err.(serializer.AppError).Code) + asserts.NoError(mock.ExpectationsWereMet()) + } +} + +func TestFileSystem_ListDeleteDirs(t *testing.T) { + asserts := assert.New(t) + fs := &FileSystem{User: &model.User{ + Model: gorm.Model{ + ID: 1, + }, + }} + + // 成功 + { + mock.ExpectQuery("SELECT(.+)"). + WillReturnRows( + sqlmock.NewRows([]string{"id"}). + AddRow(1). + AddRow(2). + AddRow(3), + ) + mock.ExpectQuery("SELECT(.+)"). + WithArgs(1, 2, 3). + WillReturnRows( + sqlmock.NewRows([]string{"id", "name"}). + AddRow(4, "1.txt"). + AddRow(5, "2.txt"). + AddRow(6, "3.txt"), + ) + err := fs.ListDeleteDirs(context.Background(), []string{"/"}) + asserts.NoError(err) + asserts.Len(fs.FileTarget, 3) + asserts.Len(fs.DirTarget, 3) + asserts.NoError(mock.ExpectationsWereMet()) + } + + // 检索文件发生错误 + { + mock.ExpectQuery("SELECT(.+)"). + WillReturnRows( + sqlmock.NewRows([]string{"id"}). + AddRow(1). + AddRow(2). + AddRow(3), + ) + mock.ExpectQuery("SELECT(.+)"). + WithArgs(1, 2, 3). + WillReturnError(errors.New("error")) + err := fs.ListDeleteDirs(context.Background(), []string{"/"}) + asserts.Error(err) + asserts.Len(fs.DirTarget, 6) + asserts.NoError(mock.ExpectationsWereMet()) + } + // 检索目录发生错误 + { + mock.ExpectQuery("SELECT(.+)"). + WillReturnError(errors.New("error")) + err := fs.ListDeleteDirs(context.Background(), []string{"/"}) + asserts.Error(err) + asserts.NoError(mock.ExpectationsWereMet()) + } +} + +func TestFileSystem_Delete(t *testing.T) { + asserts := assert.New(t) + fs := &FileSystem{User: &model.User{ + Model: gorm.Model{ + ID: 1, + }, + Storage: 3, + Group: model.Group{MaxStorage: 3}, + }} + ctx := context.Background() + + // 全部未成功 + { + mock.ExpectQuery("SELECT(.+)"). + WillReturnRows( + sqlmock.NewRows([]string{"id"}). + AddRow(1). + AddRow(2). + AddRow(3), + ) + mock.ExpectQuery("SELECT(.+)"). + WithArgs(1, 2, 3). + WillReturnRows( + sqlmock.NewRows([]string{"id", "name", "source_name", "policy_id", "size"}). + AddRow(4, "1.txt", "1.txt", 2, 1), + ) + mock.ExpectQuery("SELECT(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "name", "source_name", "policy_id", "size"}).AddRow(1, "1.txt", "1.txt", 1, 2)) + mock.ExpectQuery("SELECT(.+)files(.+)"). + WillReturnRows(sqlmock.NewRows([]string{"id", "policy_id", "source_name"})) + // 查询上传策略 + mock.ExpectQuery("SELECT(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "type"}).AddRow(1, "local")) + mock.ExpectQuery("SELECT(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "type"}).AddRow(1, "local")) + // 删除文件记录 + mock.ExpectBegin() + mock.ExpectExec("UPDATE(.+)delete(.+)"). + WillReturnResult(sqlmock.NewResult(0, 3)) + mock.ExpectCommit() + // 归还容量 + mock.ExpectBegin() + mock.ExpectExec("UPDATE(.+)"). + WillReturnResult(sqlmock.NewResult(0, 3)) + mock.ExpectCommit() + // 删除目录 + mock.ExpectBegin() + mock.ExpectExec("UPDATE(.+)delete(.+)"). + WillReturnResult(sqlmock.NewResult(0, 3)) + mock.ExpectCommit() + + err := fs.Delete(ctx, []string{"/"}, []string{"2.txt"}) + asserts.Error(err) + asserts.Equal(203, err.(serializer.AppError).Code) + asserts.Equal(uint64(0), fs.User.Storage) + } + // 全部成功 + { + file, err := os.Create("1.txt") + file2, err := os.Create("2.txt") + file.Close() + file2.Close() + asserts.NoError(err) + mock.ExpectQuery("SELECT(.+)"). + WillReturnRows( + sqlmock.NewRows([]string{"id"}). + AddRow(1). + AddRow(2). + AddRow(3), + ) + mock.ExpectQuery("SELECT(.+)"). + WithArgs(1, 2, 3). + WillReturnRows( + sqlmock.NewRows([]string{"id", "name", "source_name", "policy_id", "size"}). + AddRow(4, "1.txt", "1.txt", 2, 1), + ) + mock.ExpectQuery("SELECT(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "name", "source_name", "policy_id", "size"}).AddRow(1, "2.txt", "2.txt", 1, 2)) + mock.ExpectQuery("SELECT(.+)files(.+)"). + WillReturnRows(sqlmock.NewRows([]string{"id", "policy_id", "source_name"})) + // 查询上传策略 + mock.ExpectQuery("SELECT(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "type"}).AddRow(1, "local")) + mock.ExpectQuery("SELECT(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "type"}).AddRow(1, "local")) + // 删除文件记录 + mock.ExpectBegin() + mock.ExpectExec("UPDATE(.+)delete(.+)"). + WillReturnResult(sqlmock.NewResult(0, 3)) + mock.ExpectCommit() + // 归还容量 + mock.ExpectBegin() + mock.ExpectExec("UPDATE(.+)"). + WillReturnResult(sqlmock.NewResult(0, 3)) + mock.ExpectCommit() + // 删除目录 + mock.ExpectBegin() + mock.ExpectExec("UPDATE(.+)delete(.+)"). + WillReturnResult(sqlmock.NewResult(0, 3)) + mock.ExpectCommit() + + fs.FileTarget = []model.File{} + fs.DirTarget = []model.Folder{} + err = fs.Delete(ctx, []string{"/"}, []string{"2.txt"}) + asserts.NoError(err) + asserts.Equal(uint64(0), fs.User.Storage) + } + +} diff --git a/pkg/util/common_test.go b/pkg/util/common_test.go index b279fd4..6101872 100644 --- a/pkg/util/common_test.go +++ b/pkg/util/common_test.go @@ -25,3 +25,40 @@ func TestRandStringRunes(t *testing.T) { sameLenStr2 := RandStringRunes(32) asserts.NotEqual(sameLenStr1, sameLenStr2) } + +func TestContainsUint(t *testing.T) { + asserts := assert.New(t) + asserts.True(ContainsUint([]uint{0, 2, 3, 65, 4}, 65)) + asserts.True(ContainsUint([]uint{65}, 65)) + asserts.False(ContainsUint([]uint{65}, 6)) +} + +func TestContainsString(t *testing.T) { + asserts := assert.New(t) + asserts.True(ContainsString([]string{"", "1"}, "")) + asserts.True(ContainsString([]string{"", "1"}, "1")) + asserts.False(ContainsString([]string{"", "1"}, " ")) +} + +func TestReplace(t *testing.T) { + asserts := assert.New(t) + + asserts.Equal("origin", Replace(map[string]string{ + "123": "321", + }, "origin")) + + asserts.Equal("321origin321", Replace(map[string]string{ + "123": "321", + }, "123origin123")) + asserts.Equal("321new321", Replace(map[string]string{ + "123": "321", + "origin": "new", + }, "123origin123")) +} + +func TestBuildRegexp(t *testing.T) { + asserts := assert.New(t) + + asserts.Equal("^/dir/", BuildRegexp([]string{"/dir"}, "^", "/", "|")) + asserts.Equal("^/dir/|^/dir/di\\*r/", BuildRegexp([]string{"/dir", "/dir/di*r"}, "^", "/", "|")) +}