package services import ( "archive/zip" "bytes" "image" "image/jpeg" "os" "path/filepath" "testing" "time" ) // createTestJPEGBytes returns the bytes of a minimal valid JPEG. func createTestJPEGBytes() string { var buf bytes.Buffer img := image.NewRGBA(image.Rect(0, 0, 1, 1)) if err := jpeg.Encode(&buf, img, nil); err != nil { return "" } return buf.String() } // createPragmaticStyleEPUB builds an EPUB modeled on Pragmatic Bookshelf // output: the dc namespace declared on the element (not on // ), a scheme-less ISBN identifier, an OPF-declared cover, and a // deliberately malformed chapter body. The malformed chapter is the // regression trigger: the previous go-epub-based extractor failed the whole // book when any chapter was unparseable and wrote blank metadata. func createPragmaticStyleEPUB(epubPath string) error { file, err := os.Create(epubPath) if err != nil { return err } defer file.Close() zipWriter := zip.NewWriter(file) defer zipWriter.Close() mimetypeW, err := zipWriter.CreateHeader(&zip.FileHeader{ Name: "mimetype", Method: zip.Store, }) if err != nil { return err } mimetypeW.Write([]byte("application/epub+zip")) files := map[string]string{ "META-INF/container.xml": ``, // dc namespace declared on ; identifiers carry no scheme attr "OEBPS/content.opf": ` en A Common-Sense Guide Jay Wengrow The Pragmatic Bookshelf, LLC Content that makes you a better programmer. Programming 978-1-68050-722-8 `, // Malformed on purpose: unclosed tags "OEBPS/ch1.xhtml": `

unclosed paragraph`, "OEBPS/images/cover.jpg": createTestJPEGBytes(), } for name, content := range files { w, err := zipWriter.Create(name) if err != nil { return err } if _, err := w.Write([]byte(content)); err != nil { return err } } return zipWriter.Close() } // TestExtractEPUBMetadataBrokenChapter guards the regression where one // unparseable chapter made the extractor return nothing at all: metadata must // come from the OPF regardless of chapter-body damage. func TestExtractEPUBMetadataBrokenChapter(t *testing.T) { tmpDir := t.TempDir() epubPath := filepath.Join(tmpDir, "book.epub") if err := createPragmaticStyleEPUB(epubPath); err != nil { t.Fatalf("failed to create test EPUB: %v", err) } s := NewMediaScanner(nil) metadata, err := s.extractEPUBMetadata(epubPath) if err != nil { t.Fatalf("extractEPUBMetadata() error: %v", err) } if metadata.Title != "A Common-Sense Guide" { t.Errorf("Title = %q, want %q", metadata.Title, "A Common-Sense Guide") } if metadata.Author != "Jay Wengrow" { t.Errorf("Author = %q, want %q", metadata.Author, "Jay Wengrow") } if metadata.Publisher != "The Pragmatic Bookshelf, LLC" { t.Errorf("Publisher = %q, want %q", metadata.Publisher, "The Pragmatic Bookshelf, LLC") } if metadata.Description == "" { t.Error("Description missing") } if metadata.Language != "en" { t.Errorf("Language = %q, want %q", metadata.Language, "en") } // Scheme-less identifier that normalizes to a valid ISBN must be picked up if metadata.ISBN == "" { t.Error("ISBN missing (scheme-less dc:identifier fallback failed)") } } func TestParseOPFContentCalibreSeries(t *testing.T) { opf := ` Test Book Some Author 2020-03-15 Fiction Classic 978-3-16-148410-0 ` metadata, err := parseOPFContent([]byte(opf)) if err != nil { t.Fatalf("parseOPFContent() error: %v", err) } if metadata.Series != "Great Series" || metadata.SeriesNumber != 2 { t.Errorf("Series = %q/%d, want Great Series/2", metadata.Series, metadata.SeriesNumber) } if metadata.ISBN == "" { t.Error("schemed ISBN not extracted") } wantDate := time.Date(2020, 3, 15, 0, 0, 0, 0, time.UTC) if !metadata.PublishDate.Equal(wantDate) { t.Errorf("PublishDate = %v, want %v", metadata.PublishDate, wantDate) } if len(metadata.Tags) != 2 { t.Errorf("Tags = %v, want 2 subjects", metadata.Tags) } }