summaryrefslogtreecommitdiff
path: root/internal/caldata/caldata_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/caldata/caldata_test.go')
-rw-r--r--internal/caldata/caldata_test.go307
1 files changed, 303 insertions, 4 deletions
diff --git a/internal/caldata/caldata_test.go b/internal/caldata/caldata_test.go
index 12aa765..7dbbf84 100644
--- a/internal/caldata/caldata_test.go
+++ b/internal/caldata/caldata_test.go
@@ -74,9 +74,13 @@ func TestTridentineLoads(t *testing.T) {
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 (a II class feast of the
- // Lord, 02-02, which must displace a Sunday), the Immaculate Conception
- // (I class, 12-08).
+ // (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"},
@@ -91,7 +95,7 @@ func TestTridentineLoads(t *testing.T) {
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 w.class != "" && rc.Fields["class"] != w.class {
+ if rc.Fields["class"] != w.class {
t.Errorf("%s: class = %q, want %q", slug, rc.Fields["class"], w.class)
}
}
@@ -103,3 +107,298 @@ func TestTridentineLoads(t *testing.T) {
}
}
}
+
+// 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)
+ }
+ }
+}
+
+// TestTridentineRomanusAndEusebiusPresent: CORRECTED after review found the
+// opposite claim resting on an incomplete primary-source check. An earlier
+// version of this test (and of scripts/gen-sanctoral-ef.go's
+// knownSpuriousComm) asserted Romanus ABSENT, on the strength of ONE of the
+// three local Missal scans -- "1962-06-23,…LT.pdf", an ELECTRONIC
+// TRANSCRIPTION that silently drops vigil commemorations generally (also
+// missing: 7 August Donatus, 25 December Anastasia, both present elsewhere).
+// The other two, PHOTOGRAPHIC scans of the actual 1962 Missale Romanum, both
+// carry it: "missale-romanum-1962.pdf" calendarium, 9 August row: "XVI d V
+// 9 Vigilia, III classis, Commemoratio S. Romani Mart.", with the saint's
+// own proper text elsewhere in the same scan ("Et fit commemoratio S. Romani
+// Mar-"), and its own back-of-book index ("Romani Mart., 9 augusti ... 621").
+// Where the scans and the transcription disagree, the scans win -- see
+// gen-sanctoral-ef.go's own primary-source note, same rule recorded there so
+// it is not lost a second time.
+//
+// 14 August's "St. Eusebius" is the identical shape (calendarium: "XI b XIX
+// 14 Vigilia, II classis, Commemoratio S. Eusebii Conf.") and a DIFFERENT
+// person from 16 December's "St. Eusebius, Ep. et Mart." (calendarium: "V
+// XVII S. Eusebii Ep. et Mart., III classis.") -- a Confessor and a Bishop
+// and Martyr, not the same saint moved or duplicated. missalemeum gives both
+// the same bare English title, so they collide by slug; slugOverride
+// disambiguates rather than either being dropped.
+func TestTridentineRomanusAndEusebiusPresent(t *testing.T) {
+ l := Tridentine()
+ if rc, ok := l.Cels["romanus"]; !ok {
+ t.Error("romanus missing: the calendarium's photographic scans both carry \"Commemoratio S. Romani Mart.\" on 9 August")
+ } else if rc.Fields["date"] != "08-09" {
+ t.Errorf("romanus: date = %q, want 08-09", rc.Fields["date"])
+ }
+ want := map[string]struct{ date, rank string }{
+ // St Eusebius Confessor (14 Aug) and St Romanus (9 Aug, checked
+ // above) are both bare "Commemoratio" in the calendarium (no class
+ // of their own) -- pinned here, not just presence/date, per
+ // TestTridentineCommemorationRanksPinned's own reasoning below:
+ // romanus's own rank is pinned there instead, since that test
+ // groups every commemoration-rank entry this task's fixes touched
+ // in one place. eusebius-confessor is listed here because it is
+ // the OTHER half of this specific test's own slug-disambiguation
+ // story.
+ "eusebius-confessor": {"08-14", "commemoration"}, // "S. Eusebii Conf." -- a Confessor
+ "eusebius": {"12-16", "class-3"}, // "S. Eusebii Ep. et Mart." -- a Bishop and Martyr, a different person, unaffected by this round
+ }
+ for slug, w := range want {
+ rc, ok := l.Cels[slug]
+ if !ok {
+ t.Errorf("missing %q (%s)", slug, w.date)
+ continue
+ }
+ if rc.Fields["date"] != w.date {
+ t.Errorf("%s: date = %q, want %q", slug, rc.Fields["date"], w.date)
+ }
+ if rc.Fields["rank"] != w.rank {
+ t.Errorf("%s: rank = %q, want %q", slug, rc.Fields["rank"], w.rank)
+ }
+ }
+}
+
+// TestTridentineCommemorationRanksPinned: nothing in this test suite
+// asserted `rank` for thomas-becket, silvester, or romanus before this --
+// `TestTridentineRomanusAndEusebiusPresent` above pinned presence and date
+// for romanus but not rank, and Thomas Becket/Silvester (the I3 fix, RG
+// 68(d)/(e)) had no rank assertion anywhere at all. So a future
+// regeneration could silently rewrite any of the three back to class-4 --
+// the EXACT I3 failure mode -- with the suite green throughout. Pinned
+// together because they are the same failure shape (a bare "Commemoratio"
+// in the calendarium, no class of its own, silently promoted by
+// refYearExplainsAbsence's own coupling to temporal_ef.go, see that
+// function's doc comment) even though they were found in two different
+// review rounds (romanus in C2, Thomas Becket/Silvester in I3).
+func TestTridentineCommemorationRanksPinned(t *testing.T) {
+ l := Tridentine()
+ for _, slug := range []string{"romanus", "thomas-becket", "silvester"} {
+ rc, ok := l.Cels[slug]
+ if !ok {
+ t.Errorf("missing %q", slug)
+ continue
+ }
+ if rc.Fields["rank"] != "commemoration" {
+ t.Errorf("%s: rank = %q, want commemoration", slug, rc.Fields["rank"])
+ }
+ }
+}
+
+// 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)
+ }
+ }
+}
+
+// TestTridentineNamesPreservedAcrossRegeneration is the coverage guard a
+// regeneration silently destroying a whole language's names needed and did
+// not have: an earlier version of scripts/gen-sanctoral-ef.go's main()
+// preserved existing name.la values across a regeneration (missalemeum
+// supplies English only) but had no equivalent for name.pl -- and, because
+// the bootstrapped file has in fact never carried a name.la value, that
+// mechanism looked correct while doing nothing at all. A regeneration
+// deleted all 322 Polish names outright (name.pl count 322 -> 0), silently:
+// no test here asserted anything about a name.* field, and
+// `len(l.Cels) < 250` (TestTridentineLoads) does not notice a field going
+// missing within entries that still exist. `naming.CelebrationName`'s own
+// name[lang] -> name.en fallback (internal/naming/naming.go) then quietly
+// substituted English for Polish on every EF display in that language,
+// reaching mobile.Day(date, "ef", version, "pl") -- a shipped dlectio entry
+// point -- with no error anywhere in the chain.
+//
+// CORRECTED after review: the first version of this test counted name.pl
+// only (a hardcoded single language -- the exact "whitelist of two
+// languages" mistake C1's own fix was written to stop repeating, just
+// moved into the test), and its coverage floor (>= 315) had 7 entries of
+// slack -- the review proved dropping 6 entries' Polish names still passed
+// it. Fixed on both axes: the language set is DISCOVERED from the data
+// (every "name.<lang>" key actually present, not a hardcoded list, so a
+// regeneration dropping name.la or introducing a future name.de is
+// checked the same way as name.pl), and the floor for each discovered
+// language is its EXACT true count, not a loose approximation -- verified
+// directly against the branch point (`git show 2386a45:...`) once, by
+// hand, and hardcoded as the answer, not derived at test time from data
+// that could itself be wrong.
+//
+// True counts, independently verified: name.en on every entry (327, all
+// regenerated fresh from missalemeum, including the 5 entries this task's
+// own fixes added); name.pl on exactly 322 (preserved from the branch
+// point; the 5 new entries -- St Agnes secundo, St Boniface Martyr, St
+// Evaristus, St Theodore, St Eusebius Confessor -- never had a curated
+// Polish name to preserve in the first place, so 322, not 327, is the
+// correct target, not a shortfall); no OTHER name.<lang> exists in the
+// branch point at all (name.la is read by the generator but the
+// bootstrapped file has in fact never carried one), so the discovered
+// language set itself must be exactly {en, pl} -- a regeneration that
+// silently introduced or lost an entire language key, not just some
+// values within one, is caught by this assertion, not only by the count.
+func TestTridentineNamesPreservedAcrossRegeneration(t *testing.T) {
+ l := Tridentine()
+ counts := map[string]int{}
+ for _, rc := range l.Cels {
+ for k, v := range rc.Fields {
+ if v == "" || !strings.HasPrefix(k, "name.") {
+ continue
+ }
+ lang := strings.TrimPrefix(k, "name.")
+ counts[lang]++
+ }
+ }
+ wantLangs := map[string]bool{"en": true, "pl": true}
+ for lang := range counts {
+ if !wantLangs[lang] {
+ t.Errorf("unexpected name.%s present (%d entries) -- discovered language set must be exactly {en, pl}", lang, counts[lang])
+ }
+ }
+ for lang := range wantLangs {
+ if counts[lang] == 0 {
+ t.Errorf("name.%s entirely absent -- discovered language set must be exactly {en, pl}", lang)
+ }
+ }
+ if got := counts["en"]; got != len(l.Cels) {
+ t.Errorf("name.en coverage = %d entries, want %d (every entry, all freshly regenerated)", got, len(l.Cels))
+ }
+ if got := counts["pl"]; got != 322 {
+ t.Errorf("name.pl coverage = %d entries, want exactly 322 (the branch point's own true count; the 5 entries this task added have no curated Polish name to preserve, so more or fewer than 322 is a bug either way)", got)
+ }
+
+ rc, ok := l.Cels["assumption-of-the-blessed-virgin-mary"]
+ if !ok {
+ t.Fatal("missing assumption-of-the-blessed-virgin-mary")
+ }
+ if got := rc.Fields["name.pl"]; got != "Wniebowzięcie N. M. P." {
+ t.Errorf("assumption-of-the-blessed-virgin-mary: name.pl = %q, want the preserved Polish name", got)
+ }
+}