package store import ( "context" "testing" "gcy_hpc_server/internal/model" "gorm.io/driver/sqlite" "gorm.io/gorm" "gorm.io/gorm/logger" ) func setupFolderTestDB(t *testing.T) *gorm.DB { t.Helper() db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) if err != nil { t.Fatalf("open sqlite: %v", err) } if err := db.AutoMigrate(&model.Folder{}, &model.File{}); err != nil { t.Fatalf("migrate: %v", err) } return db } func TestFolderStore_CreateAndGetByID(t *testing.T) { db := setupFolderTestDB(t) s := NewFolderStore(db) folder := &model.Folder{ Name: "data", Path: "/data/", } if err := s.Create(context.Background(), folder); err != nil { t.Fatalf("Create() error = %v", err) } if folder.ID <= 0 { t.Fatalf("Create() id = %d, want positive", folder.ID) } got, err := s.GetByID(context.Background(), folder.ID) if err != nil { t.Fatalf("GetByID() error = %v", err) } if got == nil { t.Fatal("GetByID() returned nil") } if got.Name != "data" { t.Errorf("Name = %q, want %q", got.Name, "data") } if got.Path != "/data/" { t.Errorf("Path = %q, want %q", got.Path, "/data/") } } func TestFolderStore_GetByID_NotFound(t *testing.T) { db := setupFolderTestDB(t) s := NewFolderStore(db) got, err := s.GetByID(context.Background(), 99999) if err != nil { t.Fatalf("GetByID() error = %v", err) } if got != nil { t.Error("GetByID() expected nil for not-found") } } func TestFolderStore_GetByPath(t *testing.T) { db := setupFolderTestDB(t) s := NewFolderStore(db) folder := &model.Folder{ Name: "data", Path: "/data/", } if err := s.Create(context.Background(), folder); err != nil { t.Fatalf("Create() error = %v", err) } got, err := s.GetByPath(context.Background(), "/data/") if err != nil { t.Fatalf("GetByPath() error = %v", err) } if got == nil { t.Fatal("GetByPath() returned nil") } if got.ID != folder.ID { t.Errorf("ID = %d, want %d", got.ID, folder.ID) } got, err = s.GetByPath(context.Background(), "/nonexistent/") if err != nil { t.Fatalf("GetByPath() nonexistent error = %v", err) } if got != nil { t.Error("GetByPath() expected nil for nonexistent path") } } func TestFolderStore_ListByParentID(t *testing.T) { db := setupFolderTestDB(t) s := NewFolderStore(db) root1 := &model.Folder{Name: "alpha", Path: "/alpha/"} root2 := &model.Folder{Name: "beta", Path: "/beta/"} if err := s.Create(context.Background(), root1); err != nil { t.Fatalf("Create root1: %v", err) } if err := s.Create(context.Background(), root2); err != nil { t.Fatalf("Create root2: %v", err) } sub := &model.Folder{Name: "sub", Path: "/alpha/sub/", ParentID: &root1.ID} if err := s.Create(context.Background(), sub); err != nil { t.Fatalf("Create sub: %v", err) } roots, err := s.ListByParentID(context.Background(), nil) if err != nil { t.Fatalf("ListByParentID(nil) error = %v", err) } if len(roots) != 2 { t.Fatalf("root folders = %d, want 2", len(roots)) } if roots[0].Name != "alpha" { t.Errorf("roots[0].Name = %q, want %q (alphabetical)", roots[0].Name, "alpha") } children, err := s.ListByParentID(context.Background(), &root1.ID) if err != nil { t.Fatalf("ListByParentID(root1) error = %v", err) } if len(children) != 1 { t.Fatalf("children = %d, want 1", len(children)) } if children[0].Name != "sub" { t.Errorf("children[0].Name = %q, want %q", children[0].Name, "sub") } } func TestFolderStore_GetSubTree(t *testing.T) { db := setupFolderTestDB(t) s := NewFolderStore(db) data := &model.Folder{Name: "data", Path: "/data/"} if err := s.Create(context.Background(), data); err != nil { t.Fatalf("Create data: %v", err) } results := &model.Folder{Name: "results", Path: "/data/results/", ParentID: &data.ID} if err := s.Create(context.Background(), results); err != nil { t.Fatalf("Create results: %v", err) } other := &model.Folder{Name: "other", Path: "/other/"} if err := s.Create(context.Background(), other); err != nil { t.Fatalf("Create other: %v", err) } subtree, err := s.GetSubTree(context.Background(), "/data/") if err != nil { t.Fatalf("GetSubTree() error = %v", err) } if len(subtree) != 2 { t.Fatalf("subtree = %d, want 2", len(subtree)) } } func TestFolderStore_HasChildren_WithSubFolders(t *testing.T) { db := setupFolderTestDB(t) s := NewFolderStore(db) parent := &model.Folder{Name: "parent", Path: "/parent/"} if err := s.Create(context.Background(), parent); err != nil { t.Fatalf("Create parent: %v", err) } child := &model.Folder{Name: "child", Path: "/parent/child/", ParentID: &parent.ID} if err := s.Create(context.Background(), child); err != nil { t.Fatalf("Create child: %v", err) } has, err := s.HasChildren(context.Background(), parent.ID) if err != nil { t.Fatalf("HasChildren() error = %v", err) } if !has { t.Error("HasChildren() = false, want true (has sub-folders)") } } func TestFolderStore_HasChildren_WithFiles(t *testing.T) { db := setupFolderTestDB(t) s := NewFolderStore(db) folder := &model.Folder{Name: "docs", Path: "/docs/"} if err := s.Create(context.Background(), folder); err != nil { t.Fatalf("Create folder: %v", err) } file := &model.File{ Name: "readme.txt", FolderID: &folder.ID, BlobSHA256: "abc123", } if err := db.Create(file).Error; err != nil { t.Fatalf("Create file: %v", err) } has, err := s.HasChildren(context.Background(), folder.ID) if err != nil { t.Fatalf("HasChildren() error = %v", err) } if !has { t.Error("HasChildren() = false, want true (has files)") } } func TestFolderStore_HasChildren_Empty(t *testing.T) { db := setupFolderTestDB(t) s := NewFolderStore(db) folder := &model.Folder{Name: "empty", Path: "/empty/"} if err := s.Create(context.Background(), folder); err != nil { t.Fatalf("Create folder: %v", err) } has, err := s.HasChildren(context.Background(), folder.ID) if err != nil { t.Fatalf("HasChildren() error = %v", err) } if has { t.Error("HasChildren() = true, want false (empty folder)") } } func TestFolderStore_HasChildren_NotFound(t *testing.T) { db := setupFolderTestDB(t) s := NewFolderStore(db) has, err := s.HasChildren(context.Background(), 99999) if err != nil { t.Fatalf("HasChildren() error = %v", err) } if has { t.Error("HasChildren() = true for nonexistent, want false") } } func TestFolderStore_Delete(t *testing.T) { db := setupFolderTestDB(t) s := NewFolderStore(db) folder := &model.Folder{Name: "temp", Path: "/temp/"} if err := s.Create(context.Background(), folder); err != nil { t.Fatalf("Create() error = %v", err) } if err := s.Delete(context.Background(), folder.ID); err != nil { t.Fatalf("Delete() error = %v", err) } got, err := s.GetByID(context.Background(), folder.ID) if err != nil { t.Fatalf("GetByID() after delete error = %v", err) } if got != nil { t.Error("GetByID() after delete returned non-nil, expected soft-deleted") } } func TestFolderStore_Delete_Idempotent(t *testing.T) { db := setupFolderTestDB(t) s := NewFolderStore(db) if err := s.Delete(context.Background(), 99999); err != nil { t.Fatalf("Delete() non-existent error = %v, want nil (idempotent)", err) } } func TestFolderStore_Path_UniqueConstraint(t *testing.T) { db := setupFolderTestDB(t) s := NewFolderStore(db) f1 := &model.Folder{Name: "data", Path: "/data/"} if err := s.Create(context.Background(), f1); err != nil { t.Fatalf("Create first: %v", err) } f2 := &model.Folder{Name: "data2", Path: "/data/"} if err := s.Create(context.Background(), f2); err == nil { t.Fatal("expected error for duplicate path, got nil") } }