//go:build integration package uploads import ( "context" "crypto/sha256" "encoding/hex" "log/slog" "os" "testing" "github.com/google/uuid" "github.com/stretchr/testify/require" "pipu/api/internal/auth" "pipu/api/internal/library" "pipu/api/internal/platform/db" ) func TestUploadedArtistPortraitStagingLifetime(t *testing.T) { address := os.Getenv("TEST_DATABASE_URL") if address == "" { t.Skip("TEST_DATABASE_URL is not set") } ctx := context.Background() pool, err := db.Connect(ctx, address) require.NoError(t, err) t.Cleanup(pool.Close) require.NoError(t, db.Migrate(ctx, pool)) store := NewStore(pool) author := uuid.New() _, err = pool.Exec(ctx, `INSERT INTO users(id,username,password_hash,role) VALUES($1,$2,'x','admin')`, author, "portrait-"+author.String()) require.NoError(t, err) t.Cleanup(func() { _, err := pool.Exec(ctx, `DELETE FROM upload_batches WHERE author_id=$1`, author) require.NoError(t, err) _, err = pool.Exec(ctx, `DELETE FROM users WHERE id=$1`, author) require.NoError(t, err) require.NoError(t, store.ReconcileQuota(ctx)) }) svc := NewService(store, nil, nil, nil, nil, nil, library.FPCalc{}, Config{StagingDir: t.TempDir()}, slog.New(slog.DiscardHandler)) owner := auth.WithPrincipal(ctx, auth.Principal{UserID: author, Role: auth.RoleAdmin}) files := []CreateFile{} for _, name := range []string{"one.flac", "two.flac"} { sum := sha256.Sum256([]byte(uuid.NewString())) files = append(files, CreateFile{ClientID: name, Name: name, Size: 10, SHA256: hex.EncodeToString(sum[:])}) } batch, reserved, err := svc.Create(owner, nil, files, nil, []byte{0xff, 0xd8, 0xff, 1}) require.NoError(t, err) require.Len(t, reserved, 2) first, err := store.File(ctx, reserved[0].FileID) require.NoError(t, err) require.NotNil(t, first.ArtistImagePath) image := svc.stagingAbs(*first.ArtistImagePath) require.FileExists(t, image) inUse, err := store.StagingInUse(ctx) require.NoError(t, err) require.True(t, inUse[*first.ArtistImagePath], "restart reconciliation must retain pending portraits") require.NoError(t, svc.Withdraw(owner, first.ID)) require.FileExists(t, image, "the second file still owns this portrait") require.NoError(t, svc.Withdraw(owner, reserved[1].FileID)) require.NoFileExists(t, image) unused, err := store.UnusedArtistImage(ctx, batch) require.NoError(t, err) require.Equal(t, first.ArtistImagePath, unused) }