summaryrefslogtreecommitdiff
path: root/internal/render/render_test.go
blob: e3bdc5d8ce5b9ccf349138db5df6040a8c5b8f05 (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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
package render

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)
	}
	// 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) {
	sec := liturgy.Section{Citation: "Luke 7:36-50"}
	_, blocks := GatherVersion("vul", sec, "traditional", "pl")
	if len(blocks) == 0 || !strings.HasPrefix(blocks[0], "7:36") {
		t.Errorf("first block = %q", blocks)
	}
}

// TestGatherVersionLabelLang checks that the label's language follows the
// lang parameter -- pl reproduces today's Polish labels exactly, en gives
// the English set from internal/i18n.
func TestGatherVersionLabelLang(t *testing.T) {
	sec := liturgy.Section{Heading: "Ewangelia (J 20, 1. 11-18)"}

	label, _ := GatherVersion("vul", sec, "new", "pl")
	if label != "Wulgata (lac.)" {
		t.Errorf(`GatherVersion(..., "pl") label = %q, want "Wulgata (lac.)"`, label)
	}

	label, _ = GatherVersion("vul", sec, "new", "en")
	if label != "Vulgate (Latin)" {
		t.Errorf(`GatherVersion(..., "en") label = %q, want "Vulgate (Latin)"`, label)
	}
}

// TestCompareLabelLang checks Compare's column-header row follows lang,
// same as GatherVersion (which it wraps via compareSection).
func TestCompareLabelLang(t *testing.T) {
	sec := liturgy.Section{Heading: "Ewangelia (J 20, 1. 11-18)"}

	out := Compare([]liturgy.Section{sec}, []string{"vul"}, 80, "new", "pl")
	if !strings.Contains(out, "Wulgata (lac.)") {
		t.Errorf(`Compare(..., "pl") = %q, want it to contain "Wulgata (lac.)"`, out)
	}

	out = Compare([]liturgy.Section{sec}, []string{"vul"}, 80, "new", "en")
	if !strings.Contains(out, "Vulgate (Latin)") {
		t.Errorf(`Compare(..., "en") = %q, want it to contain "Vulgate (Latin)"`, out)
	}
}

// TestGatherVersionNoVersionLang checks that the "(not in %s)" block
// (bible.Lookup finding nothing at all) follows lang -- pl reproduces the
// original Polish wording exactly, en uses internal/i18n's English wording.
func TestGatherVersionNoVersionLang(t *testing.T) {
	sec := liturgy.Section{Heading: "Ewangelia (J 20, 1. 11-18)"}

	_, blocks := GatherVersion("zzz", sec, "new", "pl")
	if len(blocks) == 0 || blocks[0] != `(brak w „zzz”)` {
		t.Errorf(`GatherVersion(..., "pl") blocks = %v, want [(brak w „zzz”)]`, blocks)
	}

	_, blocks = GatherVersion("zzz", sec, "new", "en")
	if len(blocks) == 0 || blocks[0] != "(not in zzz)" {
		t.Errorf(`GatherVersion(..., "en") blocks = %v, want [(not in zzz)]`, blocks)
	}
}

// TestGatherVersionNoReferenceLang checks the "(no reference)" block (no
// citation resolvable at all) follows lang.
func TestGatherVersionNoReferenceLang(t *testing.T) {
	sec := liturgy.Section{Heading: "Bez odwołania"}

	_, blocks := GatherVersion("wuj", sec, "new", "pl")
	if len(blocks) == 0 || blocks[0] != "(brak odwołania)" {
		t.Errorf(`GatherVersion(..., "pl") blocks = %v, want [(brak odwołania)]`, blocks)
	}

	_, blocks = GatherVersion("wuj", sec, "new", "en")
	if len(blocks) == 0 || blocks[0] != "(no reference)" {
		t.Errorf(`GatherVersion(..., "en") blocks = %v, want [(no reference)]`, blocks)
	}
}

func TestOfflineVersions(t *testing.T) {
	got := OfflineVersions([]string{"bt", "wuj", "vul"})
	for _, v := range got {
		if v == "bt" {
			t.Error("bt not dropped offline")
		}
	}
}

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)
	}
	// 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) {
	sec := liturgy.Section{
		Heading:    "Psalm (Ps 1)",
		Paragraphs: [][]string{{"stanza one"}},
	}
	_, verses, versified := GatherVerses("bt", sec, "new", "pl")
	if versified {
		t.Error("versified = true, want false for bt (paragraph text, no verse numbers)")
	}
	if verses != nil {
		t.Errorf("verses = %+v, want nil for bt", verses)
	}
}

