Add BlobStore (ref counting), FileStore (soft delete + pagination), FolderStore (materialized path), UploadStore (idempotent upsert), and update AutoMigrate. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
147 lines
3.6 KiB
Go
147 lines
3.6 KiB
Go
package store
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"gcy_hpc_server/internal/model"
|
|
|
|
"gorm.io/driver/sqlite"
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/logger"
|
|
)
|
|
|
|
func setupBlobTestDB(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.FileBlob{}); err != nil {
|
|
t.Fatalf("migrate: %v", err)
|
|
}
|
|
return db
|
|
}
|
|
|
|
func TestBlobStore_Create(t *testing.T) {
|
|
db := setupBlobTestDB(t)
|
|
store := NewBlobStore(db)
|
|
ctx := context.Background()
|
|
|
|
blob := &model.FileBlob{
|
|
SHA256: "abc123",
|
|
MinioKey: "files/abc123",
|
|
FileSize: 1024,
|
|
MimeType: "application/octet-stream",
|
|
RefCount: 0,
|
|
}
|
|
if err := store.Create(ctx, blob); err != nil {
|
|
t.Fatalf("Create() error = %v", err)
|
|
}
|
|
if blob.ID == 0 {
|
|
t.Error("Create() did not set ID")
|
|
}
|
|
}
|
|
|
|
func TestBlobStore_GetBySHA256(t *testing.T) {
|
|
db := setupBlobTestDB(t)
|
|
store := NewBlobStore(db)
|
|
ctx := context.Background()
|
|
|
|
store.Create(ctx, &model.FileBlob{SHA256: "abc", MinioKey: "files/abc", FileSize: 100, RefCount: 0})
|
|
|
|
blob, err := store.GetBySHA256(ctx, "abc")
|
|
if err != nil {
|
|
t.Fatalf("GetBySHA256() error = %v", err)
|
|
}
|
|
if blob == nil {
|
|
t.Fatal("GetBySHA256() returned nil")
|
|
}
|
|
if blob.SHA256 != "abc" {
|
|
t.Errorf("SHA256 = %q, want %q", blob.SHA256, "abc")
|
|
}
|
|
if blob.RefCount != 0 {
|
|
t.Errorf("RefCount = %d, want 0", blob.RefCount)
|
|
}
|
|
}
|
|
|
|
func TestBlobStore_GetBySHA256_NotFound(t *testing.T) {
|
|
db := setupBlobTestDB(t)
|
|
store := NewBlobStore(db)
|
|
ctx := context.Background()
|
|
|
|
blob, err := store.GetBySHA256(ctx, "nonexistent")
|
|
if err != nil {
|
|
t.Fatalf("GetBySHA256() error = %v", err)
|
|
}
|
|
if blob != nil {
|
|
t.Error("GetBySHA256() should return nil for not found")
|
|
}
|
|
}
|
|
|
|
func TestBlobStore_IncrementDecrementRef(t *testing.T) {
|
|
db := setupBlobTestDB(t)
|
|
store := NewBlobStore(db)
|
|
ctx := context.Background()
|
|
|
|
store.Create(ctx, &model.FileBlob{SHA256: "abc", MinioKey: "files/abc", FileSize: 100, RefCount: 0})
|
|
|
|
if err := store.IncrementRef(ctx, "abc"); err != nil {
|
|
t.Fatalf("IncrementRef() error = %v", err)
|
|
}
|
|
blob, _ := store.GetBySHA256(ctx, "abc")
|
|
if blob.RefCount != 1 {
|
|
t.Errorf("RefCount after 1st increment = %d, want 1", blob.RefCount)
|
|
}
|
|
|
|
store.IncrementRef(ctx, "abc")
|
|
blob, _ = store.GetBySHA256(ctx, "abc")
|
|
if blob.RefCount != 2 {
|
|
t.Errorf("RefCount after 2nd increment = %d, want 2", blob.RefCount)
|
|
}
|
|
|
|
refCount, err := store.DecrementRef(ctx, "abc")
|
|
if err != nil {
|
|
t.Fatalf("DecrementRef() error = %v", err)
|
|
}
|
|
if refCount != 1 {
|
|
t.Errorf("DecrementRef() returned %d, want 1", refCount)
|
|
}
|
|
|
|
refCount, _ = store.DecrementRef(ctx, "abc")
|
|
if refCount != 0 {
|
|
t.Errorf("DecrementRef() returned %d, want 0", refCount)
|
|
}
|
|
}
|
|
|
|
func TestBlobStore_Delete(t *testing.T) {
|
|
db := setupBlobTestDB(t)
|
|
store := NewBlobStore(db)
|
|
ctx := context.Background()
|
|
|
|
store.Create(ctx, &model.FileBlob{SHA256: "abc", MinioKey: "files/abc", FileSize: 100, RefCount: 0})
|
|
|
|
if err := store.Delete(ctx, "abc"); err != nil {
|
|
t.Fatalf("Delete() error = %v", err)
|
|
}
|
|
|
|
blob, _ := store.GetBySHA256(ctx, "abc")
|
|
if blob != nil {
|
|
t.Error("Delete() did not remove blob")
|
|
}
|
|
}
|
|
|
|
func TestBlobStore_SHA256_UniqueConstraint(t *testing.T) {
|
|
db := setupBlobTestDB(t)
|
|
store := NewBlobStore(db)
|
|
ctx := context.Background()
|
|
|
|
store.Create(ctx, &model.FileBlob{SHA256: "dup", MinioKey: "files/dup1", FileSize: 100})
|
|
err := store.Create(ctx, &model.FileBlob{SHA256: "dup", MinioKey: "files/dup2", FileSize: 200})
|
|
if err == nil {
|
|
t.Error("expected error for duplicate SHA256, got nil")
|
|
}
|
|
}
|