aboutsummaryrefslogtreecommitdiff
path: root/internal/caldata/caldata_test.go
blob: a9bc860d2c74f35f5f6a8b3d2c0d16df8b5e29a1 (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
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
package caldata

import (
	"strings"
	"testing"

	"github.com/lukaszkasprzak/lectio/internal/calendar"
)

func TestUniversalLoads(t *testing.T) {
	l := Universal()
	if l.ID != "universal" {
		t.Fatalf("layer id = %q", l.ID)
	}
	// the Assumption (08-15, solemnity) must be present under some slug.
	assumption := false
	for _, rc := range l.Cels {
		if rc.Fields["date"] == "08-15" && rc.Fields["rank"] == "solemnity" {
			assumption = true
		}
	}
	if !assumption {
		t.Fatal("no 08-15 solemnity (Assumption) in the universal calendar")
	}
	// every entry must build into a typed Celebration whose rank parsed.
	for slug, rc := range l.Cels {
		c := calendar.BuildCelebrationForTest(slug, rc)
		if c.Rank == calendar.RankFerial && rc.Fields["rank"] != "" {
			t.Errorf("%s: rank did not parse (%q)", slug, rc.Fields["rank"])
		}
	}
}

func TestUniversalSanctoralReadings(t *testing.T) {
	l := Universal()
	// A feast/solemnity or a PROPER-reading memorial carries its own inline Mass.
	// Partial-proper memorials keep ONLY the strictly-proper part (the resolver
	// fills the rest from the ferial): the Guardian Angels a proper gospel, St
	// Barnabas a proper first reading, Our Lady of Sorrows a proper gospel.
	want := map[string][2]string{ // slug -> {reading.first substr, reading.gospel substr; "" = absent}
		"assumption-of-the-blessed-virgin-mary": {"Revelation 11", "Luke 1:39-56"},
		"all-saints":                            {"Revelation 7", "Matthew 5:1-12"},
		"barnabas-the-apostle":                  {"Acts 11", ""},
		"guardian-angels":                       {"", "Matthew 18:1-5"},
		"our-lady-of-sorrows":                   {"", "John 19:25-27"},
	}
	for slug, w := range want {
		rc, ok := l.Cels[slug]
		if !ok {
			t.Errorf("missing %q", slug)
			continue
		}
		for i, part := range []string{"reading.first", "reading.gospel"} {
			got := rc.Fields[part]
			if w[i] == "" {
				if got != "" {
					t.Errorf("%s: %s = %q, want absent (ferial)", slug, part, got)
				}
			} else if !strings.Contains(got, w[i]) {
				t.Errorf("%s: %s = %q, want containing %q", slug, part, got, w[i])
			}
		}
	}
}

func TestTridentineLoads(t *testing.T) {
	l := Tridentine()
	if l.ID != "tridentine" {
		t.Fatalf("layer id = %q", l.ID)
	}
	// The full 1962 sanctoral is populated (generated from missalemeum), not the
	// 16-entry bootstrap.
	if len(l.Cels) < 250 {
		t.Fatalf("EF sanctoral has only %d entries; expected the full calendar", len(l.Cels))
	}
	// Landmark feasts must be present with the right rank/class: Sts Peter & Paul
	// (I class, 06-29), the Purification/Presentation (II class, 02-02, class
	// = lord -- see classOf's own doc comment in scripts/gen-sanctoral-ef.go
	// for why: the calendarium's own title names the BLESSED VIRGIN, but the
	// tag exists to drive OCCURRENCE behaviour, and missalemeum -- this
	// data's own oracle -- shows the Purification taking a II-class Sunday's
	// place outright, the "festum Domini" pattern, not the ordinary-BVM
	// pattern), the Immaculate Conception (I class, 12-08).
	want := map[string]struct{ date, rank, class string }{
		"sts-peter-paul": {"06-29", "class-1", ""},
		"purification-of-the-blessed-virgin-mary":          {"02-02", "class-2", "lord"},
		"immaculate-conception-of-the-blessed-virgin-mary": {"12-08", "class-1", ""},
	}
	for slug, w := range want {
		rc, ok := l.Cels[slug]
		if !ok {
			t.Errorf("missing landmark feast %q", slug)
			continue
		}
		if rc.Fields["date"] != w.date || rc.Fields["rank"] != w.rank {
			t.Errorf("%s: date/rank = %q/%q, want %q/%q", slug, rc.Fields["date"], rc.Fields["rank"], w.date, w.rank)
		}
		if rc.Fields["class"] != w.class {
			t.Errorf("%s: class = %q, want %q", slug, rc.Fields["class"], w.class)
		}
	}
	// every entry must build into a typed Celebration whose rank parsed.
	for slug, rc := range l.Cels {
		c := calendar.BuildCelebrationForTest(slug, rc)
		if c.Rank == calendar.RankFerial && rc.Fields["rank"] != "" {
			t.Errorf("%s: rank did not parse (%q)", slug, rc.Fields["rank"])
		}
	}
}

// TestTridentineClassOfLord: scripts/gen-sanctoral-ef.go's classOf tags
// feasts of the Lord (class = lord). "Most Holy Name of Mary" was wrongly
// tagged lord (a title-substring false positive: "holy name" alone matches
// both "Holy Name of Jesus" and "Most Holy Name of MARY", calendarium
// "Sanctissimi Nominis Mariae") and is fixed here, not contested. The
// Baptism commemoration was missing the tag entirely (13 January's title
// ends "of THE Lord", not "of OUR Lord", the only suffix classOf used to
// check -- calendarium "IN COMMEMORATIONE BAPTISMATIS D. N. I. C."), also
// fixed and not contested. The Purification is DELIBERATELY absent from
// this table: it stays tagged lord, on occurrence-behaviour evidence, not
// the calendarium's title -- see TestTridentineLoads and classOf's own doc
// comment in scripts/gen-sanctoral-ef.go for the full account of why it is
// the one contested case, not a clean-cut fix.
func TestTridentineClassOfLord(t *testing.T) {
	l := Tridentine()
	want := map[string]string{
		"most-holy-name-of-mary":                   "", // BVM, not the Lord
		"commemoration-of-the-baptism-of-the-lord": "lord",
	}
	for slug, wantClass := range want {
		rc, ok := l.Cels[slug]
		if !ok {
			t.Errorf("missing landmark feast %q", slug)
			continue
		}
		if got := rc.Fields["class"]; got != wantClass {
			t.Errorf("%s: class = %q, want %q", slug, got, wantClass)
		}
	}
}

// TestTridentineLentRankNotCommemoration: 15 III-class feasts, every one
// falling 6 March - 5 April, were tagged rank = commemoration because all
// six of the generator's reference years happen to place that fixed date
// within Lent, where a privileged Lenten feria always outranks an
// equal-class feast (RG 109(e)) -- the generator mistook "how the entry
// presented on the sampled days" for the entry's true, intrinsic rank. A
// sample of the 15 (full list in the report); the calendarium gives each one
// III class outright, e.g. 5 April "S. Vincentii Ferrerii Conf., III
// classis."
func TestTridentineLentRankNotCommemoration(t *testing.T) {
	l := Tridentine()
	for _, slug := range []string{
		"sts-felicitas-perpetua", "thomas-aquinas", "frances-rome",
		"gregory-the-great", "patrick", "benedict", "francis-of-paola",
		"isidore-of-seville", "vincent-ferrer",
	} {
		rc, ok := l.Cels[slug]
		if !ok {
			t.Errorf("missing %q", slug)
			continue
		}
		if got := rc.Fields["rank"]; got != "class-3" {
			t.Errorf("%s: rank = %q, want class-3 (not commemoration)", slug, got)
		}
	}
}

// TestTridentineGenuineCommemorationStaysCommemoration: the fix above must
// NOT promote every commemoration-only entry indiscriminately -- some really
// are without an independent Mass in the 1960-reformed books, demoted even
// on an ORDINARY, unprivileged day (not explained by Lent/Passiontide or any
// higher-class temporal day). St Blaise (3 Feb, an ordinary Septuagesima-
// season feria) is live-confirmed by missalemeum as a mere commemoration
// even then, so RankCommemoration is the honest, correct rank -- promoting
// him to class-4 would let him wrongly win an ordinary green-season feria he
// has no independent Mass to celebrate.
func TestTridentineGenuineCommemorationStaysCommemoration(t *testing.T) {
	l := Tridentine()
	rc, ok := l.Cels["blaise"]
	if !ok {
		t.Fatal("missing blaise")
	}
	if got := rc.Fields["rank"]; got != "commemoration" {
		t.Errorf("blaise: rank = %q, want commemoration (genuinely no independent Mass)", got)
	}
}

// TestTridentineNoMissingEntries: four entries present in missalemeum (and
// so in the calendarium) were silently dropped by the generator's own
// dedup, which keyed a single global map by SLUG alone -- two by a literal
// slug collision with an unrelated feast of the same English title on a
// different date (28 January's "St. Agnes", the traditional SECOND
// commemoration of 21 January's own Agnes; 14 May's "St. Boniface", a
// different martyr from 5 June's Boniface of Mainz), and two (Evaristus,
// Theodore) by the generator returning on the first reference year that
// showed an observed office, before ever reaching the later year that
// revealed their commemoration.
func TestTridentineNoMissingEntries(t *testing.T) {
	l := Tridentine()
	want := map[string]string{
		"agnes-secundo":   "01-28",
		"boniface-martyr": "05-14",
		"evaristus":       "10-26",
		"theodore":        "11-09",
	}
	for slug, date := range want {
		rc, ok := l.Cels[slug]
		if !ok {
			t.Errorf("missing %q (%s)", slug, date)
			continue
		}
		if got := rc.Fields["date"]; got != date {
			t.Errorf("%s: date = %q, want %q", slug, got, date)
		}
	}
}

// TestTridentineNoSpuriousRomanus: the calendarium's 9 August row reads only
// "Vigilia, III classis.", with no "Com." line -- missalemeum's own "St.
// Romanus" commemoration that day has no calendarium support and is a
// genuine upstream data quirk (the same standard already applied to "St.
// Eusebius" on 14 August, also excluded).
func TestTridentineNoSpuriousRomanus(t *testing.T) {
	l := Tridentine()
	if _, ok := l.Cels["romanus"]; ok {
		t.Error("romanus present: the calendarium's 9 August row has no Com. line to support it")
	}
	if rc, ok := l.Cels["eusebius"]; ok && rc.Fields["date"] != "12-16" {
		t.Errorf("eusebius: date = %q, want 12-16 (14 August's Eusebius has no calendarium support)", rc.Fields["date"])
	}
}

// TestTridentineNoTransferArtifacts: a movable-transfer feast displayed on
// whatever civil date it actually landed on in a given reference year (St
// Joseph pushed to 20 March by a Sunday of Lent; the Annunciation deferred
// past Holy Week; All Souls moved to the Monday; St Matthias shown on the
// 25th in a leap year) must not leak into the sanctoral as a phantom
// fixed-date entry keyed to that transferred civil date.
func TestTridentineNoTransferArtifacts(t *testing.T) {
	l := Tridentine()
	for _, slug := range []string{
		"joseph-spouse-of-the-bl-virgin-mary-0320",
		"annunciation-of-the-blessed-virgin-mary-0405",
		"annunciation-of-the-blessed-virgin-mary-0408",
		"annunciation-of-the-blessed-virgin-mary-0409",
		"commemoration-of-all-souls-1103",
		"matthias-0225",
	} {
		if _, ok := l.Cels[slug]; ok {
			t.Errorf("phantom transfer-artifact entry %q present", slug)
		}
	}
}