package main import ( "context" "fmt" "io" "net/http" "strings" "testing" "bookhoard/internal/database" "github.com/google/uuid" "github.com/jackc/pgx/v5/pgtype" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // TestComicMetadataDisplay_ComicDetailPage tests that comic metadata fields display correctly on the book detail page func TestComicMetadataDisplay_ComicDetailPage(t *testing.T) { setup := setupTestServer(t) client := &http.Client{} ctx := context.Background() // Create a test comics library createLibrary := func(t *testing.T, name, libraryType string) uuid.UUID { t.Helper() // Get the library_type_id for the specified type libraryTypeRow, err := setup.DB.GetLibraryTypeByName(ctx, libraryType) require.NoError(t, err, "Should find library type") adminUUID := getTestUserID(t, setup.DB) library, err := setup.DB.CreateLibrary(ctx, database.CreateLibraryParams{ Name: name, Description: pgtype.Text{String: "Test library description", Valid: true}, LibraryTypeID: libraryTypeRow.ID, CreatedByAdminID: pgtype.UUID{Bytes: adminUUID, Valid: true}, }) require.NoError(t, err, "Should create library") libraryUUID, err := uuid.FromBytes(library.ID.Bytes[0:16]) require.NoError(t, err, "Should parse library UUID") t.Cleanup(func() { setup.DB.DeleteLibrary(ctx, library.ID) }) return libraryUUID } libraryID := createLibrary(t, "Comic Metadata Display Test Library", "comics") parsedLibraryID := libraryID // Helper function to create a test media item with specific metadata createComicMediaItem := func(t *testing.T, title string, metadata map[string]interface{}) string { t.Helper() // Build media item params params := database.CreateMediaItemParams{ LibraryID: pgtype.UUID{Bytes: parsedLibraryID, Valid: true}, Title: title, FilePath: "/test/" + title + ".cbz", FileSize: pgtype.Int8{Int64: 1024000, Valid: true}, MimeType: pgtype.Text{String: "application/x-cbz", Valid: true}, MangaType: pgtype.Text{String: "unknown", Valid: true}, ReadingDirection: pgtype.Text{String: "auto", Valid: true}, } // Apply metadata overrides if v, ok := metadata["manga_type"].(string); ok { params.MangaType = pgtype.Text{String: v, Valid: true} } if v, ok := metadata["reading_direction"].(string); ok { params.ReadingDirection = pgtype.Text{String: v, Valid: true} } if v, ok := metadata["series_count"].(int32); ok { params.SeriesCount = pgtype.Int4{Int32: v, Valid: true} } if v, ok := metadata["volume"].(int32); ok { params.Volume = pgtype.Int4{Int32: v, Valid: true} } if v, ok := metadata["imprint"].(string); ok { params.Imprint = pgtype.Text{String: v, Valid: true} } if v, ok := metadata["age_rating"].(string); ok { params.AgeRating = pgtype.Text{String: v, Valid: true} } if v, ok := metadata["web_url"].(string); ok { params.WebUrl = pgtype.Text{String: v, Valid: true} } if v, ok := metadata["community_rating"].(float64); ok { params.CommunityRating = pgtype.Float8{Float64: v, Valid: true} } if v, ok := metadata["story_arc"].(string); ok { params.StoryArc = pgtype.Text{String: v, Valid: true} } if v, ok := metadata["is_black_and_white"].(bool); ok { params.IsBlackAndWhite = pgtype.Bool{Bool: v, Valid: true} } if v, ok := metadata["scan_information"].(string); ok { params.ScanInformation = pgtype.Text{String: v, Valid: true} } if v, ok := metadata["summary"].(string); ok { params.Summary = pgtype.Text{String: v, Valid: true} } if v, ok := metadata["metadata_notes"].(string); ok { params.MetadataNotes = pgtype.Text{String: v, Valid: true} } if v, ok := metadata["alternate_info"].(string); ok { params.AlternateInfo = []byte(v) } mediaItem, err := setup.DB.CreateMediaItem(ctx, params) require.NoError(t, err, "Should create media item") mediaUUID, err := uuid.FromBytes(mediaItem.ID.Bytes[0:16]) require.NoError(t, err, "Should parse media UUID") return mediaUUID.String() } t.Run("Step 1: Reading Direction Badge - RTL Manga", func(t *testing.T) { mediaUUID := createComicMediaItem(t, "RTL Manga Test", map[string]interface{}{ "manga_type": "yes_and_right_to_left", "reading_direction": "rtl", }) req, _ := http.NewRequest("GET", setup.Server.URL+"/media/"+mediaUUID, nil) req.Header.Set("Authorization", "Bearer "+setup.Token) resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) body := readBody(resp) // Verify reading direction badge is displayed assert.Contains(t, body, "📖", "Should display reading direction icon") assert.Contains(t, body, "RTL", "Should display RTL reading direction") }) t.Run("Step 1: Reading Direction Badge - Western Comic (LTR)", func(t *testing.T) { mediaUUID := createComicMediaItem(t, "Western Comic Test", map[string]interface{}{ "manga_type": "no", "reading_direction": "ltr", }) req, _ := http.NewRequest("GET", setup.Server.URL+"/media/"+mediaUUID, nil) req.Header.Set("Authorization", "Bearer "+setup.Token) resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) body := readBody(resp) assert.Contains(t, body, "LTR", "Should display LTR reading direction") }) t.Run("Step 1: Reading Direction Badge - Hidden when auto", func(t *testing.T) { mediaUUID := createComicMediaItem(t, "Auto Direction Test", map[string]interface{}{ "reading_direction": "auto", }) req, _ := http.NewRequest("GET", setup.Server.URL+"/media/"+mediaUUID, nil) req.Header.Set("Authorization", "Bearer "+setup.Token) resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) body := readBody(resp) // When reading direction is "auto", the badge should not be displayed // Check for specific badge text instead of emoji (which appears elsewhere) assert.NotContains(t, body, "RTL", "Should not display RTL badge when auto") assert.NotContains(t, body, "LTR", "Should not display LTR badge when auto") assert.NotContains(t, body, "VERTICAL", "Should not display VERTICAL badge when auto") }) t.Run("Step 2: Community Rating Display", func(t *testing.T) { mediaUUID := createComicMediaItem(t, "Community Rating Test", map[string]interface{}{ "community_rating": 8.5, }) req, _ := http.NewRequest("GET", setup.Server.URL+"/media/"+mediaUUID, nil) req.Header.Set("Authorization", "Bearer "+setup.Token) resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) body := readBody(resp) // Verify community rating is displayed assert.Contains(t, body, "Community Rating", "Should display community rating label") assert.Contains(t, body, "8.5 / 10", "Should display formatted rating out of 10") assert.Contains(t, body, "★", "Should render star rating") }) t.Run("Step 3: Comic-Specific Badges - Age Rating", func(t *testing.T) { mediaUUID := createComicMediaItem(t, "Age Rating Test", map[string]interface{}{ "age_rating": "Teen", }) req, _ := http.NewRequest("GET", setup.Server.URL+"/media/"+mediaUUID, nil) req.Header.Set("Authorization", "Bearer "+setup.Token) resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) body := readBody(resp) assert.Contains(t, body, "Teen", "Should display age rating badge") }) t.Run("Step 3: Comic-Specific Badges - Black and White", func(t *testing.T) { mediaUUID := createComicMediaItem(t, "B&W Test", map[string]interface{}{ "is_black_and_white": true, }) req, _ := http.NewRequest("GET", setup.Server.URL+"/media/"+mediaUUID, nil) req.Header.Set("Authorization", "Bearer "+setup.Token) resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) body := readBody(resp) assert.Contains(t, body, "B&W", "Should display black and white badge") }) t.Run("Step 3: Comic-Specific Badges - Story Arc", func(t *testing.T) { mediaUUID := createComicMediaItem(t, "Story Arc Test", map[string]interface{}{ "story_arc": "The Dark Phoenix Saga", }) req, _ := http.NewRequest("GET", setup.Server.URL+"/media/"+mediaUUID, nil) req.Header.Set("Authorization", "Bearer "+setup.Token) resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) body := readBody(resp) assert.Contains(t, body, "📚", "Should display story arc icon") assert.Contains(t, body, "The Dark Phoenix Saga", "Should display story arc name") }) t.Run("Step 4: Universal Series Info - Series Count", func(t *testing.T) { mediaUUID := createComicMediaItem(t, "Series Count Test", map[string]interface{}{ "series_count": int32(12), }) req, _ := http.NewRequest("GET", setup.Server.URL+"/media/"+mediaUUID, nil) req.Header.Set("Authorization", "Bearer "+setup.Token) resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) body := readBody(resp) assert.Contains(t, body, "Series Count", "Should display series count label") assert.Contains(t, body, "12 items", "Should display series count") }) t.Run("Step 4: Universal Series Info - Volume", func(t *testing.T) { mediaUUID := createComicMediaItem(t, "Volume Test", map[string]interface{}{ "volume": int32(3), }) req, _ := http.NewRequest("GET", setup.Server.URL+"/media/"+mediaUUID, nil) req.Header.Set("Authorization", "Bearer "+setup.Token) resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) body := readBody(resp) assert.Contains(t, body, "Volume", "Should display volume label") assert.Contains(t, body, "Vol. 3", "Should display volume number") }) t.Run("Step 4: Universal Series Info - Imprint", func(t *testing.T) { mediaUUID := createComicMediaItem(t, "Imprint Test", map[string]interface{}{ "imprint": "Vertigo", }) req, _ := http.NewRequest("GET", setup.Server.URL+"/media/"+mediaUUID, nil) req.Header.Set("Authorization", "Bearer "+setup.Token) resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) body := readBody(resp) assert.Contains(t, body, "Imprint", "Should display imprint label") assert.Contains(t, body, "Vertigo", "Should display imprint name") }) t.Run("Step 5: Comic-Specific Metadata - Manga Type", func(t *testing.T) { mediaUUID := createComicMediaItem(t, "Manga Type Test", map[string]interface{}{ "manga_type": "yes_and_right_to_left", }) req, _ := http.NewRequest("GET", setup.Server.URL+"/media/"+mediaUUID, nil) req.Header.Set("Authorization", "Bearer "+setup.Token) resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) body := readBody(resp) assert.Contains(t, body, "Manga Type", "Should display manga type label") assert.Contains(t, body, "yes and right to left", "Should display formatted manga type") }) t.Run("Step 5: Comic-Specific Metadata - Scan Information", func(t *testing.T) { mediaUUID := createComicMediaItem(t, "Scan Info Test", map[string]interface{}{ "scan_information": "Scanned by Minutemen", }) req, _ := http.NewRequest("GET", setup.Server.URL+"/media/"+mediaUUID, nil) req.Header.Set("Authorization", "Bearer "+setup.Token) resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) body := readBody(resp) assert.Contains(t, body, "Scan Info", "Should display scan info label") assert.Contains(t, body, "Scanned by Minutemen", "Should display scan information") }) t.Run("Step 5: Comic-Specific Metadata - Alternate Series", func(t *testing.T) { alternateInfo := `{"alternate_series":"Ultimate X-Men","alternate_number":1,"alternate_count":12}` mediaUUID := createComicMediaItem(t, "Alternate Series Test", map[string]interface{}{ "alternate_info": alternateInfo, }) req, _ := http.NewRequest("GET", setup.Server.URL+"/media/"+mediaUUID, nil) req.Header.Set("Authorization", "Bearer "+setup.Token) resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) body := readBody(resp) assert.Contains(t, body, "Alternate Series", "Should display alternate series label") assert.Contains(t, body, "Ultimate X-Men", "Should display alternate series name") }) t.Run("Step 6: Summary Section - Different from description", func(t *testing.T) { mediaUUID := createComicMediaItem(t, "Summary Test", map[string]interface{}{ "summary": "Professor X creates mutant team", }) req, _ := http.NewRequest("GET", setup.Server.URL+"/media/"+mediaUUID, nil) req.Header.Set("Authorization", "Bearer "+setup.Token) resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) body := readBody(resp) assert.Contains(t, body, "Comic Summary", "Should display comic summary section") assert.Contains(t, body, "Professor X creates mutant team", "Should display summary content") }) t.Run("Step 7: Metadata Notes Section", func(t *testing.T) { mediaUUID := createComicMediaItem(t, "Metadata Notes Test", map[string]interface{}{ "metadata_notes": "From collection: John's Comics", }) req, _ := http.NewRequest("GET", setup.Server.URL+"/media/"+mediaUUID, nil) req.Header.Set("Authorization", "Bearer "+setup.Token) resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) body := readBody(resp) // DEBUG: Print the actual HTML to see what we're dealing with t.Logf("HTML body length: %d bytes", len(body)) // Find the Metadata Notes section in HTML startIdx := strings.Index(body, "Metadata Notes") if startIdx > 0 { // Print 500 characters around the Metadata Notes section endIdx := startIdx + 500 if endIdx > len(body) { endIdx = len(body) } if startIdx > 250 { t.Logf("Metadata Notes section (context):\n%s", body[startIdx-250:startIdx+250]) } else { t.Logf("Metadata Notes section (context):\n%s", body[0:endIdx]) } } else { t.Logf("WARNING: 'Metadata Notes' string not found in HTML") // Print first 1000 chars to see what we got if len(body) > 1000 { t.Logf("First 1000 chars of HTML:\n%s", body[0:1000]) } else { t.Logf("Full HTML:\n%s", body) } } // Check that the metadata notes section is present assert.Contains(t, body, "Metadata Notes", "Should display metadata notes section header") // The content is HTML-escaped by templ, so apostrophes become ' assert.Contains(t, body, "John's Comics", "Should display HTML-escaped metadata notes content") // Also check for a simpler substring without special characters assert.Contains(t, body, "collection", "Should display metadata notes content") }) t.Run("Step 8: Web URL Link - External Link", func(t *testing.T) { mediaUUID := createComicMediaItem(t, "Web URL Test", map[string]interface{}{ "web_url": "https://comicvine.gamespot.com/batman-year-one/1", "isbn": "9781234567890", }) req, _ := http.NewRequest("GET", setup.Server.URL+"/media/"+mediaUUID, nil) req.Header.Set("Authorization", "Bearer "+setup.Token) resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) body := readBody(resp) assert.Contains(t, body, "comicvine.gamespot.com", "Should display web URL domain") assert.Contains(t, body, "https://comicvine.gamespot.com/batman-year-one/1", "Should include full URL in href") assert.Contains(t, body, "target=\"_blank\"", "Should open in new tab") assert.Contains(t, body, "rel=\"noopener noreferrer\"", "Should have security attributes") }) t.Run("Test Case 1: Japanese Manga - Complete Display", func(t *testing.T) { alternateInfo := `{"alternate_series":"Ultimate X-Men","alternate_number":1,"alternate_count":12}` mediaUUID := createComicMediaItem(t, "Japanese Manga Complete", map[string]interface{}{ "manga_type": "yes_and_right_to_left", "reading_direction": "rtl", "series_count": int32(12), "age_rating": "Teen", "community_rating": 8.5, "story_arc": "The Dark Phoenix Saga", "alternate_info": alternateInfo, "scan_information": "Scanned by Minutemen", "imprint": "Shonen Jump", "is_black_and_white": false, }) req, _ := http.NewRequest("GET", setup.Server.URL+"/media/"+mediaUUID, nil) req.Header.Set("Authorization", "Bearer "+setup.Token) resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) body := readBody(resp) // Verify all manga-specific fields are displayed assert.Contains(t, body, "RTL", "Should display RTL reading direction") assert.Contains(t, body, "Teen", "Should display age rating") assert.Contains(t, body, "8.5 / 10", "Should display community rating") assert.Contains(t, body, "The Dark Phoenix Saga", "Should display story arc") assert.Contains(t, body, "Ultimate X-Men", "Should display alternate series") assert.Contains(t, body, "Shonen Jump", "Should display imprint") }) t.Run("Test Case 2: Western Comic - Complete Display", func(t *testing.T) { mediaUUID := createComicMediaItem(t, "Western Comic Complete", map[string]interface{}{ "manga_type": "no", "reading_direction": "ltr", "age_rating": "Mature", "community_rating": 9.2, "story_arc": "Batman: Year One", "series_count": int32(4), "volume": int32(1), "imprint": "Vertigo", "is_black_and_white": true, }) req, _ := http.NewRequest("GET", setup.Server.URL+"/media/"+mediaUUID, nil) req.Header.Set("Authorization", "Bearer "+setup.Token) resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) body := readBody(resp) // Verify Western comic fields assert.Contains(t, body, "LTR", "Should display LTR reading direction") assert.Contains(t, body, "Mature", "Should display age rating") assert.Contains(t, body, "B&W", "Should display black and white badge") assert.Contains(t, body, "Batman: Year One", "Should display story arc") assert.Contains(t, body, "Vol. 1", "Should display volume") assert.Contains(t, body, "Vertigo", "Should display imprint") }) t.Run("Test Case 3: Webtoon/Manhwa - Complete Display", func(t *testing.T) { mediaUUID := createComicMediaItem(t, "Webtoon Complete", map[string]interface{}{ "manga_type": "unknown", "reading_direction": "vertical", "age_rating": "Teen", "community_rating": 7.8, }) req, _ := http.NewRequest("GET", setup.Server.URL+"/media/"+mediaUUID, nil) req.Header.Set("Authorization", "Bearer "+setup.Token) resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) body := readBody(resp) // Verify webtoon fields assert.Contains(t, body, "VERTICAL", "Should display vertical reading direction") assert.Contains(t, body, "Teen", "Should display age rating") }) t.Run("Test Case 4: Regular Ebook - No Comic Metadata", func(t *testing.T) { // Create ebook library for this test ebookLibraryID := createLibrary(t, "Ebook Test Library", "ebooks") parsedEbookLibraryID := ebookLibraryID // Create ebook with only universal metadata params := database.CreateMediaItemParams{ LibraryID: pgtype.UUID{Bytes: parsedEbookLibraryID, Valid: true}, Title: "Regular Ebook", FilePath: "/test/book.epub", FileSize: pgtype.Int8{Int64: 512000, Valid: true}, MimeType: pgtype.Text{String: "application/epub+zip", Valid: true}, 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://goodreads.com/book", Valid: true}, CommunityRating: pgtype.Float8{Float64: 4.5, Valid: true}, Isbn: pgtype.Text{String: "9781234567890", Valid: true}, } mediaItem, err := setup.DB.CreateMediaItem(ctx, params) require.NoError(t, err, "Should create ebook") mediaUUID, err := uuid.FromBytes(mediaItem.ID.Bytes[0:16]) require.NoError(t, err, "Should parse media UUID") req, _ := http.NewRequest("GET", setup.Server.URL+"/media/"+mediaUUID.String(), nil) req.Header.Set("Authorization", "Bearer "+setup.Token) resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) body := readBody(resp) // Verify universal fields are displayed assert.Contains(t, body, "7 items", "Should display series count") assert.Contains(t, body, "Vol. 1", "Should display volume") assert.Contains(t, body, "HarperCollins", "Should display imprint") assert.Contains(t, body, "Everyone", "Should display age rating") assert.Contains(t, body, "goodreads.com", "Should display web URL") // Verify comic-specific fields are NOT displayed assert.NotContains(t, body, "Manga Type", "Should not display manga type for ebook") assert.NotContains(t, body, "Scan Info", "Should not display scan info for ebook") assert.NotContains(t, body, "B&W", "Should not display B&W badge for ebook") }) t.Run("Authentication Required", func(t *testing.T) { mediaUUID := createComicMediaItem(t, "Auth Test", map[string]interface{}{ "age_rating": "Teen", }) // Test without authentication req, _ := http.NewRequest("GET", setup.Server.URL+"/media/"+mediaUUID, nil) resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusUnauthorized, resp.StatusCode, "Should require authentication") }) t.Run("Regular User Can View Metadata", func(t *testing.T) { mediaUUID := createComicMediaItem(t, "User View Test", map[string]interface{}{ "manga_type": "yes_and_right_to_left", "reading_direction": "rtl", "age_rating": "Teen", }) req, _ := http.NewRequest("GET", setup.Server.URL+"/media/"+mediaUUID, nil) req.Header.Set("Authorization", "Bearer "+setup.RegularToken) resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) body := readBody(resp) // Regular users should see all metadata assert.Contains(t, body, "RTL", "Regular user should see reading direction") assert.Contains(t, body, "Teen", "Regular user should see age rating") }) t.Run("Minimal Metadata - Only Required Fields", func(t *testing.T) { mediaUUID := createComicMediaItem(t, "Minimal Comic", map[string]interface{}{ // No optional metadata set }) req, _ := http.NewRequest("GET", setup.Server.URL+"/media/"+mediaUUID, nil) req.Header.Set("Authorization", "Bearer "+setup.Token) resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) body := readBody(resp) // Page should still render successfully assert.Contains(t, body, "Minimal Comic", "Should display title") // Use "Community Rating:" with colon to avoid matching the HTML comment assert.NotContains(t, body, "Community Rating:", "Should not show community rating when not set") assert.NotContains(t, body, "Manga Type", "Should not show manga type when unknown") }) } // readBody is a helper to read response body func readBody(resp *http.Response) string { body, _ := io.ReadAll(resp.Body) return string(body) } // TestComicMetadataDisplay_AllFieldsTogether tests all metadata fields together func TestComicMetadataDisplay_AllFieldsTogether(t *testing.T) { setup := setupTestServer(t) client := &http.Client{} ctx := context.Background() var err error // Helper function to create library createLibrary := func(t *testing.T, name, libraryType string) uuid.UUID { t.Helper() libraryTypeRow, err := setup.DB.GetLibraryTypeByName(ctx, libraryType) require.NoError(t, err, "Should find library type") adminUUID := getTestUserID(t, setup.DB) library, err := setup.DB.CreateLibrary(ctx, database.CreateLibraryParams{ Name: name, Description: pgtype.Text{String: "Test library description", Valid: true}, LibraryTypeID: libraryTypeRow.ID, CreatedByAdminID: pgtype.UUID{Bytes: adminUUID, Valid: true}, }) require.NoError(t, err, "Should create library") libraryUUID, err := uuid.FromBytes(library.ID.Bytes[0:16]) require.NoError(t, err, "Should parse library UUID") t.Cleanup(func() { setup.DB.DeleteLibrary(ctx, library.ID) }) return libraryUUID } libraryID := createLibrary(t, "Complete Metadata Test Library", "comics") parsedLibraryID := libraryID // Create a media item with ALL metadata fields populated alternateInfoStr := `{"alternate_series":"Ultimate X-Men","alternate_number":1,"alternate_count":12}` var mediaItem database.MediaItems mediaItem, err = setup.DB.CreateMediaItem(ctx, database.CreateMediaItemParams{ LibraryID: pgtype.UUID{Bytes: parsedLibraryID, Valid: true}, Title: "Complete Metadata Comic", FilePath: "/test/complete.cbz", FileSize: pgtype.Int8{Int64: 2048000, Valid: true}, MimeType: pgtype.Text{String: "application/x-cbz", Valid: true}, MangaType: pgtype.Text{String: "yes_and_right_to_left", Valid: true}, ReadingDirection: pgtype.Text{String: "rtl", Valid: true}, SeriesCount: pgtype.Int4{Int32: 12, Valid: true}, Volume: pgtype.Int4{Int32: 1, Valid: true}, Imprint: pgtype.Text{String: "Marvel", Valid: true}, AgeRating: pgtype.Text{String: "Teen", Valid: true}, WebUrl: pgtype.Text{String: "https://marvel.com/comics/ultimate-xmen", Valid: true}, CommunityRating: pgtype.Float8{Float64: 8.7, Valid: true}, StoryArc: pgtype.Text{String: "The Dark Phoenix Saga", Valid: true}, IsBlackAndWhite: pgtype.Bool{Bool: false, Valid: true}, ScanInformation: pgtype.Text{String: "Scanned by Minutemen-HDC", Valid: true}, Summary: pgtype.Text{String: "Professor X creates mutant team to protect humanity", Valid: true}, MetadataNotes: pgtype.Text{String: "From personal collection", Valid: true}, AlternateInfo: []byte(alternateInfoStr), }) require.NoError(t, err) mediaUUID, err := uuid.FromBytes(mediaItem.ID.Bytes[0:16]) require.NoError(t, err) req, _ := http.NewRequest("GET", setup.Server.URL+"/media/"+mediaUUID.String(), nil) req.Header.Set("Authorization", "Bearer "+setup.Token) resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) body := readBody(resp) // Verify all fields are present in the rendered page expectedFields := []struct { field string expected string }{ {"Reading Direction", "RTL"}, {"Community Rating", "8.7 / 10"}, {"Age Rating", "Teen"}, {"Story Arc", "The Dark Phoenix Saga"}, {"Series Count", "12 items"}, {"Volume", "Vol. 1"}, {"Imprint", "Marvel"}, {"Manga Type", "yes and right to left"}, {"Scan Info", "Scanned by Minutemen-HDC"}, {"Alternate Series", "Ultimate X-Men"}, {"Comic Summary", "Professor X creates mutant team"}, {"Metadata Notes", "From personal collection"}, {"Web URL", "marvel.com"}, } for _, field := range expectedFields { assert.Contains(t, body, field.expected, fmt.Sprintf("Should display %s", field.field)) } }