package kepub import ( "bytes" "context" "image" "image/jpeg" "net/url" "os" "path/filepath" "regexp" "strings" "testing" "time" "golang.org/x/net/html" "git.monoexp.com/bajdev/rekopub/internal/build" "git.monoexp.com/bajdev/rekopub/internal/clean" "git.monoexp.com/bajdev/rekopub/internal/epubtest" "git.monoexp.com/bajdev/rekopub/internal/extract" ) // testEPUB builds a small plain EPUB with a cover and two sections. func testEPUB(t *testing.T) []byte { t.Helper() doc, err := html.Parse(strings.NewReader(`

First sentence here. Second sentence follows it!

A section

Text in the section, with emphasis and a link.

Another

`)) if err != nil { t.Fatal(err) } a := &extract.Article{ URL: "https://example.com/post", Title: "KEPUB Test", Author: "Ada Lovelace", SiteName: "Example", Language: "en", Published: time.Date(2025, 1, 2, 0, 0, 0, 0, time.UTC), Content: doc.FirstChild.LastChild, } u, _ := url.Parse(a.URL) body, err := clean.Content(a.Content, u) if err != nil { t.Fatal(err) } var cover bytes.Buffer if err := jpeg.Encode(&cover, image.NewGray(image.Rect(0, 0, 30, 40)), nil); err != nil { t.Fatal(err) } data, err := build.Build(a, body, nil, cover.Bytes()) if err != nil { t.Fatal(err) } return data } var tags = regexp.MustCompile(`<[^>]+>`) // bodyText returns the visible text of a chapter's , with whitespace // collapsed. func bodyText(chapter string) string { if i := strings.Index(chapter, "= 0 { chapter = chapter[i:] } return strings.Join(strings.Fields(tags.ReplaceAllString(chapter, " ")), " ") } func TestConvert(t *testing.T) { plain := testEPUB(t) data, err := Convert(context.Background(), plain) if err != nil { t.Fatal(err) } book := epubtest.Open(t, data) // also checks mimetype is first and stored book.AssertWellFormed(t) before := epubtest.Open(t, plain) chapter := book.File(t, "chapter.xhtml") if !strings.Contains(chapter, ``) { t.Errorf("chapter has no koboSpan elements:\n%s", chapter) } // Sentences are spanned separately, so Kobo can count and highlight them. if !strings.Contains(chapter, `>First sentence here. Example", "https://example.com/post"} { if !strings.Contains(opf, want) { t.Errorf("OPF lost %q:\n%s", want, opf) } } if !strings.Contains(book.File(t, "nav.xhtml"), "#sec-2") || !strings.Contains(book.File(t, "toc.ncx"), "dtb:uid") { t.Errorf("table of contents changed by conversion") } path := filepath.Join(t.TempDir(), "book.kepub.epub") if err := os.WriteFile(path, data, 0o644); err != nil { t.Fatal(err) } epubtest.EPUBCheck(t, path) } func TestConvertRejectsNonEPUB(t *testing.T) { if _, err := Convert(context.Background(), []byte("not a zip file")); err == nil { t.Error("Convert accepted data that isn't an EPUB") } } func TestConvertCancelled(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) cancel() if _, err := Convert(ctx, testEPUB(t)); err == nil { t.Error("Convert succeeded after cancellation") } }