Add file management API with local storage backend
- Implement FileHandler with CRUD operations for files/directories - Add FileService with business logic and SHA-256 hashing - Create LocalStorage backend for filesystem persistence - Add database repository with pagination and name uniqueness constraints - Configure max upload size in storage settings - Include comprehensive tests for all layers
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSaveAndOpen(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
store, err := NewLocalStorage(dir)
|
||||
if err != nil {
|
||||
t.Fatalf("NewLocalStorage: %v", err)
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
content := "hello, mygo storage"
|
||||
written, err := store.Save(ctx, "test/hello.txt", strings.NewReader(content))
|
||||
if err != nil {
|
||||
t.Fatalf("Save: %v", err)
|
||||
}
|
||||
if written != int64(len(content)) {
|
||||
t.Errorf("written = %d, want %d", written, len(content))
|
||||
}
|
||||
|
||||
reader, err := store.Open(ctx, "test/hello.txt")
|
||||
if err != nil {
|
||||
t.Fatalf("Open: %v", err)
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
got, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
t.Fatalf("ReadAll: %v", err)
|
||||
}
|
||||
if string(got) != content {
|
||||
t.Errorf("content = %q, want %q", string(got), content)
|
||||
}
|
||||
|
||||
// Verify the file exists on disk under the base path.
|
||||
expectedPath := dir + "/test/hello.txt"
|
||||
if _, err := os.Stat(expectedPath); err != nil {
|
||||
t.Errorf("file not found on disk at %s: %v", expectedPath, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
store, err := NewLocalStorage(dir)
|
||||
if err != nil {
|
||||
t.Fatalf("NewLocalStorage: %v", err)
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
_, err = store.Save(ctx, "to_delete.txt", strings.NewReader("delete me"))
|
||||
if err != nil {
|
||||
t.Fatalf("Save: %v", err)
|
||||
}
|
||||
|
||||
if err := store.Delete(ctx, "to_delete.txt"); err != nil {
|
||||
t.Fatalf("Delete: %v", err)
|
||||
}
|
||||
|
||||
_, err = store.Open(ctx, "to_delete.txt")
|
||||
if err == nil {
|
||||
t.Error("Open should fail after delete")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteIdempotent(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
store, err := NewLocalStorage(dir)
|
||||
if err != nil {
|
||||
t.Fatalf("NewLocalStorage: %v", err)
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
// Deleting a non-existent file should not error.
|
||||
if err := store.Delete(ctx, "nonexistent.txt"); err != nil {
|
||||
t.Errorf("Delete nonexistent should be idempotent, got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPathTraversalRejected(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
store, err := NewLocalStorage(dir)
|
||||
if err != nil {
|
||||
t.Fatalf("NewLocalStorage: %v", err)
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
_, err = store.Save(ctx, "../escape.txt", strings.NewReader("malicious"))
|
||||
if err == nil {
|
||||
t.Error("Save should reject path traversal")
|
||||
} else if !strings.Contains(err.Error(), "path traversal") {
|
||||
t.Errorf("expected path traversal error, got: %v", err)
|
||||
}
|
||||
|
||||
_, err = store.Open(ctx, "../escape.txt")
|
||||
if err == nil {
|
||||
t.Error("Open should reject path traversal")
|
||||
} else if !strings.Contains(err.Error(), "path traversal") {
|
||||
t.Errorf("expected path traversal error, got: %v", err)
|
||||
}
|
||||
|
||||
err = store.Delete(ctx, "../escape.txt")
|
||||
if err == nil {
|
||||
t.Error("Delete should reject path traversal")
|
||||
} else if !strings.Contains(err.Error(), "path traversal") {
|
||||
t.Errorf("expected path traversal error, got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenMissingFile(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
store, err := NewLocalStorage(dir)
|
||||
if err != nil {
|
||||
t.Fatalf("NewLocalStorage: %v", err)
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
_, err = store.Open(ctx, "no/such/file.txt")
|
||||
if err == nil {
|
||||
t.Error("Open should error for missing file")
|
||||
} else if !strings.Contains(err.Error(), "not found on disk") {
|
||||
t.Errorf("expected 'not found on disk' error, got: %v", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user