package bible import "testing" // TestCrossChapterRanges covers the 8 real of-lectionary.ini citations whose // verse span crosses a chapter boundary ("N:M-P:Q"), e.g. "Sirach 27:30-28:7". // Each must: parse via ParseRef, resolve a non-empty verse slice spanning // 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) { tbl, err := LoadBookTable(nil) if err != nil { t.Fatal(err) } cases := []struct { citation string fromChap, toChap int wantDisplay string // spanBoth is false only for the one citation where the corpus's own // versification (Vulgate/DRB tradition) doesn't have a chapter-8 // verse 23 at all: Isaiah 8:23 (Hebrew/NAB numbering, used by the // lectionary) is folded into Isaiah 9:1 in the Vulgate/DRB/Wujek // tradition -- a real textual/versification difference, not a // parser bug. The range-crossing fetch itself is exercised // correctly (it asks for chapter 8 from v23 and finds nothing // there, then chapter 9 through v3); it just has nothing to find in // chapter 8 of this corpus. spanBoth bool }{ {"Genesis 1:1-2:2", 1, 2, "Gen 1:1-2:2", true}, {"Isaiah 52:13-53:12", 52, 53, "Isa 52:13-53:12", true}, {"Isaiah 8:23-9:3", 8, 9, "Isa 8:23-9:3", false}, {"John 18:1-19:42", 18, 19, "Jn 18:1-19:42", true}, {"Malachi 1:14-2:2", 1, 2, "Mal 1:14-2:2", true}, {"Mat 9:36-10:8", 9, 10, "Matt 9:36-10:8", true}, {"Sirach 27:30-28:7", 27, 28, "Sir 27:30-28:7", true}, {"1 John 1:5-2:2", 1, 2, "1 Jn 1:5-2:2", true}, } for _, c := range cases { t.Run(c.citation, func(t *testing.T) { engRef, ok := tbl.ParseRef("en", c.citation) if !ok { t.Fatalf("ParseRef(%q) failed", c.citation) } vs, missing := Lookup("drb", engRef) if len(vs) == 0 { t.Fatalf("Lookup(drb, %q) empty; missing=%v", engRef, missing) } if len(missing) != 0 { t.Errorf("Lookup(drb, %q) missing=%v (want none -- some text should resolve)", engRef, missing) } last := vs[len(vs)-1] if last.Chapter != c.toChap { t.Errorf("last verse chapter = %d want %d (verse %+v)", last.Chapter, c.toChap, last) } for _, v := range vs { if v.Chapter < c.fromChap || v.Chapter > c.toChap { t.Errorf("verse outside expected chapter range: %+v", v) } } if c.spanBoth { first := vs[0] if first.Chapter != c.fromChap { t.Errorf("first verse chapter = %d want %d (verse %+v)", first.Chapter, c.fromChap, first) } sawFrom, sawTo := false, false for _, v := range vs { if v.Chapter == c.fromChap { sawFrom = true } if v.Chapter == c.toChap { sawTo = true } } if !sawFrom || !sawTo { t.Errorf("range does not span both chapters: sawFrom=%v sawTo=%v verses=%d", sawFrom, sawTo, len(vs)) } } display := tbl.FormatRef("en", engRef) if display != c.wantDisplay { t.Errorf("FormatRef(en, %q) = %q want %q", engRef, display, c.wantDisplay) } }) } } // TestCrossChapterRangeWithTrailingGroup covers the compound real citation // "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) { tbl, err := LoadBookTable(nil) if err != nil { t.Fatal(err) } engRef, ok := tbl.ParseRef("en", "Malachi 1:14-2:2,8-10") if !ok { t.Fatalf("ParseRef failed") } if engRef != "Malachi 1:14-2:2,8-10" { t.Errorf("ParseRef canonical = %q", engRef) } vs, missing := Lookup("drb", engRef) if len(vs) == 0 { t.Fatalf("Lookup(drb, %q) empty; missing=%v", engRef, missing) } // Expect: 1:14-19 (end of chap 1), 2:1-2, 2:8-10. var got []struct{ Chapter, Verse int } for _, v := range vs { got = append(got, struct{ Chapter, Verse int }{v.Chapter, v.Verse}) } if got[0].Chapter != 1 || got[0].Verse != 14 { t.Errorf("first verse = %+v want chap 1 verse 14", got[0]) } last := got[len(got)-1] if last.Chapter != 2 || last.Verse != 10 { t.Errorf("last verse = %+v want chap 2 verse 10", last) } sawChap2v2, sawChap2v8 := false, false for _, g := range got { if g.Chapter == 2 && g.Verse == 2 { sawChap2v2 = true } if g.Chapter == 2 && g.Verse == 8 { sawChap2v8 = true } } if !sawChap2v2 || !sawChap2v8 { t.Errorf("expected verses 2:2 and 2:8 present, got %+v", got) } display := tbl.FormatRef("en", engRef) if display != "Mal 1:14-2:2,8-10" { t.Errorf("FormatRef = %q want %q", display, "Mal 1:14-2:2,8-10") } } // TestSplitRefCrossChapter verifies SplitRef's expansion of a cross-chapter // group inside a comma list, and that a single cross-chapter range with no // comma is left as one part (Lookup expands it directly). func TestSplitRefCrossChapter(t *testing.T) { if g := SplitRef("Sirach 27:30-28:7"); len(g) != 1 || g[0] != "Sirach 27:30-28:7" { t.Errorf("SplitRef single cross-chapter = %v", g) } got := SplitRef("Malachi 1:14-2:2,8-10") want := []string{"Malachi 1:14-2:2", "Malachi 2:8-10"} if len(got) != 2 || got[0] != want[0] || got[1] != want[1] { t.Errorf("SplitRef cross-chapter + trailing group = %v want %v", got, want) } // Existing semicolon-turned-comma precedent must still work. got2 := SplitRef("Judith 13:22-25,15:10") want2 := []string{"Judith 13:22-25", "Judith 15:10"} if len(got2) != 2 || got2[0] != want2[0] || got2[1] != want2[1] { t.Errorf("SplitRef disjoint cross-chapter = %v want %v", got2, want2) } }