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) } } }