mirror of
https://github.com/datarhei/core.git
synced 2025-09-28 04:42:32 +08:00
577 lines
15 KiB
Go
577 lines
15 KiB
Go
package api
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/datarhei/core/v16/http/api"
|
|
"github.com/datarhei/core/v16/http/cache"
|
|
httpfs "github.com/datarhei/core/v16/http/fs"
|
|
"github.com/datarhei/core/v16/http/handler"
|
|
"github.com/datarhei/core/v16/http/mock"
|
|
"github.com/datarhei/core/v16/io/fs"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/labstack/echo/v4"
|
|
)
|
|
|
|
func getDummyFilesystemsHandler(filesystems []httpfs.FS) (*FSHandler, error) {
|
|
config := map[string]FSConfig{}
|
|
|
|
for _, fs := range filesystems {
|
|
config[fs.Name] = FSConfig{
|
|
Type: fs.Filesystem.Type(),
|
|
Mountpoint: fs.Mountpoint,
|
|
Handler: handler.NewFS(fs),
|
|
}
|
|
}
|
|
handler := NewFS(config)
|
|
|
|
return handler, nil
|
|
}
|
|
|
|
func getDummyFilesystemsRouter(filesystems []httpfs.FS) (*echo.Echo, error) {
|
|
router := mock.DummyEcho()
|
|
|
|
handler, err := getDummyFilesystemsHandler(filesystems)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
router.GET("/:storage/*", handler.GetFile)
|
|
router.PUT("/:storage/*", handler.PutFile)
|
|
router.DELETE("/:storage", handler.DeleteFiles)
|
|
router.DELETE("/:storage/*", handler.DeleteFile)
|
|
router.GET("/:storage", handler.ListFiles)
|
|
router.GET("/", handler.List)
|
|
router.PUT("/", handler.FileOperation)
|
|
|
|
return router, nil
|
|
}
|
|
|
|
func TestFilesystems(t *testing.T) {
|
|
memfs, err := fs.NewMemFilesystem(fs.MemConfig{})
|
|
require.NoError(t, err)
|
|
|
|
filesystems := []httpfs.FS{
|
|
{
|
|
Name: "foo",
|
|
Mountpoint: "/",
|
|
AllowWrite: true,
|
|
Filesystem: memfs,
|
|
},
|
|
}
|
|
|
|
router, err := getDummyFilesystemsRouter(filesystems)
|
|
require.NoError(t, err)
|
|
|
|
response := mock.Request(t, http.StatusOK, router, "GET", "/", nil)
|
|
|
|
mock.Validate(t, &[]api.FilesystemInfo{}, response.Data)
|
|
|
|
f := []api.FilesystemInfo{}
|
|
err = json.Unmarshal(response.Raw, &f)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, 1, len(f))
|
|
|
|
mock.Request(t, http.StatusNotFound, router, "GET", "/bar", nil)
|
|
|
|
response = mock.Request(t, http.StatusOK, router, "GET", "/foo", nil)
|
|
|
|
mock.Validate(t, &[]api.FileInfo{}, response.Data)
|
|
|
|
l := []api.FileInfo{}
|
|
err = json.Unmarshal(response.Raw, &l)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, 0, len(l))
|
|
|
|
mock.Request(t, http.StatusNotFound, router, "GET", "/bar/file", nil)
|
|
mock.Request(t, http.StatusNotFound, router, "GET", "/foo/file", nil)
|
|
|
|
data := mock.Read(t, "./fixtures/addProcess.json")
|
|
require.NoError(t, err)
|
|
|
|
mock.Request(t, http.StatusCreated, router, "PUT", "/foo/file", data)
|
|
mock.Request(t, http.StatusNotFound, router, "PUT", "/bar/file", data)
|
|
|
|
data = mock.Read(t, "./fixtures/addProcess.json")
|
|
require.NoError(t, err)
|
|
|
|
mock.Request(t, http.StatusNoContent, router, "PUT", "/foo/file", data)
|
|
|
|
require.Equal(t, 1, len(memfs.List("/", fs.ListOptions{})))
|
|
|
|
response = mock.Request(t, http.StatusOK, router, "GET", "/foo", nil)
|
|
|
|
mock.Validate(t, &[]api.FileInfo{}, response.Data)
|
|
|
|
l = []api.FileInfo{}
|
|
err = json.Unmarshal(response.Raw, &l)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, 1, len(l))
|
|
|
|
mock.Request(t, http.StatusNotFound, router, "GET", "/foo/elif", nil)
|
|
|
|
response = mock.Request(t, http.StatusOK, router, "GET", "/foo/file", nil)
|
|
|
|
databytes, err := io.ReadAll(mock.Read(t, "./fixtures/addProcess.json"))
|
|
require.NoError(t, err)
|
|
require.Equal(t, databytes, response.Raw)
|
|
|
|
mock.Request(t, http.StatusNotFound, router, "DELETE", "/foo/elif", nil)
|
|
mock.Request(t, http.StatusNotFound, router, "DELETE", "/bar/elif", nil)
|
|
mock.Request(t, http.StatusOK, router, "DELETE", "/foo/file", nil)
|
|
mock.Request(t, http.StatusNotFound, router, "GET", "/foo/file", nil)
|
|
|
|
require.Equal(t, 0, len(memfs.List("/", fs.ListOptions{})))
|
|
|
|
response = mock.Request(t, http.StatusOK, router, "GET", "/foo", nil)
|
|
|
|
mock.Validate(t, &[]api.FileInfo{}, response.Data)
|
|
|
|
l = []api.FileInfo{}
|
|
err = json.Unmarshal(response.Raw, &l)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, 0, len(l))
|
|
}
|
|
|
|
func TestFilesystemsListSize(t *testing.T) {
|
|
memfs, err := fs.NewMemFilesystem(fs.MemConfig{})
|
|
require.NoError(t, err)
|
|
|
|
memfs.WriteFileReader("/a", strings.NewReader("a"))
|
|
memfs.WriteFileReader("/aa", strings.NewReader("aa"))
|
|
memfs.WriteFileReader("/aaa", strings.NewReader("aaa"))
|
|
memfs.WriteFileReader("/aaaa", strings.NewReader("aaaa"))
|
|
|
|
filesystems := []httpfs.FS{
|
|
{
|
|
Name: "foo",
|
|
Mountpoint: "/foo",
|
|
AllowWrite: true,
|
|
Filesystem: memfs,
|
|
},
|
|
}
|
|
|
|
router, err := getDummyFilesystemsRouter(filesystems)
|
|
require.NoError(t, err)
|
|
|
|
response := mock.Request(t, http.StatusOK, router, "GET", "/foo", nil)
|
|
|
|
f := []api.FilesystemInfo{}
|
|
err = json.Unmarshal(response.Raw, &f)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, 4, len(f))
|
|
|
|
getNames := func(r *mock.Response) []string {
|
|
files := []api.FilesystemInfo{}
|
|
err := json.Unmarshal(r.Raw, &files)
|
|
require.NoError(t, err)
|
|
|
|
names := []string{}
|
|
for _, f := range files {
|
|
names = append(names, f.Name)
|
|
}
|
|
return names
|
|
}
|
|
|
|
files := getNames(mock.Request(t, http.StatusOK, router, "GET", "/foo?size_min=1", nil))
|
|
require.Equal(t, 4, len(files))
|
|
require.ElementsMatch(t, []string{"/a", "/aa", "/aaa", "/aaaa"}, files)
|
|
|
|
files = getNames(mock.Request(t, http.StatusOK, router, "GET", "/foo?size_min=2", nil))
|
|
require.Equal(t, 3, len(files))
|
|
require.ElementsMatch(t, []string{"/aa", "/aaa", "/aaaa"}, files)
|
|
|
|
files = getNames(mock.Request(t, http.StatusOK, router, "GET", "/foo?size_max=4", nil))
|
|
require.Equal(t, 4, len(files))
|
|
require.ElementsMatch(t, []string{"/a", "/aa", "/aaa", "/aaaa"}, files)
|
|
|
|
files = getNames(mock.Request(t, http.StatusOK, router, "GET", "/foo?size_min=2&size_max=3", nil))
|
|
require.Equal(t, 2, len(files))
|
|
require.ElementsMatch(t, []string{"/aa", "/aaa"}, files)
|
|
}
|
|
|
|
func TestFilesystemsListLastmod(t *testing.T) {
|
|
memfs, err := fs.NewMemFilesystem(fs.MemConfig{})
|
|
require.NoError(t, err)
|
|
|
|
memfs.WriteFileReader("/a", strings.NewReader("a"))
|
|
time.Sleep(1 * time.Second)
|
|
memfs.WriteFileReader("/b", strings.NewReader("b"))
|
|
time.Sleep(1 * time.Second)
|
|
memfs.WriteFileReader("/c", strings.NewReader("c"))
|
|
time.Sleep(1 * time.Second)
|
|
memfs.WriteFileReader("/d", strings.NewReader("d"))
|
|
|
|
var a, b, c, d time.Time
|
|
|
|
for _, f := range memfs.List("/", fs.ListOptions{}) {
|
|
if f.Name() == "/a" {
|
|
a = f.ModTime()
|
|
} else if f.Name() == "/b" {
|
|
b = f.ModTime()
|
|
} else if f.Name() == "/c" {
|
|
c = f.ModTime()
|
|
} else if f.Name() == "/d" {
|
|
d = f.ModTime()
|
|
}
|
|
}
|
|
|
|
filesystems := []httpfs.FS{
|
|
{
|
|
Name: "foo",
|
|
Mountpoint: "/foo",
|
|
AllowWrite: true,
|
|
Filesystem: memfs,
|
|
},
|
|
}
|
|
|
|
router, err := getDummyFilesystemsRouter(filesystems)
|
|
require.NoError(t, err)
|
|
|
|
getNames := func(r *mock.Response) []string {
|
|
files := []api.FilesystemInfo{}
|
|
err := json.Unmarshal(r.Raw, &files)
|
|
require.NoError(t, err)
|
|
|
|
names := []string{}
|
|
for _, f := range files {
|
|
names = append(names, f.Name)
|
|
}
|
|
return names
|
|
}
|
|
|
|
files := getNames(mock.Request(t, http.StatusOK, router, "GET", "/foo?lastmod_start="+strconv.FormatInt(a.Unix(), 10), nil))
|
|
require.Equal(t, 4, len(files))
|
|
require.ElementsMatch(t, []string{"/a", "/b", "/c", "/d"}, files)
|
|
|
|
files = getNames(mock.Request(t, http.StatusOK, router, "GET", "/foo?lastmod_start="+strconv.FormatInt(b.Unix(), 10), nil))
|
|
require.Equal(t, 3, len(files))
|
|
require.ElementsMatch(t, []string{"/b", "/c", "/d"}, files)
|
|
|
|
files = getNames(mock.Request(t, http.StatusOK, router, "GET", "/foo?lastmod_end="+strconv.FormatInt(d.Unix(), 10), nil))
|
|
require.Equal(t, 4, len(files))
|
|
require.ElementsMatch(t, []string{"/a", "/b", "/c", "/d"}, files)
|
|
|
|
files = getNames(mock.Request(t, http.StatusOK, router, "GET", "/foo?lastmod_start="+strconv.FormatInt(b.Unix(), 10)+"&lastmod_end="+strconv.FormatInt(c.Unix(), 10), nil))
|
|
require.Equal(t, 2, len(files))
|
|
require.ElementsMatch(t, []string{"/b", "/c"}, files)
|
|
}
|
|
|
|
func TestFilesystemsDeleteFiles(t *testing.T) {
|
|
memfs, err := fs.NewMemFilesystem(fs.MemConfig{})
|
|
require.NoError(t, err)
|
|
|
|
memfs.WriteFileReader("/a", strings.NewReader("a"))
|
|
memfs.WriteFileReader("/aa", strings.NewReader("aa"))
|
|
memfs.WriteFileReader("/aaa", strings.NewReader("aaa"))
|
|
memfs.WriteFileReader("/aaaa", strings.NewReader("aaaa"))
|
|
|
|
filesystems := []httpfs.FS{
|
|
{
|
|
Name: "foo",
|
|
Mountpoint: "/foo",
|
|
AllowWrite: true,
|
|
Filesystem: memfs,
|
|
},
|
|
}
|
|
|
|
router, err := getDummyFilesystemsRouter(filesystems)
|
|
require.NoError(t, err)
|
|
|
|
mock.Request(t, http.StatusBadRequest, router, "DELETE", "/foo", nil)
|
|
|
|
getNames := func(r *mock.Response) []string {
|
|
files := []string{}
|
|
err := json.Unmarshal(r.Raw, &files)
|
|
require.NoError(t, err)
|
|
|
|
return files
|
|
}
|
|
|
|
files := getNames(mock.Request(t, http.StatusOK, router, "DELETE", "/foo?glob=/**", nil))
|
|
require.Equal(t, 4, len(files))
|
|
require.ElementsMatch(t, []string{"/a", "/aa", "/aaa", "/aaaa"}, files)
|
|
|
|
require.Equal(t, int64(0), memfs.Files())
|
|
}
|
|
|
|
func TestFilesystemsDeleteFilesSize(t *testing.T) {
|
|
memfs, err := fs.NewMemFilesystem(fs.MemConfig{})
|
|
require.NoError(t, err)
|
|
|
|
memfs.WriteFileReader("/a", strings.NewReader("a"))
|
|
memfs.WriteFileReader("/aa", strings.NewReader("aa"))
|
|
memfs.WriteFileReader("/aaa", strings.NewReader("aaa"))
|
|
memfs.WriteFileReader("/aaaa", strings.NewReader("aaaa"))
|
|
|
|
filesystems := []httpfs.FS{
|
|
{
|
|
Name: "foo",
|
|
Mountpoint: "/foo",
|
|
AllowWrite: true,
|
|
Filesystem: memfs,
|
|
},
|
|
}
|
|
|
|
router, err := getDummyFilesystemsRouter(filesystems)
|
|
require.NoError(t, err)
|
|
|
|
getNames := func(r *mock.Response) []string {
|
|
files := []string{}
|
|
err := json.Unmarshal(r.Raw, &files)
|
|
require.NoError(t, err)
|
|
|
|
return files
|
|
}
|
|
|
|
files := getNames(mock.Request(t, http.StatusOK, router, "DELETE", "/foo?glob=/**&size_min=2&size_max=3", nil))
|
|
require.Equal(t, 2, len(files))
|
|
require.ElementsMatch(t, []string{"/aa", "/aaa"}, files)
|
|
|
|
require.Equal(t, int64(2), memfs.Files())
|
|
}
|
|
|
|
func TestFilesystemsDeleteFilesLastmod(t *testing.T) {
|
|
memfs, err := fs.NewMemFilesystem(fs.MemConfig{})
|
|
require.NoError(t, err)
|
|
|
|
memfs.WriteFileReader("/a", strings.NewReader("a"))
|
|
time.Sleep(1 * time.Second)
|
|
memfs.WriteFileReader("/b", strings.NewReader("b"))
|
|
time.Sleep(1 * time.Second)
|
|
memfs.WriteFileReader("/c", strings.NewReader("c"))
|
|
time.Sleep(1 * time.Second)
|
|
memfs.WriteFileReader("/d", strings.NewReader("d"))
|
|
|
|
var b, c time.Time
|
|
|
|
for _, f := range memfs.List("/", fs.ListOptions{}) {
|
|
if f.Name() == "/b" {
|
|
b = f.ModTime()
|
|
} else if f.Name() == "/c" {
|
|
c = f.ModTime()
|
|
}
|
|
}
|
|
|
|
filesystems := []httpfs.FS{
|
|
{
|
|
Name: "foo",
|
|
Mountpoint: "/foo",
|
|
AllowWrite: true,
|
|
Filesystem: memfs,
|
|
},
|
|
}
|
|
|
|
router, err := getDummyFilesystemsRouter(filesystems)
|
|
require.NoError(t, err)
|
|
|
|
getNames := func(r *mock.Response) []string {
|
|
files := []string{}
|
|
err := json.Unmarshal(r.Raw, &files)
|
|
require.NoError(t, err)
|
|
|
|
return files
|
|
}
|
|
|
|
files := getNames(mock.Request(t, http.StatusOK, router, "DELETE", "/foo?glob=/**&lastmod_start="+strconv.FormatInt(b.Unix(), 10)+"&lastmod_end="+strconv.FormatInt(c.Unix(), 10), nil))
|
|
require.Equal(t, 2, len(files))
|
|
require.ElementsMatch(t, []string{"/b", "/c"}, files)
|
|
|
|
require.Equal(t, int64(2), memfs.Files())
|
|
}
|
|
|
|
func TestFilesystemsFileOperation(t *testing.T) {
|
|
memfs1, err := fs.NewMemFilesystem(fs.MemConfig{})
|
|
require.NoError(t, err)
|
|
|
|
memfs2, err := fs.NewMemFilesystem(fs.MemConfig{})
|
|
require.NoError(t, err)
|
|
|
|
filesystems := []httpfs.FS{
|
|
{
|
|
Name: "foo",
|
|
Mountpoint: "/foo",
|
|
AllowWrite: true,
|
|
Filesystem: memfs1,
|
|
},
|
|
{
|
|
Name: "bar",
|
|
Mountpoint: "/bar",
|
|
AllowWrite: true,
|
|
Filesystem: memfs2,
|
|
},
|
|
}
|
|
|
|
router, err := getDummyFilesystemsRouter(filesystems)
|
|
require.NoError(t, err)
|
|
|
|
mock.Request(t, http.StatusNotFound, router, "GET", "/foo/file", nil)
|
|
mock.Request(t, http.StatusNotFound, router, "GET", "/bar/file", nil)
|
|
|
|
data := mock.Read(t, "./fixtures/addProcess.json")
|
|
require.NotNil(t, data)
|
|
|
|
mock.Request(t, http.StatusCreated, router, "PUT", "/foo/file", data)
|
|
mock.Request(t, http.StatusOK, router, "GET", "/foo/file", nil)
|
|
mock.Request(t, http.StatusNotFound, router, "GET", "/bar/file", nil)
|
|
|
|
op := api.FilesystemOperation{}
|
|
|
|
jsondata, err := json.Marshal(op)
|
|
require.NoError(t, err)
|
|
|
|
mock.Request(t, http.StatusBadRequest, router, "PUT", "/", bytes.NewReader(jsondata))
|
|
|
|
op = api.FilesystemOperation{
|
|
Operation: "copy",
|
|
}
|
|
|
|
jsondata, err = json.Marshal(op)
|
|
require.NoError(t, err)
|
|
|
|
mock.Request(t, http.StatusBadRequest, router, "PUT", "/", bytes.NewReader(jsondata))
|
|
|
|
op = api.FilesystemOperation{
|
|
Operation: "copy",
|
|
Source: "foo:/elif",
|
|
}
|
|
|
|
jsondata, err = json.Marshal(op)
|
|
require.NoError(t, err)
|
|
|
|
mock.Request(t, http.StatusBadRequest, router, "PUT", "/", bytes.NewReader(jsondata))
|
|
|
|
op = api.FilesystemOperation{
|
|
Operation: "copy",
|
|
Source: "foo:/elif",
|
|
Target: "/bar",
|
|
}
|
|
|
|
jsondata, err = json.Marshal(op)
|
|
require.NoError(t, err)
|
|
|
|
mock.Request(t, http.StatusBadRequest, router, "PUT", "/", bytes.NewReader(jsondata))
|
|
|
|
op = api.FilesystemOperation{
|
|
Operation: "copy",
|
|
Source: "foo:/file",
|
|
Target: "/bar",
|
|
}
|
|
|
|
jsondata, err = json.Marshal(op)
|
|
require.NoError(t, err)
|
|
|
|
mock.Request(t, http.StatusBadRequest, router, "PUT", "/", bytes.NewReader(jsondata))
|
|
|
|
op = api.FilesystemOperation{
|
|
Operation: "copy",
|
|
Source: "foo:file",
|
|
Target: "bar:/file",
|
|
}
|
|
|
|
jsondata, err = json.Marshal(op)
|
|
require.NoError(t, err)
|
|
|
|
mock.Request(t, http.StatusOK, router, "PUT", "/", bytes.NewReader(jsondata))
|
|
|
|
mock.Request(t, http.StatusOK, router, "GET", "/foo/file", nil)
|
|
response := mock.Request(t, http.StatusOK, router, "GET", "/bar/file", nil)
|
|
|
|
filedata, err := io.ReadAll(mock.Read(t, "./fixtures/addProcess.json"))
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, filedata, response.Raw)
|
|
|
|
op = api.FilesystemOperation{
|
|
Operation: "move",
|
|
Source: "foo:file",
|
|
Target: "bar:/file",
|
|
}
|
|
|
|
jsondata, err = json.Marshal(op)
|
|
require.NoError(t, err)
|
|
|
|
mock.Request(t, http.StatusOK, router, "PUT", "/", bytes.NewReader(jsondata))
|
|
|
|
mock.Request(t, http.StatusNotFound, router, "GET", "/foo/file", nil)
|
|
response = mock.Request(t, http.StatusOK, router, "GET", "/bar/file", nil)
|
|
|
|
filedata, err = io.ReadAll(mock.Read(t, "./fixtures/addProcess.json"))
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, filedata, response.Raw)
|
|
}
|
|
|
|
func TestFilesystemsPurgeCache(t *testing.T) {
|
|
memfs, err := fs.NewMemFilesystem(fs.MemConfig{})
|
|
require.NoError(t, err)
|
|
|
|
memfs.MkdirAll("/path", 0744)
|
|
|
|
cache, err := cache.NewLRUCache(cache.LRUConfig{
|
|
TTL: 5 * time.Minute,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
filesystems := []httpfs.FS{
|
|
{
|
|
Name: "foo",
|
|
Mountpoint: "/foo",
|
|
AllowWrite: true,
|
|
Filesystem: memfs,
|
|
DefaultFile: "index.html",
|
|
Cache: cache,
|
|
},
|
|
}
|
|
|
|
router, err := getDummyFilesystemsRouter(filesystems)
|
|
require.NoError(t, err)
|
|
|
|
data := mock.Read(t, "./fixtures/addProcess.json")
|
|
require.NotNil(t, data)
|
|
|
|
mock.Request(t, http.StatusCreated, router, "PUT", "/foo/path/index.html", data)
|
|
|
|
cache.Put("/path/", "foobar", 42)
|
|
cache.Put("/path/index.html", "barfoo", 42)
|
|
|
|
mock.Request(t, http.StatusOK, router, "DELETE", "/foo/path/index.html", nil)
|
|
|
|
f, _, err := cache.Get("/path/")
|
|
require.Nil(t, f)
|
|
require.NoError(t, err)
|
|
|
|
f, _, err = cache.Get("/path/index.html")
|
|
require.Nil(t, f)
|
|
require.NoError(t, err)
|
|
|
|
cache.Put("/path/", "foobar", 42)
|
|
cache.Put("/path/index.html", "barfoo", 42)
|
|
|
|
data = mock.Read(t, "./fixtures/addProcess.json")
|
|
require.NotNil(t, data)
|
|
|
|
mock.Request(t, http.StatusCreated, router, "PUT", "/foo/path/index.html", data)
|
|
|
|
f, _, err = cache.Get("/path/")
|
|
require.Nil(t, f)
|
|
require.NoError(t, err)
|
|
|
|
f, _, err = cache.Get("/path/index.html")
|
|
require.Nil(t, f)
|
|
require.NoError(t, err)
|
|
}
|