// TestGatherVersesLabelLang checks GatherVerses' label also follows lang,
// same as GatherVersion.
func TestGatherVersesLabelLang(t *testing.T) {
	sec := liturgy.Section{Heading: "Ewangelia (J 20, 1. 11-18)"}
	label, _, _ := GatherVerses("vul", sec, "new", "en")
	if label != "Vulgate (Latin)" {
		t.Errorf(`GatherVerses(..., "en") label = %q, want "Vulgate (Latin)"`, label)
	}
}

// TestLocalizeHeading covers render.LocalizeHeading's part-label swap
// (brief §3b): the pl label prefix of a modern (niedziela.pl) heading is
// replaced by its en label, the citation kept exactly as scraped, and every
// other case (pl, unknown/traditional partID, prefix mismatch) is a safe
// no-op that returns heading unchanged.
func TestHeadingWithRef(t *testing.T) {
	// Traditional-style: a bare label plus Citation -> reference appended.
	trad := liturgy.Section{Heading: "Gospel", Citation: "Luke 16:1-9", PartID: "evangelium"}
	if got := HeadingWithRef(trad, "en"); got != "Gospel (Luke 16:1-9)" {
		t.Errorf("traditional heading = %q, want %q", got, "Gospel (Luke 16:1-9)")
	}
	// Modern-style: heading already carries "(...)" -> not doubled; the pl label
	// is still localised to en.
	modern := liturgy.Section{Heading: "Ewangelia (J 20, 1. 11-18)", Citation: "J 20, 1. 11-18", PartID: "ewangelia"}
	if got := HeadingWithRef(modern, "en"); got != "Gospel (J 20, 1. 11-18)" {
		t.Errorf("modern heading = %q", got)
	}
	// No citation -> unchanged.
	if got := HeadingWithRef(liturgy.Section{Heading: "Gospel", PartID: "evangelium"}, "en"); got != "Gospel" {
		t.Errorf("no-citation heading = %q", got)
	}
}

func TestLocalizeHeading(t *testing.T) {
	cases := []struct {
		name, heading, partID, lang, want string
	}{
		{"gospel en", "Ewangelia (J 20, 1. 11-18)", "ewangelia", "en", "Gospel (J 20, 1. 11-18)"},
		{"first reading en", "1. czytanie (Dz 2, 14. 22-33)", "pierwsze_czytanie", "en", "1st reading (Dz 2, 14. 22-33)"},
		{"psalm en", "Psalm (Ps 15)", "psalm", "en", "Psalm (Ps 15)"},
		{"acclamation en", "Aklamacja (Alleluja)", "aklamacja", "en", "Acclamation (Alleluja)"},
		{"pl unchanged", "Ewangelia (J 20, 1. 11-18)", "ewangelia", "pl", "Ewangelia (J 20, 1. 11-18)"},
		{"unknown partID unchanged", "Coś innego (X 1)", "", "en", "Coś innego (X 1)"},
		{"already-English heading unchanged (traditional lectionary, no pl prefix to match)", "Gospel (Luke 7:36-50)", "ewangelia", "en", "Gospel (Luke 7:36-50)"},
		{"prefix mismatch unchanged", "Nieoczekiwany tytuł (J 1)", "ewangelia", "en", "Nieoczekiwany tytuł (J 1)"},
		// Split (two-reading) feast day: niedziela.pl scrapes
		// PartID="drugie_czytanie" onto a heading that still literally
		// starts with "1. czytanie" (its own numbering quirk). Matching
		// must follow the heading's actual text, not partID's label, so
		// this localises to "1st reading", not staying Polish.
		{"drugie_czytanie partID with a 1. czytanie heading (split feast day)", "1. czytanie (Dz 2, 14. 22-33)", "drugie_czytanie", "en", "1st reading (Dz 2, 14. 22-33)"},
	}

	for _, c := range cases {
		t.Run(c.name, func(t *testing.T) {
			got := LocalizeHeading(c.heading, c.partID, c.lang)
			if got != c.want {
				t.Errorf("LocalizeHeading(%q, %q, %q) = %q, want %q", c.heading, c.partID, c.lang, got, c.want)
			}
		})
	}
}