Test: get source URL of files

This commit is contained in:
HFO4
2019-12-11 12:24:09 +08:00
parent 9f26c0c8ab
commit d0bb123e03
18 changed files with 363 additions and 28 deletions

View File

@@ -23,7 +23,6 @@ type Auth interface {
}
// SignURI 对URI进行签名,签名只针对Path部分query部分不做验证
// TODO 测试
func SignURI(uri string, expires int64) (*url.URL, error) {
base, err := url.Parse(uri)
if err != nil {

7
pkg/cache/driver.go vendored
View File

@@ -33,6 +33,8 @@ type Driver interface {
Gets(keys []string, prefix string) (map[string]interface{}, []string)
// 批量设置值
Sets(values map[string]interface{}, prefix string) error
// 删除值
Delete(keys []string, prefix string) error
}
// Set 设置缓存值
@@ -45,6 +47,11 @@ func Get(key string) (interface{}, bool) {
return Store.Get(key)
}
// Deletes 删除值
func Deletes(keys []string, prefix string) error {
return Store.Delete(keys, prefix)
}
// GetSettings 根据名称批量获取设置项缓存
func GetSettings(keys []string, prefix string) (map[string]string, []string) {
raw, miss := Store.Gets(keys, prefix)

8
pkg/cache/memo.go vendored
View File

@@ -48,3 +48,11 @@ func (store *MemoStore) Sets(values map[string]interface{}, prefix string) error
}
return nil
}
// Delete 批量删除值
func (store *MemoStore) Delete(keys []string, prefix string) error {
for _, key := range keys {
store.Store.Delete(prefix + key)
}
return nil
}

View File

@@ -106,3 +106,22 @@ func TestMemoStore_Sets(t *testing.T) {
"4": "4.val",
}, vals)
}
func TestMemoStore_Delete(t *testing.T) {
asserts := assert.New(t)
store := NewMemoStore()
err := store.Sets(map[string]interface{}{
"1": "1.val",
"2": "2.val",
"3": "3.val",
"4": "4.val",
}, "test_")
asserts.NoError(err)
err = store.Delete([]string{"1", "2"}, "test_")
asserts.NoError(err)
values, miss := store.Gets([]string{"1", "2", "3", "4"}, "test_")
asserts.Equal([]string{"1", "2"}, miss)
asserts.Equal(map[string]interface{}{"3": "3.val", "4": "4.val"}, values)
}

31
pkg/cache/redis.go vendored
View File

@@ -169,13 +169,30 @@ func (store *RedisStore) Sets(values map[string]interface{}, prefix string) erro
setValues[prefix+key] = serialized
}
if rc.Err() == nil {
_, err := rc.Do("MSET", redis.Args{}.AddFlat(setValues)...)
if err != nil {
return err
}
return nil
_, err := rc.Do("MSET", redis.Args{}.AddFlat(setValues)...)
if err != nil {
return err
}
return nil
}
// Delete 批量删除给定的键
func (store *RedisStore) Delete(keys []string, prefix string) error {
rc := store.pool.Get()
defer rc.Close()
if rc.Err() != nil {
return rc.Err()
}
return rc.Err()
// 处理前缀
for i := 0; i < len(keys); i++ {
keys[i] = prefix + keys[i]
}
_, err := rc.Do("DEL", redis.Args{}.AddFlat(keys)...)
if err != nil {
return err
}
return nil
}

View File

