Add UploadService (dedup, chunk lifecycle, ComposeObject), DownloadService (Range support), FileService (ref counting), FolderService (path validation). Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
231 lines
6.2 KiB
Go
231 lines
6.2 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
|
|
"gcy_hpc_server/internal/model"
|
|
"gcy_hpc_server/internal/store"
|
|
|
|
"go.uber.org/zap"
|
|
gormlogger "gorm.io/gorm/logger"
|
|
|
|
"gorm.io/driver/sqlite"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
func setupFolderService(t *testing.T) *FolderService {
|
|
t.Helper()
|
|
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
|
|
Logger: gormlogger.Default.LogMode(gormlogger.Silent),
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("open sqlite: %v", err)
|
|
}
|
|
if err := db.AutoMigrate(&model.Folder{}, &model.File{}); err != nil {
|
|
t.Fatalf("auto migrate: %v", err)
|
|
}
|
|
return NewFolderService(
|
|
store.NewFolderStore(db),
|
|
store.NewFileStore(db),
|
|
zap.NewNop(),
|
|
)
|
|
}
|
|
|
|
func TestCreateFolder_ValidName(t *testing.T) {
|
|
svc := setupFolderService(t)
|
|
resp, err := svc.CreateFolder(context.Background(), "datasets", nil)
|
|
if err != nil {
|
|
t.Fatalf("CreateFolder() error = %v", err)
|
|
}
|
|
if resp.Name != "datasets" {
|
|
t.Errorf("Name = %q, want %q", resp.Name, "datasets")
|
|
}
|
|
if resp.Path != "/datasets/" {
|
|
t.Errorf("Path = %q, want %q", resp.Path, "/datasets/")
|
|
}
|
|
if resp.ParentID != nil {
|
|
t.Errorf("ParentID should be nil for root folder, got %d", *resp.ParentID)
|
|
}
|
|
}
|
|
|
|
func TestCreateFolder_SubFolder(t *testing.T) {
|
|
svc := setupFolderService(t)
|
|
parent, err := svc.CreateFolder(context.Background(), "datasets", nil)
|
|
if err != nil {
|
|
t.Fatalf("create parent: %v", err)
|
|
}
|
|
|
|
child, err := svc.CreateFolder(context.Background(), "images", &parent.ID)
|
|
if err != nil {
|
|
t.Fatalf("CreateFolder() error = %v", err)
|
|
}
|
|
if child.Path != "/datasets/images/" {
|
|
t.Errorf("Path = %q, want %q", child.Path, "/datasets/images/")
|
|
}
|
|
if child.ParentID == nil || *child.ParentID != parent.ID {
|
|
t.Errorf("ParentID = %v, want %d", child.ParentID, parent.ID)
|
|
}
|
|
}
|
|
|
|
func TestCreateFolder_RejectPathTraversal(t *testing.T) {
|
|
svc := setupFolderService(t)
|
|
for _, name := range []string{"..", "../etc", "/absolute", "a/b"} {
|
|
_, err := svc.CreateFolder(context.Background(), name, nil)
|
|
if err == nil {
|
|
t.Errorf("expected error for folder name %q, got nil", name)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCreateFolder_DuplicatePath(t *testing.T) {
|
|
svc := setupFolderService(t)
|
|
_, err := svc.CreateFolder(context.Background(), "datasets", nil)
|
|
if err != nil {
|
|
t.Fatalf("first create: %v", err)
|
|
}
|
|
_, err = svc.CreateFolder(context.Background(), "datasets", nil)
|
|
if err == nil {
|
|
t.Fatal("expected error for duplicate folder name")
|
|
}
|
|
if !strings.Contains(err.Error(), "already exists") {
|
|
t.Errorf("error should mention 'already exists', got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestCreateFolder_ParentNotFound(t *testing.T) {
|
|
svc := setupFolderService(t)
|
|
badID := int64(99999)
|
|
_, err := svc.CreateFolder(context.Background(), "orphan", &badID)
|
|
if err == nil {
|
|
t.Fatal("expected error for non-existent parent")
|
|
}
|
|
if !strings.Contains(err.Error(), "not found") {
|
|
t.Errorf("error should mention 'not found', got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestGetFolder(t *testing.T) {
|
|
svc := setupFolderService(t)
|
|
created, err := svc.CreateFolder(context.Background(), "datasets", nil)
|
|
if err != nil {
|
|
t.Fatalf("CreateFolder() error = %v", err)
|
|
}
|
|
|
|
resp, err := svc.GetFolder(context.Background(), created.ID)
|
|
if err != nil {
|
|
t.Fatalf("GetFolder() error = %v", err)
|
|
}
|
|
if resp.ID != created.ID {
|
|
t.Errorf("ID = %d, want %d", resp.ID, created.ID)
|
|
}
|
|
if resp.Name != "datasets" {
|
|
t.Errorf("Name = %q, want %q", resp.Name, "datasets")
|
|
}
|
|
if resp.FileCount != 0 {
|
|
t.Errorf("FileCount = %d, want 0", resp.FileCount)
|
|
}
|
|
if resp.SubFolderCount != 0 {
|
|
t.Errorf("SubFolderCount = %d, want 0", resp.SubFolderCount)
|
|
}
|
|
}
|
|
|
|
func TestGetFolder_NotFound(t *testing.T) {
|
|
svc := setupFolderService(t)
|
|
_, err := svc.GetFolder(context.Background(), 99999)
|
|
if err == nil {
|
|
t.Fatal("expected error for non-existent folder")
|
|
}
|
|
if !strings.Contains(err.Error(), "not found") {
|
|
t.Errorf("error should mention 'not found', got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestListFolders(t *testing.T) {
|
|
svc := setupFolderService(t)
|
|
parent, err := svc.CreateFolder(context.Background(), "root", nil)
|
|
if err != nil {
|
|
t.Fatalf("create root: %v", err)
|
|
}
|
|
_, err = svc.CreateFolder(context.Background(), "child1", &parent.ID)
|
|
if err != nil {
|
|
t.Fatalf("create child1: %v", err)
|
|
}
|
|
_, err = svc.CreateFolder(context.Background(), "child2", &parent.ID)
|
|
if err != nil {
|
|
t.Fatalf("create child2: %v", err)
|
|
}
|
|
|
|
list, err := svc.ListFolders(context.Background(), &parent.ID)
|
|
if err != nil {
|
|
t.Fatalf("ListFolders() error = %v", err)
|
|
}
|
|
if len(list) != 2 {
|
|
t.Fatalf("len(list) = %d, want 2", len(list))
|
|
}
|
|
|
|
names := make(map[string]bool)
|
|
for _, f := range list {
|
|
names[f.Name] = true
|
|
}
|
|
if !names["child1"] || !names["child2"] {
|
|
t.Errorf("expected child1 and child2, got %v", names)
|
|
}
|
|
}
|
|
|
|
func TestListFolders_Root(t *testing.T) {
|
|
svc := setupFolderService(t)
|
|
_, err := svc.CreateFolder(context.Background(), "alpha", nil)
|
|
if err != nil {
|
|
t.Fatalf("create alpha: %v", err)
|
|
}
|
|
_, err = svc.CreateFolder(context.Background(), "beta", nil)
|
|
if err != nil {
|
|
t.Fatalf("create beta: %v", err)
|
|
}
|
|
|
|
list, err := svc.ListFolders(context.Background(), nil)
|
|
if err != nil {
|
|
t.Fatalf("ListFolders() error = %v", err)
|
|
}
|
|
if len(list) != 2 {
|
|
t.Errorf("len(list) = %d, want 2", len(list))
|
|
}
|
|
}
|
|
|
|
func TestDeleteFolder_Success(t *testing.T) {
|
|
svc := setupFolderService(t)
|
|
created, err := svc.CreateFolder(context.Background(), "temp", nil)
|
|
if err != nil {
|
|
t.Fatalf("CreateFolder() error = %v", err)
|
|
}
|
|
if err := svc.DeleteFolder(context.Background(), created.ID); err != nil {
|
|
t.Fatalf("DeleteFolder() error = %v", err)
|
|
}
|
|
_, err = svc.GetFolder(context.Background(), created.ID)
|
|
if err == nil {
|
|
t.Error("expected error after deletion")
|
|
}
|
|
}
|
|
|
|
func TestDeleteFolder_NonEmpty(t *testing.T) {
|
|
svc := setupFolderService(t)
|
|
parent, err := svc.CreateFolder(context.Background(), "haschild", nil)
|
|
if err != nil {
|
|
t.Fatalf("create parent: %v", err)
|
|
}
|
|
_, err = svc.CreateFolder(context.Background(), "child", &parent.ID)
|
|
if err != nil {
|
|
t.Fatalf("create child: %v", err)
|
|
}
|
|
|
|
err = svc.DeleteFolder(context.Background(), parent.ID)
|
|
if err == nil {
|
|
t.Fatal("expected error when deleting non-empty folder")
|
|
}
|
|
if !strings.Contains(err.Error(), "not empty") {
|
|
t.Errorf("error should mention 'not empty', got: %v", err)
|
|
}
|
|
}
|