Files
hpc/internal/service/folder_service_test.go
dailz f0847d3978 feat(service): add upload, download, file, and folder services
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>
2026-04-15 09:23:09 +08:00

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)
}
}