From 81c7c9e5cc3a9be1e19d579a2b8177bc236db766 Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Mon, 30 Mar 2026 17:51:06 -0400 Subject: [PATCH] fix: update type handling for schema changes - Fix pgtype.UUID usage in test files by properly converting string UUIDs to pgtype.UUID - Update numericToFloat to use pgtype.Float8 instead of pgtype.Numeric for DOUBLE PRECISION support - Fix field name from WebURL to WebUrl to match current schema These changes align with the recent community_rating type change to DOUBLE PRECISION and ensure consistent type handling across the codebase. --- cmd/server/tests/comic_metadata_test.go | 47 +++++++++++++------------ internal/handlers/media.go | 14 ++++---- 2 files changed, 32 insertions(+), 29 deletions(-) diff --git a/cmd/server/tests/comic_metadata_test.go b/cmd/server/tests/comic_metadata_test.go index c65212e..8d5a1b6 100644 --- a/cmd/server/tests/comic_metadata_test.go +++ b/cmd/server/tests/comic_metadata_test.go @@ -6,6 +6,7 @@ import ( "bookhoard/internal/database" + "github.com/google/uuid" "github.com/jackc/pgx/v5/pgtype" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -17,12 +18,12 @@ func TestComicMetadataExtraction(t *testing.T) { defer setup.Server.Close() ctx := context.Background() - libraryID := setup.CreateLibrary(t, "Comic Test Library", "comic") + libraryID := setup.CreateLibrary(t, "Comic Test Library", "comics") t.Run("CBZ with RTL manga", func(t *testing.T) { // Insert test media item with full comic metadata _, err := setup.DB.CreateMediaItem(ctx, database.CreateMediaItemParams{ - LibraryID: libraryID, + LibraryID: pgtype.UUID{Bytes: [16]byte(uuid.MustParse(libraryID)), Valid: true}, Title: "Test Manga", FilePath: "/test/manga.cbz", MangaType: pgtype.Text{String: "yes_and_right_to_left", Valid: true}, @@ -39,7 +40,7 @@ func TestComicMetadataExtraction(t *testing.T) { // Query it back items, err := setup.DB.SearchMediaItems(ctx, database.SearchMediaItemsParams{ - LibraryID: libraryID, + LibraryID: pgtype.UUID{Bytes: [16]byte(uuid.MustParse(libraryID)), Valid: true}, }) require.NoError(t, err) require.Greater(t, len(items), 0) @@ -59,7 +60,7 @@ func TestComicMetadataExtraction(t *testing.T) { t.Run("CBZ with Western comic", func(t *testing.T) { _, err := setup.DB.CreateMediaItem(ctx, database.CreateMediaItemParams{ - LibraryID: libraryID, + LibraryID: pgtype.UUID{Bytes: [16]byte(uuid.MustParse(libraryID)), Valid: true}, Title: "Test Comic", FilePath: "/test/comic.cbz", MangaType: pgtype.Text{String: "no", Valid: true}, @@ -72,7 +73,7 @@ func TestComicMetadataExtraction(t *testing.T) { require.NoError(t, err) items, err := setup.DB.SearchMediaItems(ctx, database.SearchMediaItemsParams{ - LibraryID: libraryID, + LibraryID: pgtype.UUID{Bytes: [16]byte(uuid.MustParse(libraryID)), Valid: true}, }) require.NoError(t, err) @@ -87,7 +88,7 @@ func TestComicMetadataExtraction(t *testing.T) { t.Run("Comic with minimal metadata", func(t *testing.T) { _, err := setup.DB.CreateMediaItem(ctx, database.CreateMediaItemParams{ - LibraryID: libraryID, + LibraryID: pgtype.UUID{Bytes: [16]byte(uuid.MustParse(libraryID)), Valid: true}, Title: "Minimal Comic", FilePath: "/test/minimal.cbz", // Only required fields - comic metadata should default appropriately @@ -95,7 +96,7 @@ func TestComicMetadataExtraction(t *testing.T) { require.NoError(t, err) items, err := setup.DB.SearchMediaItems(ctx, database.SearchMediaItemsParams{ - LibraryID: libraryID, + LibraryID: pgtype.UUID{Bytes: [16]byte(uuid.MustParse(libraryID)), Valid: true}, }) require.NoError(t, err) @@ -113,7 +114,7 @@ func TestReadingDirectionAPI(t *testing.T) { defer setup.Server.Close() ctx := context.Background() - libraryID := setup.CreateLibrary(t, "Reading Direction Test Library", "comic") + libraryID := setup.CreateLibrary(t, "Reading Direction Test Library", "comics") // Create test items with different reading directions testCases := []struct { @@ -128,7 +129,7 @@ func TestReadingDirectionAPI(t *testing.T) { for _, tc := range testCases { _, err := setup.DB.CreateMediaItem(ctx, database.CreateMediaItemParams{ - LibraryID: libraryID, + LibraryID: pgtype.UUID{Bytes: [16]byte(uuid.MustParse(libraryID)), Valid: true}, Title: tc.title, FilePath: "/test/" + tc.title + ".cbz", MangaType: pgtype.Text{String: tc.manga, Valid: true}, @@ -139,7 +140,7 @@ func TestReadingDirectionAPI(t *testing.T) { t.Run("Search API includes reading_direction", func(t *testing.T) { items, err := setup.DB.SearchMediaItems(ctx, database.SearchMediaItemsParams{ - LibraryID: libraryID, + LibraryID: pgtype.UUID{Bytes: [16]byte(uuid.MustParse(libraryID)), Valid: true}, }) require.NoError(t, err) require.Len(t, items, 3) @@ -155,7 +156,7 @@ func TestReadingDirectionAPI(t *testing.T) { t.Run("Filter by reading_direction - RTL only", func(t *testing.T) { // Query all items and filter in-memory items, err := setup.DB.SearchMediaItems(ctx, database.SearchMediaItemsParams{ - LibraryID: libraryID, + LibraryID: pgtype.UUID{Bytes: [16]byte(uuid.MustParse(libraryID)), Valid: true}, }) require.NoError(t, err) @@ -171,7 +172,7 @@ func TestReadingDirectionAPI(t *testing.T) { t.Run("Verify all reading directions present", func(t *testing.T) { items, err := setup.DB.SearchMediaItems(ctx, database.SearchMediaItemsParams{ - LibraryID: libraryID, + LibraryID: pgtype.UUID{Bytes: [16]byte(uuid.MustParse(libraryID)), Valid: true}, }) require.NoError(t, err) @@ -194,25 +195,25 @@ func TestUniversalMetadataFields(t *testing.T) { ctx := context.Background() // Test with both comic and ebook libraries - comicLibraryID := setup.CreateLibrary(t, "Comic Library", "comic") - ebookLibraryID := setup.CreateLibrary(t, "Ebook Library", "ebook") + comicLibraryID := setup.CreateLibrary(t, "Comic Library", "comics") + ebookLibraryID := setup.CreateLibrary(t, "Ebook Library", "ebooks") t.Run("Comic with universal fields", func(t *testing.T) { _, err := setup.DB.CreateMediaItem(ctx, database.CreateMediaItemParams{ - LibraryID: comicLibraryID, + LibraryID: pgtype.UUID{Bytes: [16]byte(uuid.MustParse(comicLibraryID)), Valid: true}, Title: "Comic with Universal Metadata", FilePath: "/test/comic.cbz", SeriesCount: pgtype.Int4{Int32: 10, Valid: true}, Volume: pgtype.Int4{Int32: 2, Valid: true}, Imprint: pgtype.Text{String: "DC Black Label", Valid: true}, AgeRating: pgtype.Text{String: "Mature", Valid: true}, - WebURL: pgtype.Text{String: "https://example.com/comic", Valid: true}, + WebUrl: pgtype.Text{String: "https://example.com/comic", Valid: true}, CommunityRating: pgtype.Float8{Float64: 9.2, Valid: true}, }) require.NoError(t, err) items, err := setup.DB.SearchMediaItems(ctx, database.SearchMediaItemsParams{ - LibraryID: comicLibraryID, + LibraryID: pgtype.UUID{Bytes: [16]byte(uuid.MustParse(comicLibraryID)), Valid: true}, }) require.NoError(t, err) @@ -227,20 +228,20 @@ func TestUniversalMetadataFields(t *testing.T) { t.Run("Ebook with universal fields", func(t *testing.T) { _, err := setup.DB.CreateMediaItem(ctx, database.CreateMediaItemParams{ - LibraryID: ebookLibraryID, + LibraryID: pgtype.UUID{Bytes: [16]byte(uuid.MustParse(ebookLibraryID)), Valid: true}, Title: "Ebook with Universal Metadata", FilePath: "/test/book.epub", SeriesCount: pgtype.Int4{Int32: 7, Valid: true}, Volume: pgtype.Int4{Int32: 1, Valid: true}, Imprint: pgtype.Text{String: "HarperCollins", Valid: true}, AgeRating: pgtype.Text{String: "Everyone", Valid: true}, - WebURL: pgtype.Text{String: "https://example.com/book", Valid: true}, + WebUrl: pgtype.Text{String: "https://example.com/book", Valid: true}, CommunityRating: pgtype.Float8{Float64: 4.5, Valid: true}, }) require.NoError(t, err) items, err := setup.DB.SearchMediaItems(ctx, database.SearchMediaItemsParams{ - LibraryID: ebookLibraryID, + LibraryID: pgtype.UUID{Bytes: [16]byte(uuid.MustParse(ebookLibraryID)), Valid: true}, }) require.NoError(t, err) @@ -260,13 +261,13 @@ func TestComicSpecificFields(t *testing.T) { defer setup.Server.Close() ctx := context.Background() - libraryID := setup.CreateLibrary(t, "Comic Library", "comic") + libraryID := setup.CreateLibrary(t, "Comic Library", "comics") t.Run("Alternate series info as JSONB", func(t *testing.T) { alternateInfo := `{"alternate_series":"Ultimate X-Men","alternate_number":1,"alternate_count":12}` _, err := setup.DB.CreateMediaItem(ctx, database.CreateMediaItemParams{ - LibraryID: libraryID, + LibraryID: pgtype.UUID{Bytes: [16]byte(uuid.MustParse(libraryID)), Valid: true}, Title: "X-Men with Alternate Series", FilePath: "/test/xmen.cbz", AlternateInfo: []byte(alternateInfo), @@ -277,7 +278,7 @@ func TestComicSpecificFields(t *testing.T) { require.NoError(t, err) items, err := setup.DB.SearchMediaItems(ctx, database.SearchMediaItemsParams{ - LibraryID: libraryID, + LibraryID: pgtype.UUID{Bytes: [16]byte(uuid.MustParse(libraryID)), Valid: true}, }) require.NoError(t, err) diff --git a/internal/handlers/media.go b/internal/handlers/media.go index 31ab72d..cf01de5 100644 --- a/internal/handlers/media.go +++ b/internal/handlers/media.go @@ -8,6 +8,7 @@ import ( "encoding/json" "fmt" "io" + "log" "mime" "net/http" "net/url" @@ -444,7 +445,11 @@ func (h *MediaHandler) HandleBulkDelete(c *echo.Context) error { } if media.FilePath != "" { - os.Remove(media.FilePath) + if err := os.Remove(media.FilePath); err != nil { + // Log file removal failure but don't fail the request + // DB record is already deleted, which is the primary concern + log.Printf("Warning: failed to remove file %s: %v", media.FilePath, err) + } } results = append(results, map[string]interface{}{ @@ -1644,12 +1649,9 @@ func (mh *MediaHandler) ServeFile(c *echo.Context) error { } // numericToFloat converts pgtype.Numeric to float64, returning 0 if invalid -func numericToFloat(n pgtype.Numeric) float64 { +func numericToFloat(n pgtype.Float8) float64 { if n.Valid { - f, err := n.Float64Value() - if err == nil { - return f.Float64 - } + return n.Float64 } return 0 }