mirror of
https://github.com/pocketbase/pocketbase.git
synced 2025-03-24 07:21:04 +02:00
Currently, css files are served as text/plain by the server. It is not trivial to detect css file types similar to the issue with svg files. When the css files are served as text/plain instead of text/css they become unusable as stylesheets in the browser when served via the api. In this commit we generalize the svg detection to also detect css files and serve specific extensions with their respective mimetypes.
344 lines
7.9 KiB
Go
344 lines
7.9 KiB
Go
package filesystem_test
|
|
|
|
import (
|
|
"image"
|
|
"image/png"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/pocketbase/pocketbase/tools/filesystem"
|
|
)
|
|
|
|
func TestFileSystemExists(t *testing.T) {
|
|
dir := createTestDir(t)
|
|
defer os.RemoveAll(dir)
|
|
|
|
fs, err := filesystem.NewLocal(dir)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer fs.Close()
|
|
|
|
scenarios := []struct {
|
|
file string
|
|
exists bool
|
|
}{
|
|
{"sub1.txt", false},
|
|
{"test/sub1.txt", true},
|
|
{"test/sub2.txt", true},
|
|
{"image.png", true},
|
|
}
|
|
|
|
for i, scenario := range scenarios {
|
|
exists, _ := fs.Exists(scenario.file)
|
|
|
|
if exists != scenario.exists {
|
|
t.Errorf("(%d) Expected %v, got %v", i, scenario.exists, exists)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestFileSystemAttributes(t *testing.T) {
|
|
dir := createTestDir(t)
|
|
defer os.RemoveAll(dir)
|
|
|
|
fs, err := filesystem.NewLocal(dir)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer fs.Close()
|
|
|
|
scenarios := []struct {
|
|
file string
|
|
expectError bool
|
|
expectContentType string
|
|
}{
|
|
{"sub1.txt", true, ""},
|
|
{"test/sub1.txt", false, "application/octet-stream"},
|
|
{"test/sub2.txt", false, "application/octet-stream"},
|
|
{"image.png", false, "image/png"},
|
|
}
|
|
|
|
for i, scenario := range scenarios {
|
|
attr, err := fs.Attributes(scenario.file)
|
|
|
|
if err == nil && scenario.expectError {
|
|
t.Errorf("(%d) Expected error, got nil", i)
|
|
}
|
|
|
|
if err != nil && !scenario.expectError {
|
|
t.Errorf("(%d) Expected nil, got error, %v", i, err)
|
|
}
|
|
|
|
if err == nil && attr.ContentType != scenario.expectContentType {
|
|
t.Errorf("(%d) Expected attr.ContentType to be %q, got %q", i, scenario.expectContentType, attr.ContentType)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestFileSystemDelete(t *testing.T) {
|
|
dir := createTestDir(t)
|
|
defer os.RemoveAll(dir)
|
|
|
|
fs, err := filesystem.NewLocal(dir)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer fs.Close()
|
|
|
|
if err := fs.Delete("missing.txt"); err == nil {
|
|
t.Fatal("Expected error, got nil")
|
|
}
|
|
|
|
if err := fs.Delete("image.png"); err != nil {
|
|
t.Fatalf("Expected nil, got error %v", err)
|
|
}
|
|
}
|
|
|
|
func TestFileSystemDeletePrefix(t *testing.T) {
|
|
dir := createTestDir(t)
|
|
defer os.RemoveAll(dir)
|
|
|
|
fs, err := filesystem.NewLocal(dir)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer fs.Close()
|
|
|
|
if errs := fs.DeletePrefix(""); len(errs) == 0 {
|
|
t.Fatal("Expected error, got nil", errs)
|
|
}
|
|
|
|
if errs := fs.DeletePrefix("missing/"); len(errs) != 0 {
|
|
t.Fatalf("Not existing prefix shouldn't error, got %v", errs)
|
|
}
|
|
|
|
if errs := fs.DeletePrefix("test"); len(errs) != 0 {
|
|
t.Fatalf("Expected nil, got errors %v", errs)
|
|
}
|
|
|
|
// ensure that the test/ files are deleted
|
|
if exists, _ := fs.Exists("test/sub1.txt"); exists {
|
|
t.Fatalf("Expected test/sub1.txt to be deleted")
|
|
}
|
|
if exists, _ := fs.Exists("test/sub2.txt"); exists {
|
|
t.Fatalf("Expected test/sub2.txt to be deleted")
|
|
}
|
|
}
|
|
|
|
func TestFileSystemUpload(t *testing.T) {
|
|
dir := createTestDir(t)
|
|
defer os.RemoveAll(dir)
|
|
|
|
fs, err := filesystem.NewLocal(dir)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer fs.Close()
|
|
|
|
uploadErr := fs.Upload([]byte("demo"), "newdir/newkey.txt")
|
|
if uploadErr != nil {
|
|
t.Fatal(uploadErr)
|
|
}
|
|
|
|
if exists, _ := fs.Exists("newdir/newkey.txt"); !exists {
|
|
t.Fatalf("Expected newdir/newkey.txt to exist")
|
|
}
|
|
}
|
|
|
|
func TestFileSystemServe(t *testing.T) {
|
|
dir := createTestDir(t)
|
|
defer os.RemoveAll(dir)
|
|
|
|
fs, err := filesystem.NewLocal(dir)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer fs.Close()
|
|
|
|
scenarios := []struct {
|
|
path string
|
|
name string
|
|
expectError bool
|
|
expectHeaders map[string]string
|
|
}{
|
|
{
|
|
// missing
|
|
"missing.txt",
|
|
"test_name.txt",
|
|
true,
|
|
nil,
|
|
},
|
|
{
|
|
// existing regular file
|
|
"test/sub1.txt",
|
|
"test_name.txt",
|
|
false,
|
|
map[string]string{
|
|
"Content-Disposition": "attachment; filename=test_name.txt",
|
|
"Content-Type": "application/octet-stream",
|
|
"Content-Length": "0",
|
|
"Content-Security-Policy": "default-src 'none'; style-src 'unsafe-inline'; sandbox",
|
|
},
|
|
},
|
|
{
|
|
// png inline
|
|
"image.png",
|
|
"test_name.png",
|
|
false,
|
|
map[string]string{
|
|
"Content-Disposition": "inline; filename=test_name.png",
|
|
"Content-Type": "image/png",
|
|
"Content-Length": "73",
|
|
"Content-Security-Policy": "default-src 'none'; style-src 'unsafe-inline'; sandbox",
|
|
},
|
|
},
|
|
{
|
|
// svg exception
|
|
"image.svg",
|
|
"test_name.svg",
|
|
false,
|
|
map[string]string{
|
|
"Content-Disposition": "attachment; filename=test_name.svg",
|
|
"Content-Type": "image/svg+xml",
|
|
"Content-Length": "0",
|
|
"Content-Security-Policy": "default-src 'none'; style-src 'unsafe-inline'; sandbox",
|
|
},
|
|
},
|
|
{
|
|
// css exception
|
|
"style.css",
|
|
"test_name.css",
|
|
false,
|
|
map[string]string{
|
|
"Content-Disposition": "attachment; filename=test_name.css",
|
|
"Content-Type": "text/css",
|
|
"Content-Length": "0",
|
|
"Content-Security-Policy": "default-src 'none'; style-src 'unsafe-inline'; sandbox",
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, scenario := range scenarios {
|
|
r := httptest.NewRecorder()
|
|
|
|
err := fs.Serve(r, scenario.path, scenario.name)
|
|
hasErr := err != nil
|
|
|
|
if hasErr != scenario.expectError {
|
|
t.Errorf("(%s) Expected hasError %v, got %v (%v)", scenario.path, scenario.expectError, hasErr, err)
|
|
continue
|
|
}
|
|
|
|
result := r.Result()
|
|
|
|
for hName, hValue := range scenario.expectHeaders {
|
|
v := result.Header.Get(hName)
|
|
if v != hValue {
|
|
t.Errorf("(%s) Expected value %q for header %q, got %q", scenario.path, hValue, hName, v)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestFileSystemCreateThumb(t *testing.T) {
|
|
dir := createTestDir(t)
|
|
defer os.RemoveAll(dir)
|
|
|
|
fs, err := filesystem.NewLocal(dir)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer fs.Close()
|
|
|
|
scenarios := []struct {
|
|
file string
|
|
thumb string
|
|
cropCenter bool
|
|
expectError bool
|
|
}{
|
|
// missing
|
|
{"missing.txt", "thumb_test_missing", true, true},
|
|
// non-image existing file
|
|
{"test/sub1.txt", "thumb_test_sub1", true, true},
|
|
// existing image file - crop center
|
|
{"image.png", "thumb_file_center", true, false},
|
|
// existing image file - crop top
|
|
{"image.png", "thumb_file_top", false, false},
|
|
// existing image file with existing thumb path = should fail
|
|
{"image.png", "test", true, true},
|
|
}
|
|
|
|
for i, scenario := range scenarios {
|
|
err := fs.CreateThumb(scenario.file, scenario.thumb, "100x100")
|
|
|
|
hasErr := err != nil
|
|
if hasErr != scenario.expectError {
|
|
t.Errorf("(%d) Expected hasErr to be %v, got %v (%v)", i, scenario.expectError, hasErr, err)
|
|
continue
|
|
}
|
|
|
|
if scenario.expectError {
|
|
continue
|
|
}
|
|
|
|
if exists, _ := fs.Exists(scenario.thumb); !exists {
|
|
t.Errorf("(%d) Couldn't find %q thumb", i, scenario.thumb)
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---
|
|
|
|
func createTestDir(t *testing.T) string {
|
|
dir, err := os.MkdirTemp(os.TempDir(), "pb_test")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := os.MkdirAll(filepath.Join(dir, "test"), os.ModePerm); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
file1, err := os.OpenFile(filepath.Join(dir, "test/sub1.txt"), os.O_WRONLY|os.O_CREATE, 0666)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
file1.Close()
|
|
|
|
file2, err := os.OpenFile(filepath.Join(dir, "test/sub2.txt"), os.O_WRONLY|os.O_CREATE, 0666)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
file2.Close()
|
|
|
|
file3, err := os.OpenFile(filepath.Join(dir, "image.png"), os.O_WRONLY|os.O_CREATE, 0666)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// tiny 1x1 png
|
|
imgRect := image.Rect(0, 0, 1, 1)
|
|
png.Encode(file3, imgRect)
|
|
file3.Close()
|
|
err2 := os.WriteFile(filepath.Join(dir, "image.png.attrs"), []byte(`{"user.cache_control":"","user.content_disposition":"","user.content_encoding":"","user.content_language":"","user.content_type":"image/png","user.metadata":null}`), 0666)
|
|
if err2 != nil {
|
|
t.Fatal(err2)
|
|
}
|
|
|
|
file4, err := os.OpenFile(filepath.Join(dir, "image.svg"), os.O_WRONLY|os.O_CREATE, 0666)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
file4.Close()
|
|
|
|
file5, err := os.OpenFile(filepath.Join(dir, "style.css"), os.O_WRONLY|os.O_CREATE, 0666)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
file5.Close()
|
|
|
|
return dir
|
|
}
|