@@ -266,3 +266,47 @@ func TestRedisStore_Sets(t *testing.T) {
asserts.Error(err)
}
}
func TestRedisStore_Delete(t *testing.T) {
asserts := assert.New(t)
conn := redigomock.NewConn()
pool := &redis.Pool{
Dial: func() (redis.Conn, error) { return conn, nil },
MaxIdle: 10,
}
store := &RedisStore{pool: pool}
// 正常
{
cmd := conn.Command("DEL", redigomock.NewAnyData(), redigomock.NewAnyData(), redigomock.NewAnyData(), redigomock.NewAnyData()).ExpectSlice("OK")
err := store.Delete([]string{"1", "2", "3", "4"}, "test_")
asserts.NoError(err)
if conn.Stats(cmd) != 1 {
fmt.Println("Command was not used")
return
}
}
// 命令执行失败
{
conn.Clear()
cmd := conn.Command("DEL", redigomock.NewAnyData(), redigomock.NewAnyData(), redigomock.NewAnyData(), redigomock.NewAnyData()).ExpectError(errors.New("error"))
err := store.Delete([]string{"1", "2", "3", "4"}, "test_")
asserts.Error(err)
if conn.Stats(cmd) != 1 {
fmt.Println("Command was not used")
return
}
}
// 连接失败
{
conn.Clear()
store.pool = &redis.Pool{
Dial: func() (redis.Conn, error) { return nil, errors.New("error") },
MaxIdle: 10,
}
err := store.Delete([]string{"1", "2", "3", "4"}, "test_")
asserts.Error(err)
}
}

View File

