aboutsummaryrefslogtreecommitdiff
path: root/internal/bible/crosschapter_test.go
blob: 7fb61eb4c0791a1caca23c66c3e8063a66cbde88 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
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)
	}
}