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")
}
}