@@ -72,7 +72,6 @@ func (fs *FileSystem) GetDownloadContent(ctx context.Context, path string) (io.R
}
// GetContent 获取文件内容path为虚拟路径
// TODO:测试
func (fs *FileSystem) GetContent(ctx context.Context, path string) (io.ReadSeeker, error) {
// 触发`下载前`钩子
err := fs.Trigger(ctx, fs.BeforeFileDownload)
@@ -94,6 +93,7 @@ func (fs *FileSystem) GetContent(ctx context.Context, path string) (io.ReadSeeke
// 将当前存储策略重设为文件使用的
fs.Policy = fs.FileTarget[0].GetPolicy()
err = fs.dispatchHandler()
defer fs.CleanTargets()
if err != nil {
return nil, err
}
@@ -176,7 +176,11 @@ func (fs *FileSystem) GetSource(ctx context.Context, fileID uint) (string, error
// 检查存储策略是否可以获得外链
if !fs.Policy.IsOriginLinkEnable {
return "", serializer.NewError(serializer.CodePolicyNotAllowed, "当前存储策略无法获得外链", nil)
return "", serializer.NewError(
serializer.CodePolicyNotAllowed,
"当前存储策略无法获得外链",
nil,
)
}
// 生成外链地址

View File

@@ -4,6 +4,8 @@ import (
"context"
"github.com/DATA-DOG/go-sqlmock"
model "github.com/HFO4/cloudreve/models"
"github.com/HFO4/cloudreve/pkg/auth"
"github.com/HFO4/cloudreve/pkg/cache"
"github.com/HFO4/cloudreve/pkg/filesystem/fsctx"
"github.com/HFO4/cloudreve/pkg/filesystem/local"
"github.com/HFO4/cloudreve/pkg/serializer"
@@ -263,3 +265,109 @@ func TestFileSystem_deleteGroupedFile(t *testing.T) {
}, failed)
}
}
func TestFileSystem_GetSource(t *testing.T) {
asserts := assert.New(t)
ctx := context.Background()
fs := FileSystem{
User: &model.User{Model: gorm.Model{ID: 1}},
}
auth.General = auth.HMACAuth{SecretKey: []byte("123")}
// 正常
{
// 清空缓存
err := cache.Deletes([]string{"siteURL"}, "setting_")
asserts.NoError(err)
// 查找文件
mock.ExpectQuery("SELECT(.+)").
WithArgs(2, 1).
WillReturnRows(
sqlmock.NewRows([]string{"id", "policy_id", "source_name"}).
AddRow(2, 35, "1.txt"),
)
// 查找上传策略
mock.ExpectQuery("SELECT(.+)").
WillReturnRows(
sqlmock.NewRows([]string{"id", "type", "is_origin_link_enable"}).
AddRow(35, "local", true),
)
// 查找站点URL
mock.ExpectQuery("SELECT(.+)").WithArgs("siteURL").WillReturnRows(sqlmock.NewRows([]string{"id", "value"}).AddRow(1, "https://cloudreve.org"))
sourceURL, err := fs.GetSource(ctx, 2)
asserts.NoError(mock.ExpectationsWereMet())
asserts.NoError(err)
asserts.NotEmpty(sourceURL)
}
// 文件不存在
{
// 清空缓存
err := cache.Deletes([]string{"siteURL"}, "setting_")
asserts.NoError(err)
// 查找文件
mock.ExpectQuery("SELECT(.+)").
WithArgs(2, 1).
WillReturnRows(
sqlmock.NewRows([]string{"id", "policy_id", "source_name"}),
)
sourceURL, err := fs.GetSource(ctx, 2)
asserts.NoError(mock.ExpectationsWereMet())
asserts.Error(err)
asserts.Equal(ErrObjectNotExist.Code, err.(serializer.AppError).Code)
asserts.Empty(sourceURL)
}
// 未知上传策略
{
// 清空缓存
err := cache.Deletes([]string{"siteURL"}, "setting_")
asserts.NoError(err)
// 查找文件
mock.ExpectQuery("SELECT(.+)").
WithArgs(2, 1).
WillReturnRows(
sqlmock.NewRows([]string{"id", "policy_id", "source_name"}).
AddRow(2, 35, "1.txt"),
)
// 查找上传策略
mock.ExpectQuery("SELECT(.+)").
WillReturnRows(
sqlmock.NewRows([]string{"id", "type", "is_origin_link_enable"}).
AddRow(35, "?", true),
)
sourceURL, err := fs.GetSource(ctx, 2)
asserts.NoError(mock.ExpectationsWereMet())
asserts.Error(err)
asserts.Empty(sourceURL)
}
// 不允许获取外链
{
// 清空缓存
err := cache.Deletes([]string{"siteURL"}, "setting_")
asserts.NoError(err)
// 查找文件
mock.ExpectQuery("SELECT(.+)").
WithArgs(2, 1).
WillReturnRows(
sqlmock.NewRows([]string{"id", "policy_id", "source_name"}).
AddRow(2, 35, "1.txt"),
)
// 查找上传策略
mock.ExpectQuery("SELECT(.+)").
WillReturnRows(
sqlmock.NewRows([]string{"id", "type", "is_origin_link_enable"}).
AddRow(35, "local", false),
)
sourceURL, err := fs.GetSource(ctx, 2)
asserts.NoError(mock.ExpectationsWereMet())
asserts.Error(err)
asserts.Equal(serializer.CodePolicyNotAllowed, err.(serializer.AppError).Code)
asserts.Empty(sourceURL)
}
}

View File

@@ -80,7 +80,6 @@ func NewFileSystem(user *model.User) (*FileSystem, error) {
}
// NewAnonymousFileSystem 初始化匿名文件系统
// TODO 测试
func NewAnonymousFileSystem() (*FileSystem, error) {
fs := &FileSystem{
User: &model.User{},
@@ -160,3 +159,9 @@ func (fs *FileSystem) SetTargetFileByIDs(ids []uint) error {
fs.SetTargetFile(&files)
return nil
}
// CleanTargets 清空目标
func (fs *FileSystem) CleanTargets() {
fs.FileTarget = []model.File{}
fs.DirTarget = []model.Folder{}
}

View File

@@ -1,6 +1,7 @@
package filesystem
import (
"github.com/DATA-DOG/go-sqlmock"
model "github.com/HFO4/cloudreve/models"
"github.com/HFO4/cloudreve/pkg/filesystem/local"
"github.com/gin-gonic/gin"
@@ -63,3 +64,63 @@ func TestDispatchHandler(t *testing.T) {
err = fs.dispatchHandler()
asserts.Error(err)
}
func TestFileSystem_SetTargetFileByIDs(t *testing.T) {
asserts := assert.New(t)
// 成功
{
fs := &FileSystem{}
mock.ExpectQuery("SELECT(.+)").
WithArgs(1, 2).
WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(1, "1.txt"))
err := fs.SetTargetFileByIDs([]uint{1, 2})
asserts.NoError(mock.ExpectationsWereMet())
asserts.Len(fs.FileTarget, 1)
asserts.NoError(err)
}
// 未找到
{
fs := &FileSystem{}
mock.ExpectQuery("SELECT(.+)").WithArgs(1, 2).WillReturnRows(sqlmock.NewRows([]string{"id", "name"}))
err := fs.SetTargetFileByIDs([]uint{1, 2})
asserts.NoError(mock.ExpectationsWereMet())
asserts.Len(fs.FileTarget, 0)
asserts.Error(err)
}
}
func TestFileSystem_CleanTargets(t *testing.T) {
asserts := assert.New(t)
fs := &FileSystem{
FileTarget: []model.File{{}, {}},
DirTarget: []model.Folder{{}, {}},
}
fs.CleanTargets()
asserts.Len(fs.FileTarget, 0)
asserts.Len(fs.DirTarget, 0)
}
func TestNewAnonymousFileSystem(t *testing.T) {
asserts := assert.New(t)
// 正常
{
mock.ExpectQuery("SELECT(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "name", "policies"}).AddRow(3, "游客", "[]"))
fs, err := NewAnonymousFileSystem()
asserts.NoError(mock.ExpectationsWereMet())
asserts.NoError(err)
asserts.Equal("游客", fs.User.Group.Name)
}
// 游客用户组不存在
{
mock.ExpectQuery("SELECT(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "name", "policies"}))
fs, err := NewAnonymousFileSystem()
asserts.NoError(mock.ExpectationsWereMet())
asserts.Error(err)
asserts.Nil(fs)
}
}

View File

@@ -29,7 +29,6 @@ func (fs *FileSystem) GetThumb(ctx context.Context, id uint) (*response.ContentR
}
fs.FileTarget = []model.File{file[0]}
res, err := fs.Handler.Thumb(ctx, file[0].SourceName)
// TODO 出错时重新生成缩略图

View File

@@ -2,11 +2,16 @@ package local
import (
"context"
model "github.com/HFO4/cloudreve/models"
"github.com/HFO4/cloudreve/pkg/auth"
"github.com/HFO4/cloudreve/pkg/conf"
"github.com/HFO4/cloudreve/pkg/filesystem/fsctx"
"github.com/HFO4/cloudreve/pkg/util"
"github.com/jinzhu/gorm"
"github.com/stretchr/testify/assert"
"io"
"io/ioutil"
"net/url"
"os"
"strings"
"testing"
@@ -112,3 +117,37 @@ func TestHandler_Thumb(t *testing.T) {
asserts.Error(err)
}
}
func TestHandler_Source(t *testing.T) {
asserts := assert.New(t)
handler := Handler{}
ctx := context.Background()
auth.General = auth.HMACAuth{SecretKey: []byte("test")}
// 成功
{
file := model.File{
Model: gorm.Model{
ID: 1,
},
Name: "test.jpg",
}
ctx := context.WithValue(ctx, fsctx.FileModelCtx, file)
baseURL, err := url.Parse("https://cloudreve.org")
asserts.NoError(err)
sourceURL, err := handler.Source(ctx, "", *baseURL, 0)
asserts.NoError(err)
asserts.NotEmpty(sourceURL)
asserts.Contains(sourceURL, "sign=")
asserts.Contains(sourceURL, "https://cloudreve.org")
}
// 无法获取上下文
{
baseURL, err := url.Parse("https://cloudreve.org")
asserts.NoError(err)
sourceURL, err := handler.Source(ctx, "", *baseURL, 0)
asserts.Error(err)
asserts.Empty(sourceURL)
}
}