mirror of
				https://github.com/cloudreve/cloudreve.git
				synced 2025-10-31 16:49:03 +08:00 
			
		
		
		
	Test: delete objects related methods
This commit is contained in:
		| @ -61,7 +61,6 @@ func GetChildFilesOfFolders(folders *[]Folder) ([]File, error) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // GetPolicy 获取文件所属策略 | // GetPolicy 获取文件所属策略 | ||||||
| // TODO:test |  | ||||||
| func (file *File) GetPolicy() *Policy { | func (file *File) GetPolicy() *Policy { | ||||||
| 	if file.Policy.Model.ID == 0 { | 	if file.Policy.Model.ID == 0 { | ||||||
| 		file.Policy, _ = GetPolicyByID(file.PolicyID) | 		file.Policy, _ = GetPolicyByID(file.PolicyID) | ||||||
| @ -91,7 +90,7 @@ func RemoveFilesWithSoftLinks(files []File) ([]File, error) { | |||||||
| 	var filesWithSoftLinks []File | 	var filesWithSoftLinks []File | ||||||
| 	tx := DB | 	tx := DB | ||||||
| 	for _, value := range files { | 	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) | 	result := tx.Find(&filesWithSoftLinks) | ||||||
| 	if result.Error != nil { | 	if result.Error != nil { | ||||||
| @ -99,16 +98,22 @@ func RemoveFilesWithSoftLinks(files []File) ([]File, error) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// 过滤具有软连接的文件 | 	// 过滤具有软连接的文件 | ||||||
|  | 	// TODO: 优化复杂度 | ||||||
| 	if len(filesWithSoftLinks) == 0 { | 	if len(filesWithSoftLinks) == 0 { | ||||||
| 		filteredFiles = files | 		filteredFiles = files | ||||||
| 	} else { | 	} else { | ||||||
| 		for i := 0; i < len(files); i++ { | 		for i := 0; i < len(files); i++ { | ||||||
|  | 			finder := false | ||||||
| 			for _, value := range filesWithSoftLinks { | 			for _, value := range filesWithSoftLinks { | ||||||
| 				if value.PolicyID != files[i].PolicyID || value.SourceName != files[i].SourceName { | 				if value.PolicyID == files[i].PolicyID && value.SourceName == files[i].SourceName { | ||||||
| 					filteredFiles = append(filteredFiles, files[i]) | 					finder = true | ||||||
| 					break | 					break | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
|  | 			if !finder { | ||||||
|  | 				filteredFiles = append(filteredFiles, files[i]) | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | |||||||
| @ -66,3 +66,227 @@ func TestFolder_GetChildFile(t *testing.T) { | |||||||
| 	asserts.NoError(mock.ExpectationsWereMet()) | 	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 ( | import ( | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"github.com/DATA-DOG/go-sqlmock" | 	"github.com/DATA-DOG/go-sqlmock" | ||||||
|  | 	"github.com/HFO4/cloudreve/pkg/util" | ||||||
| 	"github.com/jinzhu/gorm" | 	"github.com/jinzhu/gorm" | ||||||
| 	"github.com/stretchr/testify/assert" | 	"github.com/stretchr/testify/assert" | ||||||
| 	"testing" | 	"testing" | ||||||
| @ -72,3 +73,59 @@ func TestFolder_GetChildFolder(t *testing.T) { | |||||||
| 	asserts.Len(files, 2) | 	asserts.Len(files, 2) | ||||||
| 	asserts.NoError(mock.ExpectationsWereMet()) | 	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 减少用户已用容量 | // DeductionStorage 减少用户已用容量 | ||||||
| func (user *User) DeductionStorage(size uint64) bool { | func (user *User) DeductionStorage(size uint64) bool { | ||||||
|  | 	if size == 0 { | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
| 	if size <= user.Storage { | 	if size <= user.Storage { | ||||||
| 		user.Storage -= size | 		user.Storage -= size | ||||||
| 		DB.Model(user).UpdateColumn("storage", gorm.Expr("storage - ?", size)) | 		DB.Model(user).UpdateColumn("storage", gorm.Expr("storage - ?", size)) | ||||||
| 		return true | 		return true | ||||||
| 	} | 	} | ||||||
|  | 	// 如果要减少的容量超出以用容量,则设为零 | ||||||
|  | 	user.Storage = 0 | ||||||
|  | 	DB.Model(user).UpdateColumn("storage", 0) | ||||||
|  |  | ||||||
| 	return false | 	return false | ||||||
| } | } | ||||||
|  |  | ||||||
| // IncreaseStorage 检查并增加用户已用容量 | // IncreaseStorage 检查并增加用户已用容量 | ||||||
| func (user *User) IncreaseStorage(size uint64) bool { | func (user *User) IncreaseStorage(size uint64) bool { | ||||||
|  | 	if size == 0 { | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
| 	if size <= user.GetRemainingCapacity() { | 	if size <= user.GetRemainingCapacity() { | ||||||
| 		user.Storage += size | 		user.Storage += size | ||||||
| 		DB.Model(user).UpdateColumn("storage", gorm.Expr("storage + ?", size)) | 		DB.Model(user).UpdateColumn("storage", gorm.Expr("storage + ?", size)) | ||||||
|  | |||||||
| @ -18,4 +18,5 @@ var ( | |||||||
| 	ErrObjectNotExist          = serializer.NewError(404, "文件不存在", nil) | 	ErrObjectNotExist          = serializer.NewError(404, "文件不存在", nil) | ||||||
| 	ErrIO                      = serializer.NewError(serializer.CodeIOFailed, "无法读取文件数据", nil) | 	ErrIO                      = serializer.NewError(serializer.CodeIOFailed, "无法读取文件数据", nil) | ||||||
| 	ErrDBListObjects           = serializer.NewError(serializer.CodeDBError, "无法列取对象记录", 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) | 	var policyGroup = make(map[uint][]*model.File) | ||||||
|  |  | ||||||
| 	for key := range files { | 	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 { | 		} else { | ||||||
| 			// 分布不存在,创建 | 			// 分布不存在,创建 | ||||||
| 			policyGroup[files[key].GetPolicy().ID] = make([]*model.File, 0) | 			policyGroup[files[key].PolicyID] = make([]*model.File, 0) | ||||||
| 			policyGroup[files[key].GetPolicy().ID] = append(policyGroup[files[key].GetPolicy().ID], &files[key]) | 			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(err) | ||||||
| 	asserts.NoError(mock.ExpectationsWereMet()) | 	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 删除失败时不删除文件记录及父目录 | 				// TODO 删除失败时不删除文件记录及父目录 | ||||||
| 			} else { | 			} else { | ||||||
| 				deletedFileIDs = append(deletedFileIDs, fs.FileTarget[i].ID) | 				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) | 			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) | 	err := model.DeleteFileByIDs(allFileIDs) | ||||||
| 	if err != nil { | 	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 { | 	for _, value := range deletedStorage { | ||||||
| 		total += value | 		total += value | ||||||
| 	} | 	} | ||||||
| 	fs.User.IncreaseStorage(total) | 	fs.User.DeductionStorage(total) | ||||||
|  |  | ||||||
| 	// 删除目录 | 	// 删除目录 | ||||||
| 	var allFolderIDs = make([]uint, 0, len(fs.DirTarget)) | 	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) | 	err = model.DeleteFolderByIDs(allFolderIDs) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return ErrDBListObjects.WithError(err) | 		return ErrDBDeleteObjects.WithError(err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if notDeleted := len(fs.FileTarget) - len(deletedFileIDs); notDeleted > 0 { | 	if notDeleted := len(fs.FileTarget) - len(deletedFileIDs); notDeleted > 0 { | ||||||
|  | |||||||
| @ -2,10 +2,13 @@ package filesystem | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
|  | 	"errors" | ||||||
| 	"github.com/DATA-DOG/go-sqlmock" | 	"github.com/DATA-DOG/go-sqlmock" | ||||||
| 	model "github.com/HFO4/cloudreve/models" | 	model "github.com/HFO4/cloudreve/models" | ||||||
|  | 	"github.com/HFO4/cloudreve/pkg/serializer" | ||||||
| 	"github.com/jinzhu/gorm" | 	"github.com/jinzhu/gorm" | ||||||
| 	"github.com/stretchr/testify/assert" | 	"github.com/stretchr/testify/assert" | ||||||
|  | 	"os" | ||||||
| 	"testing" | 	"testing" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @ -170,3 +173,192 @@ func TestFileSystem_CreateDirectory(t *testing.T) { | |||||||
| 	asserts.NoError(err) | 	asserts.NoError(err) | ||||||
| 	asserts.NoError(mock.ExpectationsWereMet()) | 	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) | 	sameLenStr2 := RandStringRunes(32) | ||||||
| 	asserts.NotEqual(sameLenStr1, sameLenStr2) | 	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