package library import ( "context" "errors" "io" "log/slog" "os" "path/filepath" "testing" "github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgxpool" "git.fabledsword.com/bvandeusen/minstrel/internal/db" "git.fabledsword.com/bvandeusen/minstrel/internal/db/dbq" "git.fabledsword.com/bvandeusen/minstrel/internal/dbtest" ) func newPool(t *testing.T) *pgxpool.Pool { t.Helper() if testing.Short() { t.Skip("skipping integration test in -short mode") } dsn := os.Getenv("MINSTREL_TEST_DATABASE_URL") if dsn == "" { t.Skip("MINSTREL_TEST_DATABASE_URL not set") } if err := db.Migrate(dsn, slog.New(slog.NewTextHandler(io.Discard, nil))); err != nil { t.Fatalf("migrate: %v", err) } pool, err := pgxpool.New(context.Background(), dsn) if err != nil { t.Fatalf("pool: %v", err) } t.Cleanup(pool.Close) dbtest.ResetDB(t, pool) return pool } func seedTrack(t *testing.T, pool *pgxpool.Pool, filePath string) (dbq.Track, dbq.Album, dbq.Artist) { t.Helper() q := dbq.New(pool) artist, err := q.UpsertArtist(context.Background(), dbq.UpsertArtistParams{ Name: "Delete Test Artist", SortName: "Delete Test Artist", }) if err != nil { t.Fatalf("artist: %v", err) } album, err := q.UpsertAlbum(context.Background(), dbq.UpsertAlbumParams{ Title: "Delete Test Album", SortTitle: "Delete Test Album", ArtistID: artist.ID, }) if err != nil { t.Fatalf("album: %v", err) } track, err := q.UpsertTrack(context.Background(), dbq.UpsertTrackParams{ Title: "Delete Test Track", AlbumID: album.ID, ArtistID: artist.ID, DurationMs: 1000, FilePath: filePath, FileSize: 100, FileFormat: "mp3", }) if err != nil { t.Fatalf("track: %v", err) } return track, album, artist } func TestDeleteTrackFile_HappyPath(t *testing.T) { pool := newPool(t) q := dbq.New(pool) dir := t.TempDir() path := filepath.Join(dir, "track.mp3") if err := os.WriteFile(path, []byte("payload"), 0o644); err != nil { t.Fatalf("write file: %v", err) } track, album, _ := seedTrack(t, pool, path) if err := DeleteTrackFile(context.Background(), pool, track.ID); err != nil { t.Fatalf("DeleteTrackFile: %v", err) } if _, err := os.Stat(path); !errors.Is(err, os.ErrNotExist) { t.Errorf("file still exists: %v", err) } if _, err := q.GetTrackByID(context.Background(), track.ID); err == nil { t.Errorf("track row still exists") } // Album row preserved (other tracks may reference it). if _, err := q.GetAlbumByID(context.Background(), album.ID); err != nil { t.Errorf("album row vanished: %v", err) } } func TestDeleteTrackFile_FileAlreadyGoneSucceeds(t *testing.T) { pool := newPool(t) q := dbq.New(pool) track, _, _ := seedTrack(t, pool, "/no/such/file/anywhere.mp3") if err := DeleteTrackFile(context.Background(), pool, track.ID); err != nil { t.Fatalf("DeleteTrackFile with missing file: %v", err) } if _, err := q.GetTrackByID(context.Background(), track.ID); err == nil { t.Errorf("track row still exists") } } func TestDeleteTrackFile_NotFoundReturnsErr(t *testing.T) { pool := newPool(t) var bogus pgtype.UUID bogus.Bytes = [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} bogus.Valid = true err := DeleteTrackFile(context.Background(), pool, bogus) if !errors.Is(err, ErrTrackNotFound) { t.Errorf("err = %v, want ErrTrackNotFound", err) } }