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