summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-07-29 11:46:17 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-07-29 11:46:17 +0200
commit8ddd910dd545c45a7227da87ca125758b4a0e2cb (patch)
treeb652bb239c622e2f8806a443db21a0b0bf8c8962
parent79f9ae051c1e8f179e2e7c17f590c105eb18bef1 (diff)
downloadlectio-8ddd910dd545c45a7227da87ca125758b4a0e2cb.tar.gz
lectio-8ddd910dd545c45a7227da87ca125758b4a0e2cb.zip
test(bible): skip corpus-dependent tests when optional corpora aren't embedded
Since 51c0f4e split the corpora (only vul embedded by default; wuj/drb/grb behind -tags fullbible), `go test ./...` on the default build was red across six packages -- every failure was a test assuming an optional corpus is present. Guard those assertions with a skip keyed on bible.Meta(code), so they run under -tags fullbible and skip -- not fail -- on the default vul-only build. Mixed tests are split into subtests so the always-embedded vul assertions and pure logic (pl->vul fallback, explicit passthrough, bt-rejection, i18n labels) keep running on both builds. Test-only change; no product code or corpora touched. - internal/bible: TestVerses, TestLookup, TestCorpusBooks, TestChapters, TestGrbNoApparatusMarkers, TestCrossChapterRange* (requireCorpus helper). - internal/cli, config, render, tui, web: the same pattern for their corpus-dependent tests. - Fixes an index-out-of-range panic in internal/tui's reader tests that was aborting the package binary and masking 3 further corpus-absence failures (TestReaderBookmarkFlow, TestReaderChapterJump, TestReaderRemembersPlace). Verified: `go test ./...` and `go test -tags fullbible ./...` both green (0 FAIL); guards active only on the default build (29 skips vs 1 unrelated pre-existing); gofmt and go vet clean.
-rw-r--r--internal/bible/bible_test.go36
-rw-r--r--internal/bible/crosschapter_test.go2
-rw-r--r--internal/bible/ref_test.go37
-rw-r--r--internal/cli/cli_test.go1
-rw-r--r--internal/cli/liturgy_test.go31
-rw-r--r--internal/config/config_test.go82
-rw-r--r--internal/render/render_test.go48
-rw-r--r--internal/tui/reader_test.go48
-rw-r--r--internal/web/render_test.go45
-rw-r--r--internal/web/server_test.go58
10 files changed, 287 insertions, 101 deletions
diff --git a/internal/bible/bible_test.go b/internal/bible/bible_test.go
index 71a2c89..e3c18c8 100644
--- a/internal/bible/bible_test.go
+++ b/internal/bible/bible_test.go
@@ -14,24 +14,40 @@ func TestVerses(t *testing.T) {
{"wuj", "Wisdom", 3, 1, "A dusze sprawiedliwych"}, // deuterocanonical
}
for _, c := range cases {
- vs := Verses(c.version, c.book, c.chap)
- var got string
- for _, v := range vs {
- if v.Verse == c.verse {
- got = v.Text
+ t.Run(c.version+"/"+c.book, func(t *testing.T) {
+ requireCorpus(t, c.version)
+ vs := Verses(c.version, c.book, c.chap)
+ var got string
+ for _, v := range vs {
+ if v.Verse == c.verse {
+ got = v.Text
+ }
}
- }
- if !hasPrefix(got, c.wantPrefix) {
- t.Errorf("%s %s %d:%d = %q want prefix %q", c.version, c.book, c.chap, c.verse, got, c.wantPrefix)
- }
+ if !hasPrefix(got, c.wantPrefix) {
+ t.Errorf("%s %s %d:%d = %q want prefix %q", c.version, c.book, c.chap, c.verse, got, c.wantPrefix)
+ }
+ })
}
}
func hasPrefix(s, p string) bool { return len(s) >= len(p) && s[:len(p)] == p }
+// requireCorpus skips the test when corpus `code` is not available. The optional
+// corpora (wuj, drb, grb) are compiled in only with `-tags fullbible` (or dropped
+// into the user corpora dir); without that, only vul is embedded. Keying on
+// Meta() means these tests run fully under fullbible and skip -- not fail --
+// under the default build, so `go test ./...` stays honest either way.
+func requireCorpus(t *testing.T, code string) {
+ t.Helper()
+ if _, ok := Meta(code); !ok {
+ t.Skipf("corpus %q not embedded; build with -tags fullbible", code)
+ }
+}
+
// TestGrbNoApparatusMarkers guards the corpus fix: the SBLGNT apparatus sigla
// (U+2E00–U+2E0D) were stripped from the Greek text.
func TestGrbNoApparatusMarkers(t *testing.T) {
+ requireCorpus(t, "grb")
for _, v := range Verses("grb", "Luke", 16) {
for _, r := range v.Text {
if r >= 0x2E00 && r <= 0x2E0D {
@@ -53,6 +69,7 @@ func TestVul2Kings(t *testing.T) {
}
func TestCorpusBooks(t *testing.T) {
+ requireCorpus(t, "wuj")
books := CorpusBooks("wuj")
if len(books) == 0 {
t.Fatal("wuj corpus has no books")
@@ -72,6 +89,7 @@ func TestCorpusBooks(t *testing.T) {
}
func TestChapters(t *testing.T) {
+ requireCorpus(t, "wuj")
ch := Chapters("wuj", "John")
if len(ch) == 0 || ch[0] != 1 {
t.Fatalf("John chapters = %v", ch)
diff --git a/internal/bible/crosschapter_test.go b/internal/bible/crosschapter_test.go
index 7fb61eb..20074fb 100644
--- a/internal/bible/crosschapter_test.go
+++ b/internal/bible/crosschapter_test.go
@@ -8,6 +8,7 @@ import "testing"
// both chapters via Lookup, and format back to the clean "N:M-P:Q" form via
// FormatRef (no sentinel, no mangled chapter).
func TestCrossChapterRanges(t *testing.T) {
+ requireCorpus(t, "drb")
tbl, err := LoadBookTable(nil)
if err != nil {
t.Fatal(err)
@@ -90,6 +91,7 @@ func TestCrossChapterRanges(t *testing.T) {
// "Malachi 1:14-2:2,8-10" (Ordinary Sunday 31 A): a cross-chapter range
// followed by a plain verse group in the new (second) chapter.
func TestCrossChapterRangeWithTrailingGroup(t *testing.T) {
+ requireCorpus(t, "drb")
tbl, err := LoadBookTable(nil)
if err != nil {
t.Fatal(err)
diff --git a/internal/bible/ref_test.go b/internal/bible/ref_test.go
index 9473123..0c81a40 100644
--- a/internal/bible/ref_test.go
+++ b/internal/bible/ref_test.go
@@ -14,22 +14,29 @@ func TestSplitRef(t *testing.T) {
}
func TestLookup(t *testing.T) {
- vs, missing := Lookup("wuj", "John 20:1,11-18")
- if len(missing) != 0 {
- t.Fatalf("missing = %v", missing)
- }
- if len(vs) == 0 || vs[0].Verse != 1 {
- t.Fatalf("first verse = %+v", vs)
- }
- last := vs[len(vs)-1]
- if last.Verse != 18 {
- t.Errorf("last verse = %d want 18", last.Verse)
- }
- // Deuterocanon now present in vul + drb (Clementine Vulgate / Douay-Rheims).
+ t.Run("wuj", func(t *testing.T) {
+ requireCorpus(t, "wuj")
+ vs, missing := Lookup("wuj", "John 20:1,11-18")
+ if len(missing) != 0 {
+ t.Fatalf("missing = %v", missing)
+ }
+ if len(vs) == 0 || vs[0].Verse != 1 {
+ t.Fatalf("first verse = %+v", vs)
+ }
+ last := vs[len(vs)-1]
+ if last.Verse != 18 {
+ t.Errorf("last verse = %d want 18", last.Verse)
+ }
+ })
+ // Deuterocanon in the always-embedded Clementine Vulgate.
if vs, m := Lookup("vul", "Wisdom 3:1"); len(m) != 0 || len(vs) == 0 {
t.Errorf("vul Wisdom 3:1: missing=%v verses=%d (deuterocanon should resolve)", m, len(vs))
}
- if vs, m := Lookup("drb", "Judith 13:22"); len(m) != 0 || len(vs) == 0 {
- t.Errorf("drb Judith 13:22: missing=%v verses=%d (deuterocanon should resolve)", m, len(vs))
- }
+ // Deuterocanon in Douay-Rheims (optional corpus).
+ t.Run("drb", func(t *testing.T) {
+ requireCorpus(t, "drb")
+ if vs, m := Lookup("drb", "Judith 13:22"); len(m) != 0 || len(vs) == 0 {
+ t.Errorf("drb Judith 13:22: missing=%v verses=%d (deuterocanon should resolve)", m, len(vs))
+ }
+ })
}
diff --git a/internal/cli/cli_test.go b/internal/cli/cli_test.go
index a244885..429ceaf 100644
--- a/internal/cli/cli_test.go
+++ b/internal/cli/cli_test.go
@@ -298,6 +298,7 @@ func TestRandVerse(t *testing.T) {
}
func TestRandChapter(t *testing.T) {
+ requireCorpus(t, "wuj") // --rand-ch -b wuj needs the optional wuj corpus embedded
var out, errb bytes.Buffer
if code := Run([]string{"--rand-ch", "-b", "wuj"}, nil, &out, &errb); code != 0 {
t.Fatalf("rand-ch code=%d stderr=%q", code, errb.String())
diff --git a/internal/cli/liturgy_test.go b/internal/cli/liturgy_test.go
index 67303f3..88a8b3d 100644
--- a/internal/cli/liturgy_test.go
+++ b/internal/cli/liturgy_test.go
@@ -57,14 +57,35 @@ func TestRunLiturgyTraditionalComputesEF(t *testing.T) {
}
func TestVernacularVersionResolver(t *testing.T) {
- if got := vernacularVersion(config.Config{ReadingVersion: "drb"}); got != "drb" {
- t.Fatalf("explicit: %q", got)
- }
+ // pl has no autoselect-eligible corpus (the built-in Wujek sets
+ // autoselect=false), so Polish always falls back to Latin regardless of
+ // which optional corpora are embedded -- pure logic, keep it running.
if got := vernacularVersion(config.Config{UILanguage: "pl"}); got != "vul" {
t.Fatalf("pl should fall back to Latin: %q", got)
}
- if got := vernacularVersion(config.Config{UILanguage: "en"}); got != "drb" {
- t.Fatalf("en should be drb: %q", got)
+ // Both the explicit reading_version passthrough and the en->drb autoselect
+ // resolve through bible.Meta("drb") (Config.ReadingCorpus), so they need the
+ // optional drb corpus embedded.
+ t.Run("drb", func(t *testing.T) {
+ requireCorpus(t, "drb")
+ if got := vernacularVersion(config.Config{ReadingVersion: "drb"}); got != "drb" {
+ t.Fatalf("explicit: %q", got)
+ }
+ if got := vernacularVersion(config.Config{UILanguage: "en"}); got != "drb" {
+ t.Fatalf("en should be drb: %q", got)
+ }
+ })
+}
+
+// requireCorpus skips the test when corpus `code` is not embedded in this
+// build. The optional corpora (wuj, drb, grb) compile in only with
+// `-tags fullbible` (or when dropped into the user corpora dir); mirrors the
+// helper in internal/bible so these tests run under fullbible and skip -- not
+// fail -- on the default vul-only build.
+func requireCorpus(t *testing.T, code string) {
+ t.Helper()
+ if _, ok := bible.Meta(code); !ok {
+ t.Skipf("corpus %q not embedded; build with -tags fullbible", code)
}
}
diff --git a/internal/config/config_test.go b/internal/config/config_test.go
index 4d172c0..b1f3ddd 100644
--- a/internal/config/config_test.go
+++ b/internal/config/config_test.go
@@ -4,8 +4,22 @@ import (
"os"
"path/filepath"
"testing"
+
+ "github.com/lukaszkasprzak/lectio/internal/bible"
)
+// requireCorpus skips the test when corpus `code` is not embedded in this
+// build. The optional corpora (wuj, drb, grb) compile in only with
+// `-tags fullbible` (or when dropped into the user corpora dir); mirrors the
+// helper in internal/bible so these tests run under fullbible and skip -- not
+// fail -- on the default vul-only build.
+func requireCorpus(t *testing.T, code string) {
+ t.Helper()
+ if _, ok := bible.Meta(code); !ok {
+ t.Skipf("corpus %q not embedded; build with -tags fullbible", code)
+ }
+}
+
func TestLoadSeeds(t *testing.T) {
dir := t.TempDir()
t.Setenv("XDG_CONFIG_HOME", dir)
@@ -16,26 +30,32 @@ func TestLoadSeeds(t *testing.T) {
if cfg.DefaultVersion != "vul" || cfg.Offline {
t.Errorf("defaults wrong: %+v", cfg)
}
- // Versions defaults to the corpora actually available in this build, Vulgate
- // first then the rest sorted. Tests build with -tags fullbible, so all four
- // are present (see Makefile); a default vul-only binary would list just vul.
- wantVersions := []string{"vul", "drb", "grb", "wuj"}
- if len(cfg.Versions) != len(wantVersions) {
- t.Errorf("versions default = %v, want %v", cfg.Versions, wantVersions)
- } else {
- for i, v := range wantVersions {
- if cfg.Versions[i] != v {
- t.Errorf("versions default = %v, want %v", cfg.Versions, wantVersions)
- break
- }
- }
- }
if cfg.Lectionary != "new" {
t.Errorf("lectionary default wrong: %+v", cfg)
}
if _, err := os.Stat(filepath.Join(dir, "lectio", "config.ini")); err != nil {
t.Error("config not seeded")
}
+ // Versions defaults to the corpora actually available in this build, Vulgate
+ // first then the rest sorted. With -tags fullbible all four are present (see
+ // Makefile); a default vul-only binary would list just vul, so this
+ // assertion needs the optional corpora embedded.
+ t.Run("all corpora listed", func(t *testing.T) {
+ requireCorpus(t, "drb")
+ requireCorpus(t, "grb")
+ requireCorpus(t, "wuj")
+ wantVersions := []string{"vul", "drb", "grb", "wuj"}
+ if len(cfg.Versions) != len(wantVersions) {
+ t.Errorf("versions default = %v, want %v", cfg.Versions, wantVersions)
+ } else {
+ for i, v := range wantVersions {
+ if cfg.Versions[i] != v {
+ t.Errorf("versions default = %v, want %v", cfg.Versions, wantVersions)
+ break
+ }
+ }
+ }
+ })
}
// TestLoadMigratesBT checks a legacy config still carrying the retired "bt"
@@ -324,20 +344,26 @@ func TestPartShown(t *testing.T) {
}
func TestReadingCorpusResolution(t *testing.T) {
- // drb is an embedded corpus with lang=en.
- c := Config{ReadingVersion: "drb"}
- if got := c.ReadingCorpus(); got != "drb" {
- t.Fatalf("explicit reading_version: got %q", got)
- }
- c = Config{ReadingLang: "en"}
- if got := c.ReadingCorpus(); got != "drb" {
- t.Fatalf("reading_lang match: got %q", got)
- }
- c = Config{UILanguage: "en"}
- if got := c.ReadingCorpus(); got != "drb" {
- t.Fatalf("ui_language fallback: got %q", got)
- }
- c = Config{UILanguage: "pl"} // no pl corpus embedded
+ // The explicit-passthrough, reading_lang and ui_language cases all resolve to
+ // the embedded drb (lang=en) via bible.Meta/CorporaForLang, so they need drb.
+ t.Run("drb", func(t *testing.T) {
+ requireCorpus(t, "drb")
+ c := Config{ReadingVersion: "drb"}
+ if got := c.ReadingCorpus(); got != "drb" {
+ t.Fatalf("explicit reading_version: got %q", got)
+ }
+ c = Config{ReadingLang: "en"}
+ if got := c.ReadingCorpus(); got != "drb" {
+ t.Fatalf("reading_lang match: got %q", got)
+ }
+ c = Config{UILanguage: "en"}
+ if got := c.ReadingCorpus(); got != "drb" {
+ t.Fatalf("ui_language fallback: got %q", got)
+ }
+ })
+ // No pl corpus is autoselect-eligible, so pl resolves to "" on either build
+ // -- pure logic, keep it running.
+ c := Config{UILanguage: "pl"}
if got := c.ReadingCorpus(); got != "" {
t.Fatalf("no match should be empty: got %q", got)
}
diff --git a/internal/render/render_test.go b/internal/render/render_test.go
index dd177bb..e3bdc5d 100644
--- a/internal/render/render_test.go
+++ b/internal/render/render_test.go
@@ -4,20 +4,39 @@ import (
"strings"
"testing"
+ "github.com/lukaszkasprzak/lectio/internal/bible"
"github.com/lukaszkasprzak/lectio/internal/liturgy"
)
+// requireCorpus skips the test when corpus `code` is not embedded in this
+// build. The optional corpora (wuj, drb, grb) compile in only with
+// `-tags fullbible` (or when dropped into the user corpora dir); mirrors the
+// helper in internal/bible so these tests run under fullbible and skip -- not
+// fail -- on the default vul-only build.
+func requireCorpus(t *testing.T, code string) {
+ t.Helper()
+ if _, ok := bible.Meta(code); !ok {
+ t.Skipf("corpus %q not embedded; build with -tags fullbible", code)
+ }
+}
+
func TestGatherBible(t *testing.T) {
// Offline sections carry the English-canonical lookup reference in Ref; the
// display citation stays in the reader's sigla dialect.
sec := liturgy.Section{Heading: "Ewangelia", Citation: "J 20, 1. 11-18", Ref: "John 20:1,11-18"}
label, blocks := GatherVersion("wuj", sec, "new", "pl")
+ // The label is an i18n chrome string, independent of whether the corpus is
+ // embedded, so it must resolve on either build.
if !strings.Contains(label, "Wujek") {
t.Errorf("label = %q", label)
}
- if len(blocks) == 0 || !strings.HasPrefix(blocks[0], "20:1") {
- t.Errorf("first block = %q", blocks)
- }
+ // The verse blocks come from the wuj corpus text itself.
+ t.Run("verses", func(t *testing.T) {
+ requireCorpus(t, "wuj")
+ if len(blocks) == 0 || !strings.HasPrefix(blocks[0], "20:1") {
+ t.Errorf("first block = %q", blocks)
+ }
+ })
}
func TestGatherTraditional(t *testing.T) {
@@ -106,18 +125,23 @@ func TestOfflineVersions(t *testing.T) {
func TestGatherVersesBible(t *testing.T) {
sec := liturgy.Section{Heading: "Ewangelia", Citation: "J 20, 1. 11-18", Ref: "John 20:1,11-18"}
label, verses, versified := GatherVerses("wuj", sec, "new", "pl")
+ // The label is an i18n chrome string, independent of the corpus.
if !strings.Contains(label, "Wujek") {
t.Errorf("label = %q", label)
}
- if !versified {
- t.Error("versified = false, want true for wuj with a resolvable citation")
- }
- if len(verses) == 0 {
- t.Fatal("verses empty")
- }
- if verses[0].Chapter != 20 || verses[0].Verse != 1 || verses[0].Text == "" {
- t.Errorf("verses[0] = %+v", verses[0])
- }
+ // Versified extraction requires the wuj corpus text.
+ t.Run("verses", func(t *testing.T) {
+ requireCorpus(t, "wuj")
+ if !versified {
+ t.Error("versified = false, want true for wuj with a resolvable citation")
+ }
+ if len(verses) == 0 {
+ t.Fatal("verses empty")
+ }
+ if verses[0].Chapter != 20 || verses[0].Verse != 1 || verses[0].Text == "" {
+ t.Errorf("verses[0] = %+v", verses[0])
+ }
+ })
}
func TestGatherVersesBT(t *testing.T) {
diff --git a/internal/tui/reader_test.go b/internal/tui/reader_test.go
index 9d324ba..7bce759 100644
--- a/internal/tui/reader_test.go
+++ b/internal/tui/reader_test.go
@@ -30,6 +30,18 @@ func key(m ReaderModel, k tea.KeyMsg) ReaderModel {
return nm.(ReaderModel)
}
+// requireCorpus skips the test when corpus `code` is not embedded in this
+// build. The optional corpora (wuj, drb, grb) compile in only with
+// `-tags fullbible` (or when dropped into the user corpora dir); mirrors the
+// helper in internal/bible so these tests run under fullbible and skip -- not
+// fail -- on the default vul-only build.
+func requireCorpus(t *testing.T, code string) {
+ t.Helper()
+ if _, ok := bible.Meta(code); !ok {
+ t.Skipf("corpus %q not embedded; build with -tags fullbible", code)
+ }
+}
+
func runes(s string) tea.KeyMsg { return tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune(s)} }
func win(m ReaderModel, w, h int) ReaderModel {
@@ -55,6 +67,8 @@ func TestReaderFilterAndOpen(t *testing.T) {
nm, _ := m.Update(tea.WindowSizeMsg{Width: 80, Height: 24})
m = nm.(ReaderModel)
m = key(m, runes("jn"))
+ // Fuzzy filtering and entering read mode come from the book table and the
+ // model's state machine -- independent of any corpus being embedded.
if len(m.matches) == 0 || m.books[m.matches[0]].Canonical != "John" {
t.Fatalf("filter 'jn' top = %v", m.books[m.matches[m.pickSel]])
}
@@ -62,15 +76,25 @@ func TestReaderFilterAndOpen(t *testing.T) {
if m.mode != modeRead {
t.Fatalf("did not enter read mode")
}
- if m.books[m.bookIdx].Canonical != "John" || len(m.verses) == 0 {
- t.Errorf("opened book=%q verses=%d", m.books[m.bookIdx].Canonical, len(m.verses))
- }
- if !strings.Contains(m.View(), "1") { // chapter 1 header
- t.Errorf("read view missing chapter:\n%s", m.View())
- }
+ // The reader's first version is wuj (see enReader): its verses and chapter
+ // header need the optional wuj corpus embedded.
+ t.Run("wuj verses", func(t *testing.T) {
+ requireCorpus(t, "wuj")
+ if m.books[m.bookIdx].Canonical != "John" || len(m.verses) == 0 {
+ t.Errorf("opened book=%q verses=%d", m.books[m.bookIdx].Canonical, len(m.verses))
+ }
+ if !strings.Contains(m.View(), "1") { // chapter 1 header
+ t.Errorf("read view missing chapter:\n%s", m.View())
+ }
+ })
}
func TestReaderChapterAndVersion(t *testing.T) {
+ // Whole-test guard: the reader's first version is wuj (see enReader), so the
+ // very first read-mode step below indexes m.chapters[m.chapPos] -- which
+ // panics on an empty chapter list when wuj is not embedded. The chapter
+ // stepping and version cycling that follow are all a wuj reading session.
+ requireCorpus(t, "wuj")
m := enReader(t)
nm, _ := m.Update(tea.WindowSizeMsg{Width: 80, Height: 24})
m = nm.(ReaderModel)
@@ -107,6 +131,10 @@ func TestReaderFuzzyScore(t *testing.T) {
}
func TestReaderBookmarkFlow(t *testing.T) {
+ // The whole flow reads and bookmarks verses of John from wuj (the reader's
+ // first version); "m" only opens the verse picker when len(verses)>0, so
+ // the optional wuj corpus must be embedded.
+ requireCorpus(t, "wuj")
m := enReader(t)
m = win(m, 80, 24)
m = key(m, runes("jn"))
@@ -166,6 +194,10 @@ func TestReaderBookmarkFlow(t *testing.T) {
}
func TestReaderChapterJump(t *testing.T) {
+ // Reads John from wuj (the reader's first version); "c" only opens the
+ // chapter-jump prompt when the book has >1 chapter loaded, so the optional
+ // wuj corpus must be embedded.
+ requireCorpus(t, "wuj")
m := enReader(t)
m = win(m, 80, 24)
m = key(m, runes("jn"))
@@ -224,6 +256,10 @@ func TestReaderChapterJump(t *testing.T) {
}
func TestReaderRemembersPlace(t *testing.T) {
+ // Persisting/restoring a reading position needs real verses (savePlace
+ // no-ops when len(verses)==0); the reader's first version is wuj, so the
+ // optional wuj corpus must be embedded.
+ requireCorpus(t, "wuj")
dir := t.TempDir()
t.Setenv("XDG_DATA_HOME", dir)
tbl, _ := bible.LoadBookTable(nil)
diff --git a/internal/web/render_test.go b/internal/web/render_test.go
index 39bcc58..94d5e6e 100644
--- a/internal/web/render_test.go
+++ b/internal/web/render_test.go
@@ -124,26 +124,37 @@ func TestRenderReadingsInterlinear(t *testing.T) {
t.Errorf("interlinear output missing vnum: %q", html[:min(300, len(html))])
}
// The first ilverse block should group both version labels under one key.
- // Bound it by the next vnum span (each ilverse carries exactly one).
- i := strings.Index(html, `class="vnum"`)
- block := html[i:]
- if j := strings.Index(html[i+1:], `class="vnum"`); j != -1 {
- block = html[i : i+1+j]
- }
- if !strings.Contains(block, "Wujek") || !strings.Contains(block, "Wulgata") {
- t.Errorf("first interlinear verse block missing both version labels: %q", block)
- }
+ // Bound it by the next vnum span (each ilverse carries exactly one). The
+ // Wujek line appears only when the optional wuj corpus is embedded and
+ // contributes versified text to the alignment.
+ t.Run("both version labels", func(t *testing.T) {
+ requireCorpus(t, "wuj")
+ i := strings.Index(html, `class="vnum"`)
+ block := html[i:]
+ if j := strings.Index(html[i+1:], `class="vnum"`); j != -1 {
+ block = html[i : i+1+j]
+ }
+ if !strings.Contains(block, "Wujek") || !strings.Contains(block, "Wulgata") {
+ t.Errorf("first interlinear verse block missing both version labels: %q", block)
+ }
+ })
}
func TestRenderReadingsInterlinearExcludesBT(t *testing.T) {
secs := []liturgy.Section{{Heading: "Ewangelia", Citation: "J 20, 1. 11-18", Ref: "John 20:1,11-18", PartID: "ewangelia"}}
html := string(RenderReadings(secs, []string{"bt", "vul"}, "new", "interlinear", "pl", liturgy.DayInfo{}))
+ // bt is dropped before rendering regardless, so its own label never shows.
if strings.Contains(html, "Biblia Tysiąclecia (niedziela.pl)") {
t.Errorf("interlinear output should substitute wuj for bt, not carry bt's label: %q", html[:min(300, len(html))])
}
- if !strings.Contains(html, "Wujek") {
- t.Errorf("interlinear output should substitute wuj for bt: %q", html[:min(300, len(html))])
- }
+ // bt maps to wuj, but the Wujek label only renders when wuj is embedded and
+ // supplies versified text to the alignment.
+ t.Run("wuj substituted", func(t *testing.T) {
+ requireCorpus(t, "wuj")
+ if !strings.Contains(html, "Wujek") {
+ t.Errorf("interlinear output should substitute wuj for bt: %q", html[:min(300, len(html))])
+ }
+ })
}
func TestRenderReadingsInterlinearNoVersifiedNote(t *testing.T) {
@@ -190,9 +201,15 @@ func TestRenderReadingsLocalizesEN(t *testing.T) {
func TestRenderPassageColumns(t *testing.T) {
html := string(RenderPassage("John", "John", 3, []string{"wuj"}, "vertical", "en"))
- if !strings.Contains(html, "John 3") || !strings.Contains(html, "3:16") {
- t.Errorf("passage columns missing heading/verse:\n%s", html)
+ if !strings.Contains(html, "John 3") { // passage heading, independent of the corpus
+ t.Errorf("passage columns missing heading:\n%s", html)
}
+ t.Run("verse", func(t *testing.T) {
+ requireCorpus(t, "wuj") // the verse text comes from the optional wuj corpus
+ if !strings.Contains(html, "3:16") {
+ t.Errorf("passage columns missing verse:\n%s", html)
+ }
+ })
}
func TestRenderPassageInterlinear(t *testing.T) {
diff --git a/internal/web/server_test.go b/internal/web/server_test.go
index 42de0b5..49f7776 100644
--- a/internal/web/server_test.go
+++ b/internal/web/server_test.go
@@ -15,6 +15,18 @@ import (
"github.com/lukaszkasprzak/lectio/internal/liturgy"
)
+// requireCorpus skips the test when corpus `code` is not embedded in this
+// build. The optional corpora (wuj, drb, grb) compile in only with
+// `-tags fullbible` (or when dropped into the user corpora dir); mirrors the
+// helper in internal/bible so these tests run under fullbible and skip -- not
+// fail -- on the default vul-only build.
+func requireCorpus(t *testing.T, code string) {
+ t.Helper()
+ if _, ok := bible.Meta(code); !ok {
+ t.Skipf("corpus %q not embedded; build with -tags fullbible", code)
+ }
+}
+
// TestServer exercises NewServer's handler tree end to end via httptest. Every
// reading is computed offline (no network, no cache, no fixture server).
func TestServer(t *testing.T) {
@@ -33,10 +45,14 @@ func TestServer(t *testing.T) {
t.Errorf("body missing reading heading: %q", body)
}
// ?v=wuj -> the gospel is rendered from the Wujek corpus ("grobu" is
- // distinctly Polish Wujek verse text, not Latin/English).
- if !strings.Contains(body, "grobu") {
- t.Errorf("body missing Wujek verse text: %q", body)
- }
+ // distinctly Polish Wujek verse text, not Latin/English) -- needs the
+ // optional wuj corpus embedded.
+ t.Run("wuj verse text", func(t *testing.T) {
+ requireCorpus(t, "wuj")
+ if !strings.Contains(body, "grobu") {
+ t.Errorf("body missing Wujek verse text: %q", body)
+ }
+ })
if !strings.Contains(body, "htmx") {
t.Errorf("body missing htmx reference")
}
@@ -121,9 +137,14 @@ func TestServer(t *testing.T) {
block = body[i : i+1+j]
}
// config.Default() is English chrome: "Wujek (Polish)"/"Vulgate (Latin)".
- if !strings.Contains(block, "Wujek") || !strings.Contains(block, "Vulgate") {
- t.Errorf("interlinear verse block missing both version labels grouped together: %q", block)
- }
+ // The Wujek line appears only when the optional wuj corpus is embedded and
+ // contributes versified text to the alignment.
+ t.Run("both version labels", func(t *testing.T) {
+ requireCorpus(t, "wuj")
+ if !strings.Contains(block, "Wujek") || !strings.Contains(block, "Vulgate") {
+ t.Errorf("interlinear verse block missing both version labels grouped together: %q", block)
+ }
+ })
})
t.Run("readings partial vertical", func(t *testing.T) {
@@ -144,9 +165,15 @@ func TestServer(t *testing.T) {
t.Fatalf("status = %d, want 200", rec.Code)
}
body := rec.Body.String()
- if !strings.Contains(body, "Wujek") {
- t.Errorf("bt should be substituted with wuj in interlinear mode: %q", body)
- }
+ // bt maps to wuj, but its Wujek label only renders when wuj is embedded and
+ // supplies versified text to the interlinear alignment.
+ t.Run("wuj substituted", func(t *testing.T) {
+ requireCorpus(t, "wuj")
+ if !strings.Contains(body, "Wujek") {
+ t.Errorf("bt should be substituted with wuj in interlinear mode: %q", body)
+ }
+ })
+ // bt is dropped before rendering regardless, so its own label never shows.
if strings.Contains(body, "Biblia Tysiąclecia (niedziela.pl)") {
t.Errorf("bt paragraph column should not appear in interlinear mode: %q", body)
}
@@ -251,9 +278,16 @@ func TestReaderPassage(t *testing.T) {
if rec.Code != 200 {
t.Fatalf("status %d", rec.Code)
}
- if b := rec.Body.String(); !strings.Contains(b, "John 3") || !strings.Contains(b, "3:16") {
- t.Errorf("passage missing heading/verse")
+ b := rec.Body.String()
+ if !strings.Contains(b, "John 3") { // passage heading, independent of the corpus
+ t.Errorf("passage missing heading")
}
+ t.Run("verse", func(t *testing.T) {
+ requireCorpus(t, "wuj") // the verse text comes from the optional wuj corpus
+ if !strings.Contains(b, "3:16") {
+ t.Errorf("passage missing verse")
+ }
+ })
}
func TestReaderCompareAndBTFilter(t *testing.T) {