mirror of
				https://github.com/cloudreve/cloudreve.git
				synced 2025-11-01 00:57:15 +08:00 
			
		
		
		
	Test: delete objects related methods
This commit is contained in:
		| @ -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]) | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|  | ||||
| @ -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) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -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) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -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)) | ||||
|  | ||||
| @ -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) | ||||
| ) | ||||
|  | ||||
| @ -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]) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|  | ||||
| @ -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) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -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 { | ||||
|  | ||||
| @ -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) | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| @ -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"}, "^", "/", "|")) | ||||
| } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 HFO4
					HFO4